Techno Barje
2023-10-23T19:00:00.000Z
http://techno-barje.fr/
Alexandre Poirot
Knowledge versus market -- Sharing versus Ease of use.
2023-10-23T19:00:00.000Z
http://techno-barje.fr/post/2023/10/23/knowledge-versus-market
<p>This blog post is a continuation of the previous one about the <a href="/post/2023/10/20/history-of-edition-and-publishing-in-web-browsers/">history of editing and publishing in web browsers</a>.</p>
<p>I'm now going to focus on the significant shift of vision about the Web between the two first browsers: WorldWideWeb versus Mosaic.</p>
<p>While the very first, WorldWideWeb, defined the web as being editable by default,
Mosaic restricted the browser to become read-only.
All features related to web page editing disappeared in Mosaic.</p>
<p>Later down the road, Netscape 4 re-introduced a web page editor.
But as Netscape copied the interpretation of the Web from Mosaic,
the web pages were no longer editable by default.
This somewhat divided users in two distinct groups: readers versus authors.
The editor in Netscape 4 was an external feature of the browser, opening a distinct window.</p>
<p>An interesting fact is that both WorldWideWeb and Netscape 4 were superseded by Mosaic and Internet Explorer,
which were focusing strictly on read-only vision for the Web.</p>
<h1 id="worldwideweb-the-very-first-browser">WorldWideWeb, the very first browser.</h1>
<p>WorldWideWeb browser and the premises of the web was created in the lab called CERN, the European Organization for Nuclear Research.</p>
<p>Their browser and the web spread within various research labs and universities.
The main audience were scientists and librarians.</p>
<p><a href="https://home.cern/fr/science/computing/birth-web/short-history-web">This page</a> does a very nice summary of these first usages of the web:</p>
<blockquote>
<p>The Web was originally conceived and developed to meet the demand for automated information-sharing between scientists in universities and institutes around the world.</p>
</blockquote>
<p><a href="https://www.ahp-numerique.fr/2021/12/02/edition-scientifique-libre-acces/">This other page</a>, in french, describes at length how scientists shared information from the 60s.
The part about the web ("1.6 Le web (1984-1996)") is also an interesting read.</p>
<p>From these extracts, it isn't clear if the users were really editing web pages from the browser.
It looks like it was mostly meant to query large databases of documents (scientific articles) and information (phonebooks).
It sounds like it was already going into the direction of a read-only web.</p>
<p>Otherwise, we can easily explain why the web was originally restricted to scientists and librarians.
This browser only worked on <a href="https://en.wikipedia.org/wiki/NeXT">NeXT computers</a>.
This was a serious limitation to a widespread audience as these computers were targeting higher education and business markets.</p>
<h1 id="violawww-the-second-browser">ViolaWWW, the second browser</h1>
<p>This browser got very little coverage in the history of browsers, but may have had a significant impact on the future of the web.</p>
<p>Many browsers appeared after WorldWideWeb. <a href="http://9p.sdf.org/who/tweedy/ancient_browsers/">This web page</a> is archiving the list of all of those.</p>
<p>But <a href="https://en.wikipedia.org/wiki/ViolaWWW">ViolaWWW</a> was particularly important for three reasons:</p>
<ul>
<li>The CERN suggested to use this browser instead of WorldWideWeb and quickly became the default browser in the lab <a href="https://www.w3.org/People/Berners-Lee/FAQ.html#browser">source</a>.</li>
<li>This browser, while becoming popular for its additional features (scripting and stylesheets), also regressed all the editor aspects. It looks like it only rendered the web pages and disallow any editing.</li>
<li>The main author of Mosaic (Marc Andreessen) was shown ViolaWWW just before initiating the Mosaic project <a href="https://www.w3.org/People/Berners-Lee/FAQ.html#Mosaic">1st source</a> <a href="https://www.w3.org/DesignIssues/TimBook-old/History.html">2nd source</a></li>
</ul>
<p>ViolaWWW may have been the very precise project influencing the future of browser as read-only tools to browse the web.
That mostly by inspiring the creation of Mosaic, which also got rid of editing features to focus on reading and browsing the web.</p>
<h1 id="mosaic-the-first-widespread-browser">Mosaic, the first widespread browser.</h1>
<p><a href="https://en.wikipedia.org/wiki/Mosaic_(web_browser">Mosaic</a> was developed at the National Center for Supercomputing Applications (NCSA).</p>
<p>It was the first to reach a very wide audience up to the mass-market.
The main difference with past browsers was its compatibility with many hardwares and Operating Systems.
It was the very first to support Unix, MacOS and Windows.
The team behind it also focused a lot on making it easy to install and use.</p>
<p>This browser sealed this vision of the web by becoming much more popular.
Mosaic, like ViolaWWW really focused on browsing the web. It contained no feature around editing the web pages.</p>
<h1 id="knowledge-sharing-versus-market-and-ease-of-use">Knowledge sharing versus Market and ease of use</h1>
<p>Now it may be interesting to compare the vision of the web promoted at CERN/WorldWideWeb versus NCSA/Mosaic.</p>
<p>The CERN described the web in a simple and generic way:</p>
<blockquote>
<p>The WorldWideWeb (W3) is a wide-area hypermedia information retrieval initiative aiming to give universal access to a large universe of documents.
<a href="https://info.cern.ch/hypertext/WWW/TheProject.html">source</a></p>
</blockquote>
<p>While the original web at CERN was meant to ease sharing the knowledge between scientists.
It was probably not intentionally targeting any larger audience.</p>
<p>The web of Mosaic was clearly shifting to a wide audience of ordinary people.
But the way they were promoting the Internet was quite different:</p>
<blockquote>
<p>Mosaic offers a window into the Internet, presenting content and services to users in a friendly, interactive, point-and-click way.
<a href="http://mosaic.mcom.com/lowres_www/lowres/backgrounder/mosaic.html">source</a></p>
</blockquote>
<blockquote>
<p>Mosaic Communications Corporation intends to support companies and consumers...to accelerate the coming of this new era with tools that ease and advance online communications.
<a href="http://mosaic.mcom.com/lowres_www/lowres/backgrounder/future.html">source</a></p>
</blockquote>
<p>Mosaic was promoting a whole market/ecosystem for the Internet.
It would be made of companies providing services to consumers.
It was drastically different from CERN phrasing: "giving universal access to large universe of documents".</p>
<p>I imagine we could debate at length about these two ways of framing the web, but I would like to instead focus on the most important appeal of Mosaic, which explained its success: The ease of use.</p>
<p>Mosaic surely gained lots of traction thanks to its support of most hardwares and operating system,
but it also polished its ease of use. Unfortunately it only focused on browsing and reading the web.
But I'm wondering, what if Mosaic also spent some time in these early days on helping the first users of the web to create and edit their own websites??</p>
<p>Instead, it promoted companies to build the services. Building the services here meant to build the web pages.
This ultimately delegated content creation to experts in the early days.</p>
<p>What if Mosaic focused on the ease of use of web page editing?
What if Mosaic continued along the lines of Tim Berners-Lee original vision of the web described <a href="https://www.w3.org/DesignIssues/Editor.html">over here</a>.</p>
<blockquote>
<p>If you think surfing hypertext is cool, that's because you haven't tried writing it.</p>
</blockquote>
<blockquote>
<p>The Web is universal and so should be able to encompass everything across the range from the very rough scribbled idea on the back of a virtual envelope to a beautifully polished work of art. </p>
</blockquote>
<blockquote>
<p>A first assumption, by the way, is that you have modeless interface in which browsing and editing are not separate functions. If to edit a page, you have to switch from browsing mode to editing mode, then you have lost already.</p>
</blockquote>
<p>That's the vision I'd like to elaborate in 2023. Give a second change for the web to be fully editable (almost) by default.</p>
<p>Note: I published this article late after writing it. I actually wrote it before the release of <a href="https://a16z.com/the-techno-optimist-manifesto/">Marc Andreessen's manifesto</a>, which introduced lots of debate about his vision of tech, like <a href="https://davekarpf.substack.com/p/why-cant-our-tech-billionaires-learn">here</a>. This is typically the kind of discussion, which I find enlightning, but I really wanted to focus on actual actionnable Web features.</p>
The History of editing and publishing in web browsers
2023-10-20T09:00:00.000Z
http://techno-barje.fr/post/2023/10/20/history-of-edition-and-publishing-in-web-browsers
<p>Some web browsers used to offer built-in features to <strong>edit</strong> and <strong>publish</strong> web pages.</p>
<p>You could <strong>edit</strong> any web page. Modify the text, the formatting and styling, attach images, link to another page...</p>
<p>After having done these changes, you could <strong>publish</strong> them to the web server so that others can see your contribution.</p>
<p>I'm going to highlight that this was only possible for a limited period of time, on browsers with a limited audience.</p>
<h1 id="worldwideweb-1990-1994">WorldWideWeb (1990-1994)</h1>
<p>The Web was originally created within a European research lab called CERN, European Organization for Nuclear Research.<br>This is where was developed the very first browser called "WorldWideWeb".<br>The original documentation pages are still available online!<br>The following quote highlights the read and write capabilities of this browser.</p>
<blockquote>
<p>The "WorldWideWeb" application for the NeXT is a prototype Hypertext browser/editor.<br><a href="https://info.cern.ch/hypertext/WWW/NeXT/WorldWideWeb.html">source</a></p>
</blockquote>
<p>The main author of this application, Tim Berners Lee also insists about the editor aspect in this retrospective:</p>
<blockquote>
<p>The first web browser - or browser-editor rather - was called WorldWideWeb [...]<br><a href="https://www.w3.org/People/Berners-Lee/WorldWideWeb.html">source</a></p>
</blockquote>
<p>And another time in this note:</p>
<blockquote>
<p>If you think surfing hypertext is cool, that's because you haven't tried writing it.<br><a href="https://www.w3.org/DesignIssues/Editor.html">source</a></p>
</blockquote>
<p>In 2019, the CERN organized a project to rebuild WorldWideWeb using today's web technologies.
While doing so, they published <a href="https://worldwideweb.cern.ch/worldwideweb/">a website</a> describing the original vision of the Web
and its related browser application in details.</p>
<p>This website also insists a lot about the editor side of the browser:</p>
<blockquote>
<p>Today it's hard to imagine that web browsers might also be used to create web pages.
It turned out that people were quite happy to write HTML by hand—something that Tim Berners-Lee and colleagues never expected.
They thought that some kind of user interface would be needed for making web pages and links. That's what the WorldWideWeb browser provided.</p>
</blockquote>
<p>You can test this browser on this project <a href="https://worldwideweb.cern.ch/browser/">web page</a>.
This works slightly better on Chrome than Firefox, but I must warn you, it is quite buggy. There is many cursor issues.</p>
<p><img src="/public/editable-web/worldwideweb/worldwideweb.png" alt="Screenshot of editing of the home page in WorldWideWeb"></p>
<p>Nonetheless, it is quite stunning to see how this browser actually works.<br>You can move the caret anywhere, in all the web pages, and modify the text anywhere.<br>Do some basic styling, copy and paste text, ...<br>Exactly like Microsoft Word / Google Docs, but against remote web pages!</p>
<p>But... it had some serious limitation.<br>While you can edit all the pages, you could only save your changes to local files.<br>You could edit, but not publish your changes.</p>
<p>This is mentioned on this documentation page about how to create a new page:</p>
<blockquote>
<p>You can edit existing documents using WWW so long as they are files. You cannot normally edit information retrieved from remote databases.<br><a href="https://info.cern.ch/hypertext/WWW/NeXT/MakingDocuments.html">source</a></p>
</blockquote>
<p>This actually relates to some implementation detail, which was clarified by Tim Berners Lee:</p>
<blockquote>
<p>It would browse http: space and news: and ftp: spaces and local file: space,
but edit only in file: space as HTTP PUT was not implemented back then.<br><a href="https://www.w3.org/People/Berners-Lee/WorldWideWeb.html">source</a>
(I will followup about this in another blog post)</p>
</blockquote>
<p>None of the future browsers ever re-implemented such behavior: editable by default.</p>
<h1 id="mosaic-1993-1997">Mosaic (1993-1997)</h1>
<p>The second most notable browser, "Mosaic", drastically changed the vision of the Web.<br>You could only open and browse HTML pages. All the editing features disappeared in this browser.<br>This introduced the URL bar which wasn't visible in WorldWideWeb.</p>
<p><img src="/public/editable-web/mosaic/view-source.png" alt="Screenshot of view source dialog in Mosaic"></p>
<p>You could only open the HTML sources internally (via view source feature), or via an external editor application.
<a href="https://github.com/alandipert/ncsa-mosaic/blob/af1c9aaaa299da3540faa16dcab82eb681cf624e/src/gui-dialogs.c#L2704">Source code</a></p>
<p>Mosaic influenced much more the long term future of web browsers.
Looking at its UI, you can see that it is very similar to today's browser UI.</p>
<p>Note that you can run Mosaic on Linux!
But you have to build it from <a href="https://github.com/alandipert/ncsa-mosaic">sources available on GitHub</a>.
It can easily fail building, but see <a href="https://github.com/alandipert/ncsa-mosaic/issues/14">this issue</a> to address the failures.</p>
<h1 id="early-netscape-versions-up-to-2-1994-1996">Early Netscape versions up to 2 (1994-1996)</h1>
<p>"Netscape" started being released one year after the first version of Mosaic.
Netscape took the lead on being the most popular browser, but still didn't reimplement page editing in any way.</p>
<p><a href="https://www.webdesignmuseum.org/old-software/web-browsers/netscape-navigator-2-0">Screenshots for Netscape Navigator 2</a></p>
<h1 id="netscape-3-1997">Netscape 3 (1997)</h1>
<p>Netscape 3, via the Gold edition, shipped the "Netscape Editor" feature.
Pressing "Ctrl-E" would allow you to edit any page in it!
You could then save your modifications in a local file, but still not publish it to the remote server.<br>You could edit, but not publish your changes.
This was really similar to the behavior of WorldWideWeb browser, except that pages aren't editable by default.
The editing had to be done within a distinct application/window.</p>
<p><img src="/public/editable-web/netscape-3/composer.png" alt="Screenshot of Editor in Netscape 3">
<a href="https://www.webdesignmuseum.org/old-software/web-browsers/netscape-navigator-3-04-gold">source</a></p>
<h1 id="netscape-4-june-1997-2000">Netscape 4 (June 1997-2000)</h1>
<p>Netscape 4 started exposing a publish feature while renaming "Netscape Editor" into "Netscape Composer".
<img src="/public/editable-web/netscape-4/composer.png" alt="Screenshot of Composer in Netscape 4">
Notice the HTTP -or- FTP upload methods. (I'll followup about the HTTP upload method in a another blog post)<br>This finally addressed the shortcomings of WorldWideWeb browser.
We could easily publish the changes we just made on the page, as soon as you had the necessary
credentials on the remote web server for uploading files.</p>
<p>Unfortunately, this is also the last popular Netscape product.
Netscape had 80% market share in 1997, but only 13% in 2000!
<a href="https://en.wikipedia.org/wiki/Usage_share_of_web_browsers#Older_reports_(pre-2000)">1st source</a>
<a href="https://en.wikipedia.org/wiki/Netscape_Navigator#/media/File:Netscape-navigator-usage-data.svg">2nd source</a></p>
<h1 id="netscape-6-2000-2002">Netscape 6 (2000-2002)</h1>
<p>Note that Netscape 5 was never released. The version was dropped in favor of Netscape 6.</p>
<p>Surprisingly Netscape 6 dropped the publish feature from Composer:
<img src="/public/editable-web/netscape-6/composer.png" alt="Screenshot of Composer in Netscape 6">
Thus, getting back to the behavior of Netscape 3.
You can edit changes locally, but you can no longer publish the changes to the web server.</p>
<p>Mention in Netscape 6 troubleshooting:</p>
<blockquote>
<p>Problem:
The Editor application does not support the Publish feature.
<a href="https://www-archive.mozilla.org/unix/solaris#BrowserGeneral">source</a></p>
</blockquote>
<h1 id="netscape-7-2002">Netscape 7 (2002)</h1>
<p>Surprisingly again, Netscape 7 revived the publishing feature in Composer:
<img src="/public/editable-web/netscape-7/composer.gif" alt="Screenshot of Composer in Netscape 7">
But at this point, netscape was under 4% in market shares.
<a href="https://en.wikipedia.org/wiki/Usage_share_of_web_browsers#Older_reports_(pre-2000)">source</a></p>
<p>The complex history between Netscape 4, 5, 6 and 7 is probably related to the move
of Netscape to an open source codebase. This was initiated by the Mozilla project, which started in 1998,
one year after the release of Netscape 4. <a href="https://web.archive.org/web/20140603235609/http://archive.wired.com/techbiz/media/news/1998/11/16466">source</a>
This may be the reason why the version 5 was cancelled and why some features were dropped in Netscape 6.</p>
<p>On the plus side, today, we are able to track the development of the publishing feature which was re-implemented from scratch in the open source codebase.
The latest version of Netscape 6 was based on Mozilla 0.9.4.1. <a href="https://en.wikipedia.org/wiki/Netscape_6#Release_history">source</a>
The feature was tracked by <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=88208">this bugzilla ticket</a>,
the first patches landed into Mozilla 0.9.7 (November 2001) and the very last patch landed into Mozilla 1.0 (March 2002).
Netscape 7 was later released in August 2002 based on Mozilla 1.0. <a href="https://en.wikipedia.org/wiki/Netscape_7">source</a></p>
<h1 id="browsers-landscape-from-2002-till-now">Browsers landscape from 2002 till now</h1>
<p>In 2002, "Internet Explorer" had already around 90% of market shares.
And Internet Explorer did not have any editing capabilities.</p>
<p><img src="/public/editable-web/internet-explorer/ie6.png" alt="Screenshot of Internet Explorer 6"></p>
<p>A few years later, in 2006, the first "Firefox" version was released and also focused only on browsing and reading the web (Like Internet Explorer).
"Netscape Composer" was never reintroduced in Firefox.
<img src="/public/editable-web/firefox-1/firefox.png" alt="Screenshot of Firefox 1"></p>
<p>In 2008, "Chrome" doubled down on stripping down browser features and UI to delegate even more capabilities to the websites.
<img src="/public/editable-web/chrome-1/chrome.png" alt="Screenshot of Chrome 1"></p>
<h1 id="bonus-seamonkey-2006-today">Bonus: Seamonkey (2006-today)</h1>
<p>A browser still exists today, in 2023, with web page editing <strong>and</strong> publishing, exactly like Netscape 7!</p>
<p>Believe it or not, but a group of contributors are maintaining the original open source codebase of Netscape over the decades!!<br>This browser is <a href="https://www.seamonkey-project.org/">SeaMonkey</a>.
Like Netscape, it includes: a Web browser, but also a mail reader (ThunderBird), a newsgroup reader, IRC chat, and last but not least, one HTML editor (Composer).
This project is still active and released a new version in September.</p>
<p>I encourage everyone to give it a try. This is really amazing to see all these old and complex softwares still working today.
It is also the easiest way to run a Web browser with full editing and publishing support on modern computers.
The icing on the cake is that, as it is based on latest version of Gecko (the Web engine of Firefox) it benefits from almost the same support of Web standards as Firefox.</p>
<p><img src="/public/editable-web/seamonkey-2.53/composer.png" alt="Screenshot of Composer on SeaMonkey"></p>
<h1 id="conclusion">Conclusion</h1>
<p>Web page editing and publication features through the browser UI was only exposed to a wide audience during three years (1997 to 2000).
It was actually even shorter than that as it was during the Netscape 4 era, where their market share fall part.</p>
<p>I'll investigate in a following blog post how different WorldWideWeb's vision of the web was compared to all subsequent browsers. And the consequences it had on how the Web is used starting from Mosaic.</p>
<p align="center">Overview of browser history</p>
<p><img src="/public/editable-web/history-of-edition-and-publishing-in-web-browsers.drawio.png" alt="Overview of all this history of browsers"></p>
<p><a href="https://app.diagrams.net/#Uhttp%3A%2F%2Ftechno-barje.fr%2Fpublic%2Feditable-web%2Fhistory-of-edition-and-publishing-in-web-browsers.drawio">source for this diagram</a></p>
Declarative Web Component to replace build-time HTML templates
2023-10-05T12:20:00.000Z
http://techno-barje.fr/post/2023/10/05/declarative-web-component
<p>Recently I moved away from Jekyll to build this blog (<a href="/post/2023/10/03/minimal-blog-post-setup/">see more</a>).<br>While doing so I also moved away from the traditional HTML templates.<br>Instead I started using a "single file declarative web component".<br>The nice outcome is that the HTML page now mostly contains the text content of the blog post!<br>Do not hesitate to view the source of this page :)</p>
<p>This idea of "single file Web Component" actually comes from Tomasz Jakut (CK Editor) very simple JavaScript loader <a href="https://ckeditor.com/blog/implementing-single-file-web-components/">described over there</a>.</p>
<h1 id="single-file">"Single File"</h1>
<p>In one file you can bundle the HTML, the CSS and the JavaScript for a given Web Component.<br>This is handy as you only have one file to register.<br>On this blog, all the HTML pages displaying a blog post will uses a unique Web Component to implement and display the blog design/template.<br>Instead of having a build step processing tool duplicating the template on every single HTML page,
the browser engine use this unique Web Component to display all the blog post the same way.</p>
<p>Here is an overview of this Web Component.<br>You can see the header with the blog image, the navigation links, the footer,
and finally in middle of this, a <code><slot></code> to define where the blog post content should be put.</p>
<pre><code><template>
<header>
...
</header>
<nav role=navigation>
<ul>
<li><a href="/">Index</a></li>
<li><a href="/archives/">Archives</a></li>
<li><a href="/resume/">About me/Resume</a></li>
</ul>
</nav>
<div id="content"><slot>ARTICLE</slot></div>
<footer><p>Copyright &copy; 2023 - Alexandre Poirot</p></footer>
</template>
<style>
header { background-image: url("/images/header.png"); }
nav { background: black; color: white; }
</style>
</code></pre>
<h1 id="declarative">"Declarative"</h1>
<p>This refers to <a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Declarative-Shadow-DOM.md#self-sufficient-html">Declarative-Shadow-DOM</a>
and <a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Declarative-Custom-Elements-Strawman.md">Declarative-Custom-Elements-Strawman</a> proposal... in some way.<br>The idea is being able to load it from the HTML page, without JavaScript.</p>
<p>On this web site, the Web Component used on all blog post pages is registered like this:</p>
<pre><code><link rel="component" href="/blog-article.wc">
</code></pre>
<p>And will implement the <code><blog-article></code> DOM Element used in the HTML page.
Unfortunately, as this isn't part of any implemented standard, I'm using Tomasz's naive JavaScript loader to make this work.</p>
<h1 id="example-of-a-blog-post-html-page">Example of a blog post HTML page</h1>
<p>The traditional header of any HTML page in 2023:</p>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</code></pre>
<p>The blog post title followed by the blog title.</p>
<pre><code class="language-html"> <title>Using the fediverse/Mastodon for comments on blogs - Techno Barje</title>
</code></pre>
<p>Then, Tomasz JS loader, which will implement the support for <code><link rel=component></code>.</p>
<pre><code class="language-html"> <script src="/loader.js"></script>
</code></pre>
<p>The declaration of the <code><blog-article></code> Web Component</p>
<pre><code class="language-html"> <link rel="component" href="/blog-article.wc">
</code></pre>
<p>The unique CSS for the whole blog, and the end of <code></head></code> section.</p>
<pre><code class="language-html"> <link href="/document.css" rel="stylesheet" type="text/css">
</head>
</code></pre>
<p>Now this is where it becomes interesting.<br>The <code><blog-article></code> component will implement the overall blog design/template.<br>So that the HTML page can focus only on the specific content of that specific HTML file:</p>
<ul>
<li>The blog post title and link to it,</li>
<li>Its publish date,</li>
<li>The actual content of the blog post.</li>
</ul>
<pre><code class="language-html"><body>
<blog-article>
<div class="entry-title">
<h1><a href="/post/2023/10/05/fediverse-for-comments-on-blogs/">Using the fediverse/Mastodon for comments on blogs</a></h1>
<time datetime="2023-10-05T10:00:00.000Z" pubdate>Oct 05, 2023</time>
</div>
<article>
... The content of a blog post ...
</article>
</blog-article>
</body>
</html>
</code></pre>
<p>And that's it. We close the <code></html></code> right after.</p>
<h1 id="outcomes">Outcomes</h1>
<p>My hope is that by simplifying the HTML files to the barebone actual text content, we can revive the straight edition of HTML files!</p>
<p>In 2023, everyone is still using either:</p>
<ul>
<li>Wordpress/Medium/write.as/Fediverse to publish content when you don't want to care about the hosting side of things,</li>
<li>Jekyll/Hugo/writefreely.org or more and more custom build scripts for the tech-savvy who are at ease running command lines and managing the (self) hosting.</li>
</ul>
<p>Except a few web survivalist, I've not seen anyone edit HTML page for publishing text. HTML is now some kind of assembly language only generated or at best assembled by programs.</p>
<p>I'll keep bloging about this topic as this Declarative Web Component trick is only one small thing. We can do much more to get back to the roots of the editable web.</p>
Using the fediverse/Mastodon for comments on blogs
2023-10-04T09:30:00.000Z
http://techno-barje.fr/post/2023/10/04/fediverse-for-comments-on-blogs
<p>Yesterday I moved away from Jekyll to build this blog (<a href="/post/2023/10/03/minimal-blog-post-setup/">see more</a>).<br>But while doing that, I also moved away from Disqus for handling the comments on my blog.<br>This wasn't a trivial move as it was hard to keep the old comments.
I realized late that I was bound to this provider.</p>
<p>A nice list of self-hosted solutions is available on <a href="https://lisakov.com/projects/open-source-comments/">lisakov.com</a>.
But I was scared about the maintenance and hosting cost of such option.</p>
<p>As a long time user of Matrix, I gave a try to <a href="https://cactus.chat/">Castus Comments</a>, but it was a bit too complex to manage.</p>
<p>I finally ended up discovering a very simple snippet on <a href="https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/">carlschwan.eu</a>.
This uses the Fediverse to expose comments on a static blog.
So all the credits go to <a href="https://carlschwan.eu/">carlschwan.eu</a>, <a href="https://mastodon.online/@veronica/110028499674748958">@veronica@mastodon.online</a> and <a href="https://mastodon.blaede.family/@cassidy">@cassidy@blaede.family</a>.</p>
<p>This is just perfect:</p>
<ul>
<li>super simple, a couple tens of lines of JavaScript.<br>This is also very lightweight for people visiting the blog. Much much lighter than Disqus!</li>
<li>I used to announce new blog post on Twitter/Mastodon.<br>The comments sent on this announcement message are now merged with comments visible on the blog!</li>
<li>No hosting to maintain as it relies on the fediverse server.</li>
<li>No longer bound to a unique service provider. I can move to another fediverse server.</li>
<li>The moderation is part of the fediverse, so I should be able to manage the comments.</li>
</ul>
<p>I can only think about three downsides so far:</p>
<ul>
<li>It requires people to be on the fediverse to be able to comment.<br>But any service compatible with Mastodon. You no longer have to register against Disqus.<br>Also, you have to write your comment on the Fediverse web page. It would require more work and maintaince to offer that from the page.</li>
<li>I have to manually create a new message on my mastodon server for each new blog post.<br>But that's something I was doing anyway to announce new blog posts...</li>
<li>Mastodon doesn't allow migrating existing messages to a new server.<br>So it may be hard to keep the past message while migrating to something new.<br>But as this is an open service with many open APIs, it should still be easy to export with custom scripts.\</li>
<li>EDIT: Commnents from people using a locked account won't appear.</li>
</ul>
Minimal blog post setup - Simpler Faster Stronger
2023-10-03T10:00:00.000Z
http://techno-barje.fr/post/2023/10/03/minimal-blog-post-setup
<p>I could no longer build my blog...
so I finally escaped from Jekyll and its <a href="http://octopress.org/">Octopress</a> fork!</p>
<p>Using latest ruby would throw many exceptions.
I tried using the old versions of ruby thanks to Docker, but none of them were working either.
I tried to debug a few exceptions, but I just kept having too many.</p>
<p>The only way out would have been to migrate to lastest jekyll, but this require to redo my design again using jekyll templates instead of octopress.</p>
<p>Instead, I decided to stop using any framework to avoid any similar issue in the future...</p>
<h1 id="simpler">Simpler</h1>
<p>I still had to redo my design, but in pure HTML+CSS, which was simplier as no matter what the framework you are using, you have to know HTML and CSS...
But you no longer have to understand, nor maintain the additional bits of the framework.</p>
<p>There is significantly less files to be aware of. This ease the comprehension of the overall setup.</p>
<p>For now, I'm using the simplest possible template files with naive regexp+replace done by a JS file.</p>
<h1 id="faster">Faster</h1>
<p>This migration has a very nice impact on performance. The blog looks almost the same, but is much more conservative in resources!</p>
<p>Let's compare my <a href="http://blog.techno-barje.fr/post/2007/12/21/lets-go/">very first blog post</a>.
Its markdown sources is only 1.1K large.</p>
<p>With the previous jekyll setup that simple blog post would spawn 40 requests for a total of 1803kB, but only 1600kB transferred thanks to http compression.</p>
<p>Now, with this new naive setup the same post only spawns 6 requests for a total 99kB with the same transferred size.
Most of it comes from the header image which is 85kB...</p>
<p>The 6 requests are:</p>
<ul>
<li>the html page (2kB compared to 1.1kB markdown sources)</li>
<li>the header image</li>
<li>the favicon</li>
<li>one unique CSS file</li>
<li>one unique JS file, implementing Declarative and single file Web Component and the mastodon comment at the footer</li>
<li>one unique Declarative Web Component, for blog posts</li>
</ul>
<h1 id="stronger">Stronger</h1>
<p>For the templates, and the parsing of markdown blog posts, I still depend on NodeJS with a custom build script.
But the APIs involved here are so trivial that it should unlikely break in the future.
No more dependency with any particular templating system which may change over time,
no more dependency on any particular library which may itself depend on a language/interpreter/compiler
which may ultimately force you to update your templates by dependency chains.</p>
<h1 id="there-is-more">There is more</h1>
<p>I also moved from Disqus to the fediverse for comments. I'll say a few words about this in another blog post.</p>
<p>I mentioned "Declarative single file Web Component", same, I'll detail this in a next blog post.</p>
Trabant Calculator - A data visualization of TreeHerder Jobs durations
2019-09-17T17:00:00.000Z
http://techno-barje.fr/post/2019/09/17/trabant-calculator
<p><a href="https://ochameau.github.io/trabant-calc/">Link to this tool</a> (its <a href="https://github.com/ochameau/trabant-calc/">sources</a>)</p>
<h2 id="what-is-this-tool-about">What is this tool about?</h2>
<p>Its goal is to give a better sense on how much computations are going on in Mozilla automation.
Current <a href="https://treeherder.mozilla.org/#/jobs?repo=mozilla-central">TreeHerder UI</a> surfaces job durations, but only per job. To get a sense on how much we stress
our automation, we have to click on each individual job and do the sum manually.
This tool is doing this sum for you.
Well, it also tries to rank the jobs by their durations. I would like to open minds about the possible impact on the environment we may have here.
For that, I am translating these durations into something fun that doesn't necessarily make any sense.</p>
<h2 id="what-is-that-cars-gif">What is that car's GIF?</h2>
<p>The car is a <a href="https://en.wikipedia.org/wiki/Trabant">Trabant</a>. This car is often seen as symbolic of the former East Germany and the collapse of the Eastern Bloc in general. This part of the tool is just a joke. You may only consider looking at durations, which are meant to be trustable data. Translating a worker duration into CO2 emission is almost impossible to get right. And that's what I do here: Translate worker duration into a potential energy consumption, which I translate into a potential CO2 emission, before finally translating that CO2 emission into the equivalent emission of a trabant over a given distance in kilometers.</p>
<h2 id="power-consumption-of-an-aws-worker-per-hour">Power consumption of an AWS worker per hour</h2>
<p>Here is a really weak computation of Amazon AWS CO2 emissions for a t4.large worker.
The power usage of the machines these workers are running on could be 0.6 kW.
Such worker uses 25% of these machines.
Then let's say that Amazon <a href="https://en.wikipedia.org/wiki/Power_usage_effectiveness">Power Usage Effectiveness</a> is 1.1.
It means that one hour of a worker consumes <strong>0.165 kWh</strong> (0.6 * 0.25 * 1.1).</p>
<h2 id="co2-emission-of-electricity-per-kwh">CO2 emission of electricity per kWh</h2>
<p>Based on US Environmental Protection Agency (<a href="https://www.epa.gov/sites/production/files/2018-02/documents/egrid2016_summarytables.pdf">source</a>), the average CO2 emission per MWh is 998.4 lb/MWh.
So 998.4 * 453.59237(g/lb) = 452866 g/MWh, and, 452866 / 1000 = <strong>452 g of CO2/kWh</strong>.
Unfortunately, the data is already old. It comes from a 2018 report, which seems to be about 2017 data.</p>
<h2 id="co2-emission-of-a-trabant-per-km">CO2 emission of a Trabant per km</h2>
<p>A Trabant emits <strong>170 g of CO2 / km</strong> (<a href="http://www.trabantforum.de/ubb/Forum1/HTML/007230.html">source</a>). (Another [source] reports 140g, but let's say it emits a lot.)</p>
<h2 id="final-computation">Final computation</h2>
<pre><code>Trabant's kilometers = "Hours of computation" * "Power consumption of a worker per hour"
* "CO2 emission of electribity per kWh"
/ "CO2 emission of a trabant per km"
Trabant's kilometers = "Hours of computation" * 0.165 * 452 / 170
=> Trabant's kilometers = "Hours of computation" * 0.4387058823529412 **
</code></pre>
<h2 id="all-of-this-must-be-wrong">All of this must be wrong</h2>
<p>Except the durations! Everything else is highly subject to debate. <br/>
Sources are <a href="https://github.com/ochameau/trabant-calc/">here</a>, and contributions or feedback are welcomed.</p>
Interfaces experiments for and from Firefox
2016-06-28T11:30:00.000Z
http://techno-barje.fr/post/2016/06/28/html-experiments
<p>What about easily experimenting new interfaces for Firefox?
Written with regular web technologies, served from http, refreshable via a key shortcut.</p>
<h2 id="how-so">How so?</h2>
<p>Follow these 3 steps:</p>
<ul>
<li>Ensure running <a href="https://nightly.mozilla.org/">Firefox Nightly</a>,</li>
<li>Install <a href="http://techno-barje.fr/public/browser_ui-0.1.2-fx.xpi">this addon</a>,</li>
<li>Visit this link: <a href="browserui://rawgit.com/ochameau/planula-browser-advanced/addon-demo/">browserui://rawgit.com/ochameau/planula-browser-advanced/addon-demo/</a>.</li>
</ul>
<p>You should see a page asking you to confirm testing this browser experiment.
Once you click on the install button, the current Firefox interface will be replaced on the fly.</p>
<iframe width="420" height="315" src="https://www.youtube.com/embed/JZemGiSl5LA" frameborder="0" allowfullscreen></iframe>
<p>This interface is a old version of <a href="https://github.com/browserhtml/browserhtml/">Browser.html</a>. But instead of requiring a custom runtime, this is just a regular web site, written with web technologies and fetched from github at every startup.
If you want to check if this is a regular web page, just look at the sources:
view-source:<a href="http://rawgit.com/ochameau/planula-browser-advanced/addon-demo/">http://rawgit.com/ochameau/planula-browser-advanced/addon-demo/</a></p>
<p>If needed you can revert at any time back to default Firefox UI using the "Ctrl + Alt + R" shortcut.</p>
<p>Want to see more interfaces, here are some links:</p>
<ul>
<li><a href="browserui://rawgit.com/ochameau/planula-minimal-browser/master/index.html">browserui://rawgit.com/ochameau/planula-minimal-browser/master/index.html</a> Simplest interface ever. Just one HTML file.</li>
<li><a href="browserui://rawgit.com/ochameau/planula-browser-advanced/addon-demo/index.html?tabsui=sidetabs">browserui://rawgit.com/ochameau/planula-browser-advanced/addon-demo/index.html?tabsui=sidetabs</a> Tabs on the side.</li>
<li>Yours?</li>
</ul>
<h2 id="how-does-it-work-">How does it work ?</h2>
<p>The addon itself is simple. It does 4 things:</p>
<ul>
<li>Install a custom protocol handler for browserui:// in order to redirect to the install page,</li>
<li>The install page then communicate with a privileged script to set the "browser.chromeURL" preference which indicates the url of the top level document,</li>
<li>While we set this preference, we also grant additional permissions to the target url to use the "mozbrowser" property on iframes,</li>
<li>Finnaly, it reload the top level document with the target url.</li>
</ul>
<p>The <iframe mozbrowser> tag, while beeing non-standard, allows an iframe to act similarly to a <xul:browser> or a <webview> tag. It allows to safely open websites within the interface. Webpages loaded inside it also run into a seperated content process (e10s) contrary to regular <iframe> tag.</p>
<h2 id="why">Why?</h2>
<p>Last year, during Whistler All Hands, there was this "Kill XUL" meeting.
<a href="https://public.etherpad-mozilla.org/p/kill-xul-planning">Various options</a> were discussed. But it is unclear that any has been really looked into.
Except may be the electron option, via Tofino project.</p>
<p>Then a thread was posted on <a href="https://mail.mozilla.org/pipermail/firefox-dev/2015-July/003063.html">firefox-dev</a>. At least Go faster and new Test Pilot addons
started using HTML for new self-contained features of Firefox, which is already a great step forward!</p>
<p>But there was no experiment to measure how we could leverage HTML to build browsers within Mozilla.</p>
<p>Myself and <a href="https://github.com/vingtetun/">Vivien</a> started looking into this and ended up releasing this addon.
But we also have some more concrete plan on how to slowly migrate Firefox full of XUL and cryptic XPCOM/jsm/chrome technologies
to a mix of Web standards + Web extensions. We do have a way to make Web extensions to work within these new HTML interfaces.
Actually, it already supports basic features. When you open the browserui:// links, it actually opens an HTML page from a Web extension.</p>
<h2 id="how-to-hack-your-own-thing">How to hack your own thing?</h2>
<p>First, you need to host some html page somewhere.
Any website could be loaded. <a href="browserui://localhost/">browserui://localhost/</a> if you are hosting files locally.
But you may also just load google if you want to <a href="browserui://google.com/">browserui://google.com/</a>.
Just remember the "Ctrl + Alt + R" shortcut to get back to the default Firefox UI!</p>
<p>The easiest is probably to fork <a href="https://github.com/ochameau/planula-minimal-browser/">this one-file minimal browser</a>, or directly the <a href="https://github.com/ochameau/planula-browser-advanced/tree/addon-demo/">demo browser</a>.
Host it somewhere and open the right browserui:// url.
browserui:// just maps one to one to the same URL starting with "http" instead of "browserui".
Given that this addon is just a showcase, we don't support https yet.</p>
<p>Then, change the files, hit "Ctrl + R" and your browser UI is going to be reloaded, fetching resources again from http.</p>
<p>Once you have something you want to share, using github is handy.
If you push files to let say the "mozilla" account and "myui" as the repository name,
then you can share simply via the following link:</p>
<p> browserui://rawgit.com/mozilla/myui/master/</p>
<p>But there is many ways to control which particular version you want to share.
Sharing another branch, like the "demo" branch:</p>
<p> browserui://rawgit.com/mozilla/myui/demo/</p>
<p>Or a particular changeset:</p>
<p> browserui://rawgit.com/mozilla/myui/5a931e3e0046ccde6d4ad3a73e93016bcc3a9650/</p>
<h2 id="contribute">Contribute</h2>
<p>This addon lives on github, over here: <a href="https://github.com/ochameau/browserui">https://github.com/ochameau/browserui</a>.
Feel free to submit Issues or Pull requests!</p>
<h2 id="whats-next">What's next?</h2>
<ul>
<li>Demonstrate WebExtension based browser and the ability to implement Web Extension APIs from an HTML document.</li>
<li>Tweak the platform to handle OS integration better from low privileged HTML document.
Things like popup/panels, transparent windows, native OS controls, menus, ...</li>
<li>Also tune the platform to be able to load existing browser features from HTML like about: pages, view-source:, devtools, ...</li>
</ul>
<p>Actually, we already have various patches to do that and would like to upstream them to Firefox!</p>
Shipping Firefox features as Web Extensions
2016-03-16T10:40:00.000Z
http://techno-barje.fr/post/2016/03/16/shipping-firefox-features-as-addon
<p>What about using Web Extension APIs to implement core firefox features?
Here is the opportunity I would like to discuss today.</p>
<p>Not only new features (Hello, Pocket) but also existing built-in features (e.g Session Restore). I <a href="http://blog.techno-barje.fr/post/2016/03/14/session-restore-web-extension/">recently blogged</a> about building it as a web extension.</p>
<p>Session restore is a critical feature of Firefox.
It uses many mozilla-only technologies: XUL, XPCOM, message managers, jsm and so on.
It also involves mostly privileged code whereas it isn't really needed, possibly leading to security issues.
Even if it is living in it's own folder <em>/browser/components/sessionstore/</em>, there are many hardcoded parts of it elsewhere.
It is clearly not self contained.</p>
<p>Instead of just hardcoding this feature into Firefox, we could possibly ship it as an addon.
That would have various benefits:</p>
<ul>
<li>Let a chance to release this part of firefox faster than the platform,</li>
<li>Help us experiment by doing some A/B testing with two very different implementations,</li>
<li>Dogfooding Web Extension APIs would make them more stable and ensure they are both useful and powerful,</li>
<li>It should open ways to reuse these addons once Servo is ready and implements Web Extension APIs,</li>
<li>Last but not least, it dramatically reduces the contribution efforts required to modify a core Firefox feature:<ul>
<li>Forget about building C++ and having a build environment,</li>
<li>You can possibly checkout a small repo instead of all mozilla-central,</li>
<li>Do not necessarily have to use various mozilla specific tools like mach,</li>
<li>No need to even build Firefox itself, instead you could fetch a nightly build and install the addon on it,</li>
<li>And forget about all cryptic technologies that we keep using as ancient relics like xul, xpcom and so on!</li>
</ul>
</li>
</ul>
<p>About contribution. I asked about how many people contribute(d) to session restore.
There is mostly one active employee working on it: mconley.
Then sparse contributions are being made by other employees like ttaubert, yoric, dragana, mystor, mayhemer,...
But there seem to be only one non-employee contribution made by Allasso Travesser with just one patch.</p>
<p>I'm convinved we can engage more with simplier workflows (Addon versus built-in) and technologies with a lower learning curve (Web Extension vs XUL).</p>
Session restore as a Web Extension
2016-03-14T07:00:00.000Z
http://techno-barje.fr/post/2016/03/14/session-restore-web-extension
<p>Session Restore is a built-in Firefox feature which preserve user data after a crash or an unexpected close.
I spent a little time exploring if it is possible to build such a feature as an replaceable web extension.</p>
<p>Here is a sketch of session store implemented as a web extension:
<iframe width="420" height="315" src="https://www.youtube.com/embed/58vPBJWmAig" frameborder="0" allowfullscreen></iframe></p>
<p>This addon currently save'n restore:</p>
<ul>
<li>tabs (the url for each tab and the tab order)</li>
<li>form values</li>
<li>scroll positions</li>
</ul>
<p>Missing features (compared to the built-in session restore):</p>
<ul>
<li>Does not restore session storage</li>
<li>Always restore the previous session</li>
<li>I have no idea what it does regarding private browsing</li>
<li>No dedicated about:sessionrestore page</li>
<li>Does not save tab history. Instead it just saves the current tab document/form/scroll</li>
</ul>
<p>To get the above points working, it is a matter of time and possibly some tweaks to the current Web Extensions APIs.</p>
<br/>
Yes. It is possible to implement a core Firefox feature with the [in-progress implementation][3] of Web Extension APIs.
It also shows the limitations of the current Chrome APIs. For example in order to fully support tab history, the APIs may needs to be extended.
<p>Source code is available on <a href="https://github.com/ochameau/session-restore-webext/">github</a>.
A <a href="https://github.com/ochameau/session-restore-webext/releases/download/v0.1-beta/sessionstore.mozilla.org-v0.1-beta.xpi">pre-release version</a> is also available. Don't forget to toggle <code>xpinstall.signatures.required</code> preference to false from about:config to be able to install it.</p>
Debug pure javascript leaks
2013-03-09T08:00:00.000Z
http://techno-barje.fr/post/2013/03/09/js-memory-debugging
<p>Mozilla ecosystem already has plenty of <a href="https://wiki.mozilla.org/Performance:Leak_Tools">built-in features, scripts and addons</a> to debug memory usage. But most of them are focused on the internals of the C++ codebase. These tools are very verbose and expose very-very few Javascript metadata. So that you have to start learning tons of internal C++ classes before being able to undersstand that your Javascript objects are actually visible in these tools output!</p>
<p>For now, when chasing Addon SDK memory leaks, I was just looking at overall memory usage and tried to read and re-read our codebase until I finally find the leak by seeing it in the code... But that practice may come to an end!
We should have Javascript-oriented memory debugging tool. With a clear picture of which objects are still allocated at a given point in time. <strong>Without any C++ aspect.</strong> With an output that any confirmed Javascript developer can easily read and understand without knowing much about how Mozilla engine works.</p>
<p>With that in mind, I started looking at <strong>the object CC/GC graph</strong>. This graph contains a view of all objects allocated dynamically by the Garbage collector. All Javascript objects end up being in this graph. But also way more other C++ objects that we will have to translate into a meaningfull Javascript paradigm to the developer.</p>
<p>Then I realized that an XPCOM component already expose the whole CC graph: <a href="https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsICycleCollectorListener">nsICycleCollectorListener</a>. But again, with very few Javascript information other than "this is a Javascript object", or, "this is a javascript function". Not much more. It ends up being quite frustrating as most of the information is there, we just miss few pinches of Javascript metadata.
Like:</p>
<ul>
<li>what are the attributes of this object?</li>
<li>in which document it lives?</li>
<li>in which script it has been allocated?</li>
<li>in which line?</li>
<li>in which function?</li>
<li>what other Javascript objects refers this one?</li>
<li>what is the function name/source?</li>
<li>...</li>
</ul>
<p>Finally, because of -or- thanks to the extra motivation given by <a href="http://paul.cx/">padenot</a> and <a href="https://github.com/vingtetun">vingtetun</a>, I ended up doing crazy hacks to fetch this few information directly from Javascript: Call the <a href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/JSAPI_Reference">jsapi</a> library by using jsctypes with the object addresses given by the nsICycleCollectorListener interface. The benefit is that this experiment can run on any firefox release build (i.e. no need for a custom Firefox build). Using only JS also allows to experiment faster by avoiding the compiling phase. But that should definitely be kept as an experiment as I would not consider this as a safe practice!!</p>
<p>The result of this is:</p>
<ul>
<li>a <a href="https://github.com/ochameau/jscpptypes">jsctypes C++ mangling library</a>,</li>
<li>a <a href="/public/demo/cc-js-tool/cc-js-tool.xpi">work-in-progress addon</a> to identify DOM node leaks involving multiple compartments/documents, and,</li>
<li>two (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=833783">1</a>, <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=839280">2</a>) bug fixes.</li>
</ul>
<p>You can install this <a href="/public/demo/cc-js-tool/cc-js-tool.xpi">addon</a>, it should work on Windows and Linux with FF20+. You can easily see bug 839280's leaks on today's Aurora (FF21) by opening firefox with this addon, then open and close the devtool inspector panel (CTRL+MAJ+I) and finally run the memory script by pressing ALT+SHIFT+D shortcut.
Wait a bit, the addon is processing the whole CC graph and will freeze your firefox instance. And then open a folder with a log file that displays various information about potential cross compartment leaks.</p>
<p>Let me show you addon's output for this leak.
The code involved in this leak is the following button's click listener:
<a href="http://hg.mozilla.org/mozilla-central/annotate/5d7a14c71f51/browser/devtools/shared/DeveloperToolbar.jsm#l102">/browser/devtools/shared/DeveloperToolbar.jsm</a></p>
<pre><code>button.addEventListener("click", function() {
requisition.update(buttonSpec.typed);
//if (requisition.getStatus() == Status.VALID) {
requisition.exec();
/*
}
else {
console.error('incomplete commands not yet supported');
}
*/
}, false);
</code></pre>
<p>The script will print this in the log file:</p>
<pre><code>############################################################################
DOM Listener leak.
>>> Leaked listener ctypes.uint64_t.ptr(ctypes.UInt64("0x128a16c0")) - JS Object (Function)
Function source:
function () {
"use strict";
requisition.update(buttonSpec.typed);
//if (requisition.getStatus() == Status.VALID) {
requisition.exec();
/*
}
else {
console.error('incomplete commands not yet supported');
}
*/
}
>>> DOM Event target holding the listener ctypes.uint64_t.ptr(ctypes.UInt64("0x12a95f60"))
FragmentOrElement (XUL) toolbarbutton id='command-button-responsive' class='command-button' chrome://browser/content/devtools/framework/toolbox.xul
############################################################################
Scope variable leak.
>>> Function keeping 'button' scope variable alive ctypes.uint64_t.ptr(ctypes.UInt64("0xf9a1640")) - JS Object (Function)
Function source:
function () {
"use strict";
requisition.update(buttonSpec.typed);
//if (requisition.getStatus() == Status.VALID) {
requisition.exec();
/*
}
else {
console.error('incomplete commands not yet supported');
}
*/
}
</code></pre>
<p>It immediatly tells you that you <strong>may</strong> leak something via this anonymous function. <strong>may leak</strong>, and not <strong>do leak</strong>, as it is always hard to tell which references are expected to be removed or not, but at least, it tells you that this reference still exist and may keep your compartment/document/global alive.</p>
<p>To make it short, the script first search for FragmentOrElement objects in the CC and search for all objects from the same compartment. Then I focused my work on cross compartment leaks so that I looked for edges going from and to these objects. Finally I analysed each of these objects having references from and to other compartments and tried to translate C++ object patterns into a meaningfull sentence for the Javascript paradigm.</p>
<p>Now What?</p>
<p>I'd like to get feedback from people used to debug leaks (no matter the language) and also discuss with people used to gecko internals like nsXPCWrappedJS, JS Object (Call), ... in order to know if assumptions I made <a href="https://github.com/ochameau/cc-js-tool/blob/master/main.js#L213-L250">here</a> are correct. So that I can continue translating new potential C++ object patterns into meaningfull Javascript usecases.</p>
How to write a new WebAPI in Firefox Desktop, mobile, OS - part 1 ?
2013-02-14T08:00:00.000Z
http://techno-barje.fr/post/2013/02/14/how-to-write-a-webapi
<p>Mozilla teams recently wrote <a href="https://wiki.mozilla.org/WebAPI">tons of new API</a>
in a very short period of time, mostly for Firefox OS, but not only.
As Firefox Desktop, Firefox Mobile and Firefox OS are based on the same source
code, some of these API can easily be enabled on Desktop and mobile.</p>
<p>Writing a new API can be seen as both complicated and simple. Depending on the
one you want to write, you don't necessary need to write anything else than
Javascript code (for example the <a href="https://wiki.mozilla.org/WebAPI/SettingsAPI">settings API</a>).
That makes such task much more accessible and easier to prototype as you do
not enter in compile/run development cycles, nor have to build firefox before even trying to experiment. But there is a significant number of
mozilla specific knowledges to have before being able to write your API code.</p>
<p>The aim of this article is to write down a simple API example from ground
and try to explain all necessary things you need to know before writing an API
with the same level of expertise than what did Firefox OS engineers.</p>
<h1 id="the-example-api--commonjs-require">The example API: « CommonJS require »</h1>
<p>Let's say we would like to expose to websites a <code>require()</code> method that act like
the nodejs/commonjs method with the same name. This function allows you to load
javascript files exposing a precise interface, without polluting your current javascript scope.</p>
<p>So given the following javascript file:
{% codeblock <a href="http://blog.techno-barje.fr/public/webapi/module.js">http://blog.techno-barje.fr/public/webapi/module.js</a> lang:js %}
// All properties set to <code>exports</code> variable will be returned to the requirer
exports.hello = function () {
return "World";
};
{% endcodeblock %}</p>
<p>Any webpage will be able to use its <code>hello</code> function like this:
{% codeblock the web lang:js %}
var module = navigator.webapi.require("<a href="http://blog.techno-barje.fr/public/webapi/module.js">http://blog.techno-barje.fr/public/webapi/module.js</a>");
alert(module.hello()); // >> Display "World"
{% endcodeblock %}</p>
<h1 id="simpliest-implementation-possible">Simpliest implementation possible</h1>
<p>In this first example I stripped various advanced features in order to ease
jumping into Firefox internal code. I bundled this example as a Firefox addon
so that you can easily see it running and also hack it.
You can download it <a href="/public/webapi/api-without-idl.xpi">here</a>. Once installed, you will have to relaunch Firefox,
open any webpage, then open a Web console and finally execute the
<code>navigator.webapi.require</code> code I just gave.</p>
<p>Now let's see what's inside.
This .xpi file is just a zip file so you can
open it and see three files:</p>
<ul>
<li><strong>install.rdf</strong>:</li>
</ul>
<p> A really boring file describing our addon. The only two important
fields in this file are: <code><em:bootstrap>false</em:bootstrap></code> and
<code><em:unpack>true</em:unpack></code> required when you need to register a XPCOM file.
More info <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#bootstrap">here</a>.</p>
<ul>
<li><strong>chrome.manifest</strong>:</li>
</ul>
<pre><code># Those two lines allow to register the Javascript xpcom component defined in
# `web-api.js`
component {20bf1550-64b8-11e2-bcfd-0800200c9a77} web-api.js
contract @mozilla.org/webapi-example;1 {20bf1550-64b8-11e2-bcfd-0800200c9a77}
# That line registers the xpcom component in the "JavaScript-navigator-property"
# category which add it to the list of components that inject a new property in
# `navigator` web pages global object. The second argument defines the name of
# the property we would like to set.
category JavaScript-navigator-property webapi @mozilla.org/webapi-example;1
</code></pre>
<ul>
<li><strong>web-api.js</strong>:</li>
</ul>
<p>And last but not least. The Javascript XPCOM file. XPCOM is a component object
model overused in Mozilla codebase.
More info <a href="https://developer.mozilla.org/en/docs/XPCOM">here</a>
Let's analyse its content by pieces:</p>
<p>{% codeblock lang:js %}
function WebAPI() {}</p>
<p>WebAPI.prototype = {
// Define the XPCOM component id, that has to match the one given in
// chrome.manifest file
classID: Components.ID("{20bf1550-64b8-11e2-bcfd-0800200c9a77}"),</p>
<p> // Mandatory XPCOM method, that defines which interfaces
// an object exposes.
// * nsIDOMGlobalPropertyInitializer:
// <a href="https://developer.mozilla.org/fr/docs/XPCOM_Interface_Reference/nsIDOMGlobalPropertyInitializer">https://developer.mozilla.org/fr/docs/XPCOM_Interface_Reference/nsIDOMGlobalPropertyInitializer</a>
// This interface is related to the XPCOM category "JavaScript-navigator-property"
// declared in the chrome.manifest file and this interface defines the <code>init</code>
// method that is called when a webpage try to access navigator.webapi property.
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIDOMGlobalPropertyInitializer
]),</p>
<p> // nsIDOMGlobalPropertyInitializer:init
init: function init(win) {
// The <code>init</code> method can return an object that will be the one exposed as
// <code>navigator.webapi</code>. This object will be created for each web document.
return {
require: function (url) {
return require(win, url);
},
// Special internal attribute used to define which property the website
// will be able to access. Only attribute whose name is specified here
// are going to be accessible by the page.
// <a href="https://wiki.mozilla.org/XPConnect_Chrome_Object_Wrappers">https://wiki.mozilla.org/XPConnect_Chrome_Object_Wrappers</a>
<strong>exposedProps</strong>: {
require: 'r'
}
};
}
};</p>
<p>// Last XPCOM thingy that allows to expose our WebAPI Component to the system
// More info here: <a href="https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/XPCOMUtils.jsm">https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/XPCOMUtils.jsm</a>
const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebAPI]);
{% endcodeblock %}</p>
<p>I let you discover the implementation of the <code>require</code> method, but it will
be your job to implement such method. Now, you have the very minimal set where
you can tweak the returned value of the <code>init</code> method and expose your own
API to webpages.</p>
<p>Now note that this is a very minimal example. I'll try to continue blogging
about that and eventually talk about:</p>
<ul>
<li>interfaces definition,</li>
<li>custom event implementation,</li>
<li>other XPCOM categories (in order to inject on other object than navigator),</li>
<li>how to implement a cross process API (mandatory for Firefox OS),</li>
<li>prototyping via the Addon SDK,</li>
<li>...</li>
</ul>
Firefox OS Bootstrap: How to Build It on a VM
2012-10-27T07:00:00.000Z
http://techno-barje.fr/post/2012/10/27/firefox-os-bootstrap
<p>During my on-boarding on Firefox OS team I kept a draft of all necessary stuff that need to be done in order to build the project and flash it to the phone.
I'm pretty sure it can help people on-boarding the project by having a single page that would allow anyone to start working on Firefox OS. I highly suggest you to take a look at <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS">MDN Firefox OS documentation</a> if you visit this page later on, as this blogpost will most likely be outdated in some weeks.</p>
<h1 id="environnement">Environnement</h1>
<h2 id="use-a-virtual-machine">Use a Virtual Machine</h2>
<p>I'm suggesting everyone to use a VM. It allows you to use exactly same environment, in order to maximize your chances to succeed building Firefox OS!
Using another OS, another linux distro or even another Ubuntu version will introduce differences in dependencies versions and can easily give you errors no one but you are facing :(</p>
<p>You can use VMware Player which is free and available <a href="https://my.vmware.com/web/vmware/free#desktop_end_user_computing/vmware_player/4_0">here</a>, or any other VM software you are confortable with that has decent USB support (required to flash the phone).</p>
<h2 id="use-ubuntu-1110">Use Ubuntu 11.10</h2>
<p>For the same reason than the VM, I suggest you to use the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Boot_to_Gecko/B2G_build_prerequisites#Requirements_for_Linux">recommended linux distro and version</a>
You can download this <a href="http://releases.ubuntu.com/oneiric/ubuntu-11.10-desktop-amd64.iso">Ubuntu 11.10 x64 ISO image</a> and create a VM out of it (It is super easy with VMware, it almost does everything for you). The only important things are to set a large enough virtual drive, 30GB is a safe minimum, and enough memory, 4GB is a safe minimum.</p>
<p>Now open a terminal and launch all following commands in order to install all necessary dependencies.</p>
<h1 id="install-dependencies">Install dependencies</h1>
<h2 id="install-build-dependencies">Install build dependencies:</h2>
<pre><code>sudo apt-get install build-essential bison flex lib32ncurses5-dev lib32z1-dev lib32z1-dev ia32-libs libx11-dev libgl1-mesa-dev gawk make curl bzip2 g++-multilib libc6-dev-i386 autoconf2.13 ccache git
sudo apt-get build-dep firefox
</code></pre>
<h2 id="java-jdk-6-needed-for-adb">Java JDK 6 needed for adb</h2>
<pre><code># The following PPA allows you to easily install the JDK through apt-get
sudo add-apt-repository ppa:ferramroberto/java
sudo apt-get update
sudo apt-get install sun-java6-jdk
</code></pre>
<h2 id="android-sdk-in-order-install-adb">Android SDK in order install adb</h2>
<pre><code># Your first need to install 32 bit libs as we are using 64bit OS
# otherwise, you will have following error while running adb:
# $ adb: No such file or directory
sudo apt-get install ia32-libs
# There is no particular reason to use this SDK version
# It was the current version when I've installed it
wget http://dl.google.com/android/android-sdk_r20.0.3-linux.tgz
tar zxvf android-sdk_r20.0.3-linux.tgz
cd android-sdk-linux/
# The following command installs only "platform-tools" package which
# contains adb and fastboot
./tools/android update sdk --no-ui --filter 1,platform-tool
# Register adb in your PATH
echo "PATH=`pwd`/platform-tools:\$PATH" >> ~/.bashrc
# Execute in a new bash instance in order to gain from this new PATH
bash
</code></pre>
<h2 id="tweak-udev-in-order-to-recognize-your-phone">Tweak udev in order to recognize your phone</h2>
<p>If you do not do that at all, or not properly, <code>$ adb devices</code> will print this:</p>
<pre><code>???????????? no permissions
</code></pre>
<p>You need to put the following content into <code>/etc/udev/rules.d/51-android.rules</code></p>
<pre><code>cat <<EOF | sudo tee -a /etc/udev/rules.d/51-android.rules
SUBSYSTEM=="usb", ATTRS{idVendor}=="19d2", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", MODE="0666"
EOF
sudo restart udev
</code></pre>
<p>Here I register only internal Mozilla phones otoro and unagi IDs.
You may want to add lines for other phones. See <a href="http://developer.android.com/tools/device.html#VendorIds">this webpage</a> for other vendor IDs.</p>
<h1 id="checkout-all-necessary-projects">Checkout all necessary projects</h1>
<h2 id="checkout-b2g-repository">Checkout B2G repository</h2>
<pre><code>git clone https://github.com/mozilla-b2g/B2G.git
</code></pre>
<p>Take a minute to configure git, otherwise next steps will keep bugging you asking for your name and email.</p>
<pre><code>cat > ~/.gitconfig <<EOF
[user]
name = My name
email = me@mail.com
[color]
ui = auto
EOF
</code></pre>
<h2 id="connect-your-phone-and-ensure-it-is-visible-from-your-vm">Connect your phone and ensure it is visible from your VM.</h2>
<p>In order to do so run <code>adb devices</code>, you should see non-empty list of devices.</p>
<pre><code>$ adb devices
List of devices attached
full_unagi device
</code></pre>
<p>If you see <code>no permissions</code> message, checkout udev step.<br />
Note that you have to setup your Virtual machine software to connect the USB port to the VM. In VMware player, click on: <code>Player menu > Removable devices > "...something..." Android > Connect (Disconnnect from host)</code>.<br /></p>
<h2 id="checkout-all-dependencies-necessary-for-your-particular-phone">Checkout all dependencies necessary for your particular phone</h2>
<p>Before running the following command, ensure that your phone is connected.
Note that you have to run this command with <strong>your phone still being on Android OS and ICS version</strong>. If your phone is already on B2G, you will have to retrieve the backup-otoro or backup-unagi folder automatically created when running the following command.<br/>
If your device is on an Android version older than ICS, you will have to flash it first to ICS. For both of these issues, ask in #b2g for help.
This step will take a while, as it will download tons of big projects: android, gong, kernel, mozilla-central, gaia,... More than 4GB of git repositories, so be patient.</p>
<pre><code>cd B2G/
# Run ./config --help for the list of supported phones.
./config.sh unagi
</code></pre>
<h2 id="install-qualcomm-areno-graphic-driver">Install Qualcomm Areno graphic driver</h2>
<p>Only if you are aiming to build Firefox OS for otoro or unagi phones,
you will have to manually download Qualcomm areno armv7 graphic driver, available <a href="https://developer.qualcomm.com/file/10127">here</a>. <br/>
Unfortunately, you will have to register to this website in order to be able to download this file. Once downloaded, put this <code>Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip</code> into your <code>B2G</code> directory.</p>
<h1 id="build-firefox-os">Build Firefox OS</h1>
<p>If ./config.sh went fine, you can now build Firefox OS!</p>
<pre><code>./build.sh
</code></pre>
<p>Here is the possible error you might see:</p>
<ul>
<li><p><code>arm-linux-androideabi-g++: Internal error: Killed (program cc1plus)</code></p>
<p>You are most likely lacking of memory. 4GB is a safe minimum.</p>
</li>
<li><p><code>KeyedVector.h:193:31: error: indexOfKey was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]</code></p>
<p>Your gcc version is too recent. Try using gcc 4.6.x version.</p>
</li>
</ul>
<h1 id="flash-the-phone">Flash the phone</h1>
<p>If ./build.sh went fine, you can now flash your phone:</p>
<pre><code> ./flash.sh
</code></pre>
<p>Note that I have to unplug replug the device in order to make it work in the VM.
When running ./flash.sh, the unagi phone switch to a blue screen, then ./flash.sh script is stuck on <code>< waiting device ></code> message. If I unplug and plug in back, it immediately starts flashing. Be carefull if you have to do the same, ensure that ./flash.sh doesn't start flashing when you unplug it! <br/><br/>
If ./flash.sh failed by saying that the image is too large, It might mean that you have to root your phone first. Again, ask in #b2g for help.</p>
Addon SDK 1.11 - the page-mod release
2012-09-19T07:00:00.000Z
http://techno-barje.fr/post/2012/09/19/1.11-page-mod-release
<p><a href="https://addons.mozilla.org/en-US/developers/docs/sdk/latest/packages/addon-kit/page-mod.html">page-mod API</a> is the most commonly used API in jetpack. It allows to execute Javascript piece of code against any given website. It is very similar to greasemonkey and userscripts.</p>
<p>In Addon SDK version 1.11, which is due for October, 30th, we will bring various subtle but very important fixes, features and improvements to this API. In the meantime we will start releasing beta versions on tuesday (09/25) with 1.11b1.</p>
<p>Here is an overview of these changes:</p>
<ol>
<li><p>You will now be able to execute page-mod scripts to already opened tab, by using the new <code>attachTo</code> option.
[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=708190">bug 708190</a>]</p>
</li>
<li><p>With the same <code>attachTo</code> option, you can execute page-mod scripts only on top-level tab documents, and so avoid being applied to iframes.
The <a href="https://blog.mozilla.org/addons/2012/09/12/introducing-page-mods-attachto/">following blogpost</a> goes into detail about this new option.
[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=684047">bug 684047</a>]</p>
</li>
<li><p>page-mod now ignores non-tab documents like: panel, widget, sidebar, hidden document living in firefox's hidden window, ...
[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=777632">bug 777632</a>]</p>
</li>
<li><p>Your addon will be more efficient as we removed some costly workaround: the Javascript proxies layer between your content script and the page. We are now relying directly on C++ wrappers, also known as Xraywrappers. We are expecting a major improvement in term of memory and CPU usage. As this change depends on modifications made in Firefox, it will only be enabled on Firefox 17 and greater.
[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=786976">bug 786976</a>]</p>
</li>
<li><p>Content scripts are now correctly frozen when you go back and forth in tab history. Before that, your content script was still alive and could throw some unexpected exception or modify an unexpected document.
[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=766088">bug 766088</a>]</p>
</li>
<li><p>Random fixes: window.top and window.parent will be correct for iframes [<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=784431">bug 784431</a>].</p>
</li>
<li><p>Last but not least and still at risk for 1.11 release. You will be able to extend priviledges of your content script to extra domains. So that your script will now be able to execute some action on your own domain in addition to the current page domain, without facing cross domain limitations. This rely on some improvements being made to Firefox and will only be enabled on Firefox 17+.
[<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=786681">bug 786681</a>]</p>
</li>
</ol>
<p>It is realy exciting to see our most used API receiving so many improvements and I hope that we fixed most of the long-living issues you may have faced with page-mod!!</p>
<p>We would really like to get your feedback on these changes. If you find anything wrong, please, file bugs <a href="https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK">here</a> and do not hesitate to come discuss with our team in the <a href="https://groups.google.com/forum/?fromgroups#!forum/mozilla-labs-jetpack">mailing-list</a> </p>
Jetpack localization using YAML format
2011-11-17T08:00:00.000Z
http://techno-barje.fr/post/2011/11/17/jetpack-localization-yaml
<p>In <a href="/post/2011/10/31/jetpack-localization/">a previous post</a>, I've described my first proposal for localization support in jetpack addons. I've decided to change locale files format for <a href="http://en.wikipedia.org/wiki/YAML">YAML</a> instead of JSON. During MozCamp event, folks helped me identifying some pitfalls with JSON:</p>
<ul>
<li><strong>No multiline string support.</strong> Firefox parser allows multiline but it is not officialy supported! So that it will disallow third party tools to work properly.</li>
<li><strong>No easy way to add comments.</strong> It is mandatory for localizers to have context description in comments next to keys to translate. As there is no way to add comments in JSON, it will end up complexifying a lot locale format.</li>
</ul>
<h1 id="example">Example</h1>
<pre><code class="language-yaml">
# You can add comments with `#`...
Hello %s: Bonjour %s # almost ...
hello_key: Bonjour %s # wherever you want!
# For multiline, you need to indent your string with spaces
multiline:
"Bonjour
%s"
# Plural forms.
# we use a nested object with attributes that depends on the target language
# in english, we only have 'one' (for 1) and 'other' (for everything but 1)
# in french, it is the same except that 'one' match 0 and 1
# in some language like Polish, there is 4 forms and 6 in arabic
#
# So that having a structured format like YAML,
# help us writing these translations!
pluralString:
one: "%s telechargement"
other: "%s telechargements"
# I need to enclode these strings with `"` because of %. See note after.
</code></pre>
<pre><code class="language-javascript">
// Get a reference to `_` gettext method with:
const _ = require("l10n").get;
// These three forms end up returning the same string.
// We can still use a locale string in code, or use a key.
// And multiline string gets its `\n` removed. (there is a way to keep them)
_("Hello %s", "alex") == _("hello_key", "alex") == _("multiline", "alex")
// Example of non-naive l10n feature, plurals:
_("pluralString", 0) == "0 telechargement"
_("pluralString", 1) == "1 telechargement"
_("pluralString", 10) == "10 telechargements"
</code></pre>
<h1 id="advantages-of-yaml">Advantages of YAML</h1>
<ul>
<li><strong>Multiline strings are supported nicely / easy to read.</strong> You do not need to add a final <code>\</code> on all lines. As mulitiline is easier, localizers can use them more often and it will surely improve readability of locale files!</li>
<li><strong>Structured data format.</strong> we can use this power whenever it is needed. For example, when we need to implement complex l10n features like plural forms or any feature that goes beyond simple 1-1 localization. The cool thing if we compare to JSON is that even if we define structures, we keep a really simple format with no noise (like {, }, ", ...).</li>
</ul>
<br/>
<p>As nothing comes without any issues, here is what I've found around YAML:</p>
<ul>
<li>This format is not a Web standard. I don't think it makes much sense to avoid using it because of that. We are clearly missing a standardized format for localization in the web world.</li>
<li>You may hit some issues when you do not enclose your strings with <code>"</code> or <code>'</code>. For example, you can't start a string with <code>%</code>, nor having a <code>:</code> in middle of your string without enclosing it.</li>
<li>Even if YAML is not a web standard, it has been formaly specified. And unfortunately, a handy feature becomes a pitfall for our purpose! Some strings are automatically converted. <code>Yes</code>, <code>True</code>, <code>False</code>, ... are automatically converted to a boolean value. We can work around this in multiple ways, either by documenting it, or modifying the parser. The same solution apply here, you need to enclose your string with quotes.</li>
</ul>
<p><br/><br/></p>
<p>Again, feedback is welcomed on <a href="https://groups.google.com/group/mozilla-labs-jetpack/t/da50c6dac33b445b">this group thread</a> and you can follow this work in <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=691782">bug 691782</a>.</p>
Jetpack localization
2011-10-31T07:00:00.000Z
http://techno-barje.fr/post/2011/10/31/jetpack-localization
<p>I'm going to describe the first proposal of localization support for Jetpack.
This approach uses gettext pattern and json files for locales.
It is the first step of multiple iterations. This one only allows retrieving localized string in javascript code.
We are going to give ways to translate files, mainly HTML files, through another iteration.
And we are about to offer an online tool to ease addon localization (like babelzilla website).</p>
<p>Let's start by looking at a concrete example, then I'll justify our different choices.</p>
<pre><code class="language-javascript">{
"Hello %s": "Bonjour %s",
"hello_user": "Bonjour %s"
}
</code></pre>
<pre><code class="language-javascript">// Retrieve a dynamic reference to `_` gettext method with:
const _ = require("l10n").get;
// Then print to the console a localized string:
console.log(_("Hello %s", "alex"));
// => Prints "Bonjour alex" in french.
// Or, if we don't want to use localized string in addon code:
console.log(_("hello_user", "alex"));
</code></pre>
<h2 id="why-gettext">Why gettext?</h2>
<ol>
<li>It gives a way to automatically fetch localizable strings or ids from source code
by searching for <code>_( )</code> pattern. </li>
<li>It allows to use either strings or IDs as value to translate.
It is obviously better to use IDs. Because locales will broke
each time addon developer fix a typo in the main language hard coded in the code.</li>
</ol>
<p>But we should not forget that the high level APIs is trying to
simplify addon development. So that it has to be really easy to translate a simple
addon that has only 2 JS files and less than 50 lines of code!
And the simple fact to mandatory require a locale file for the default language
appears like a big burden for such small addon.</p>
<p>Having said that, I'm really happy that gettext approach doesn't discourage, nor
makes it harder to use IDs, and so, if an addon developer build a big addon
or just want to take more time to use better pratice, he still can do it, easily!</p>
<h2 id="why-json-for-locales">Why JSON for locales?</h2>
<p>We could have used properties files, like XUL addons. But this format has some
limitations that are not compatible with gettext pattern. Keys can't contain spaces
and are limited to ASCII or something alike, so that we can't put text in a key.</p>
<p>So instead of using yet another specific format, I'm suggesting here to use JSON.
JSON is really easy to parse and generate from both client and server side,
and I'm convinced that it is simple enough to be edited with a text editor.
On top of that we can build a small web application to ease localization.</p>
<p>In my very first proposal, I used a complex JSON object with nested attributes.
But it ends up complexifying the whole story without real advantage.
So I'm suggesting now to use the most simple JSON file we can require:
one big object with keys being strings or id to translate and values being translated strings.
Then we will be able to use JSON features to implement complex localization features,
like plurals handling. So that values may be an array of plurals forms.</p>
<h2 id="the-big-picture">The big picture</h2>
<p>Everything starts with one addon developer or one of its contributor.
If one of them want to make the addon localizable, they have to use this new localization module.</p>
<pre><code class="language-js">const _ = require("l10n").get;
</code></pre>
<p>There is already multiple choices that has been made here:</p>
<ul>
<li><code>_</code> is not a <em>magic global</em>. We need to explicitely require it.
This choice will simplify compatibility with other CommonJS environnements, like NodeJS.</li>
<li>The name of the module itself is <code>l10n</code> instead of <code>localization</code> in order to ease the use of it.</li>
<li>This module expose <code>_</code> function on <code>get</code> attribute in order to be able to
expose another methods. I'm quite confident we will need some functions for plurals or files localization.</li>
</ul>
<p>Then, they need to use <code>_</code> on localizable strings:</p>
<pre><code class="language-js">var cm = require("context-menu");
cm.Item({
label: _("My Menu Item"),
context: cm.URLContext("*.mozilla.org")
});
</code></pre>
<p>Now, they have two choices:</p>
<ul>
<li>use a string written in their prefered language, like here.
So that they don't have to create a locale file.</li>
<li>use an ID. Instead of <code>_("My Menu Item")</code>, we will use: <code>_("contextMenuLabel")</code>.
But it forces to create a localization file in order to map <code>contextMenuLabel</code> to <code>My Menu Item</code>.</li>
</ul>
<p>Then, either a developer or a localizer can generate or modify locales files.
Each jetpack package can have its own <code>locale</code> folder.
This folder contains one JSON file per supported language.
Here is how looks like a jetpack addon:</p>
<pre><code>* my-addon/
* package.json # manifest file with addon name, description, version, ...
* data/ # folder for all static files
* images,
* html files,
* ...
* lib / # folder that contains all JS modules:
* main.js # main module to execute on startup
* my-module.js # custom module that may use localization module
* ...
* locale/ # our main interest!
* en-US.json
* fr-FR.json
* en-GB.json
* ...
</code></pre>
<p>The next iteration will add a new feature to our command line tool,
that is going to generate or update a locale file for a given language by fetching localization strings from source code.
For example, the following command will generate <code>my-addon/locale/fr-FR.json</code> file:</p>
<pre><code class="language-sh">$ cfx fetch-locales fr-FR
</code></pre>
<pre><code class="language-javascript">{
"My Menu Item": "My Menu Item"
}
</code></pre>
<p>Finally, we need to replace right side values with the localized strings:</p>
<pre><code class="language-javascript">{
"My Menu Item": "Mon menu"
}
</code></pre>
<p>And build the final addon XPI file with:</p>
<pre><code class="language-sh"> $ cfx xpi
</code></pre>
<p>Any kind of feedback would be highly appreciated on <a href="https://groups.google.com/group/mozilla-labs-jetpack/t/da50c6dac33b445b">this group thread</a>.</p>
<p>If you want to follow this work,
subscribe to <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=691782">bug 691782</a>.</p>
Jetpack runner
2011-03-31T07:00:00.000Z
http://techno-barje.fr/post/2011/03/31/Jetpack-runner
<p>Here is the very first release of Jetpack runner. This firefox extension built
on top of the Addon SDK is a personal project that aim to ease development of
firefox extension using the SDK. It is a great exhibit of SDK capabilities as
we can now develop such tool using the SDK itself! For now, to create an addon
you need to go thought a python application that only has a command line
interface: <img src="/public/jetpack_runner/cfx.jpg" alt="cfx.jpg" style="margin: 0 auto; display: block;" title="cfx" /> This is painfull to install
and even more annoying to use on Windows as there is no really decent command
line interface. Finally, if we compare to chrome extensions, we only need
chrome to build an addon! This leads me to build a Firefox extension, that can
be really easy to install in Firefox and allow to build really cool interfaces
to create, run and test your addons.<br />
<br /></p>
<h2>Jetpack runner features:</h2>
<img src="/public/jetpack_runner/jr.png" alt="jr.png" style="float: right; margin: 0 0 1em 1em;" title="jr.png, mar. 2011" />
<ul>
<li>Download and install SDK automatically</li>
<li>Create addon from templates</li>
<li>Run an addon</li>
<li>Execute unit-tests</li>
<li>Generate firefox extension XPI file or xulrunner application package</li>
<li>You can run these either in current firefox instance or run them in a new
one</li>
<li>We can execute a package as a firefox extension or as a xulrunner
application</li>
</ul>
<br />
<br />
<h2>Jetpack runner first steps:</h2>
On extension installation, a tab opens automatically on "jetpack:" url, the
main jetpack runner interface. That allow to download and install a precise SDK
release: <img src="/public/jetpack_runner/jr-first-run.jpg" alt="jr-first-run.jpg" style="margin: 0 auto; display: block;" title="jr-first-run.jpg, mar. 2011" /> Then it displays a list of packages provided
by addon SDK. "addon-sdk" is the main package to play with. <img src="/public/jetpack_runner/jr-packages.jpg" alt="jr-packages.jpg" style="margin: 0 auto; display: block;" title="jr-packages.jpg, mar. 2011" /> After
clicking on "Create addon" button, you would easily create a new one by filling
obvious form and selecting a template addon: <img src="/public/jetpack_runner/jr-templates.jpg" alt="jr-templates.jpg" style="margin: 0 auto; display: block;" title="jr-templates.jpg, mar. 2011" /> And
then, you end up on your newly created addon package page, where you can run
it, execute unit tests or download as a firefox extension XPI file: <img src="/public/jetpack_runner/jr-addon.jpg" alt="jr-addon.jpg" style="margin: 0 auto; display: block;" title="jr-addon.jpg, mar. 2011" /><br />
<br />
<h2>Jetpack runner!!!</h2>
Last but not least, here is a link to install it or to checkout the
source.<br />
<br />
<b>Firefox Extension:</b><br />
<a href="/public/jetpack_runner/jetpack-runner-0.1.1.xpi">jetpack-runner-0.1.1.xpi</a><br />
<br />
<b>Source code:</b><br />
<a href="https://github.com/ochameau/jetpack-runner">Github project</a>
Arretetoi.la - la géolocalisation horodaté!
2011-01-08T08:00:00.000Z
http://techno-barje.fr/post/2011/01/08/Arretetoi.la-geolocalisation-horodate
<p style="text-align: center"><a href="http://arretetoi.la/" style="font-size: 2em;">http://arretetoi.la/</a></p>
<img src="/public/arretetoila/icon_128.png" style="float: right;" /> Cette
application web va vous permettre de récupérer une liste de restaurants le long
d'un trajet en voiture. D'abord, vous indiquez votre lieu de départ, d'arrivée
ainsi que l'heure à laquelle vous comptez partir. Tout ceci pour obtenir une
liste de restaurants chaudement recommandés par <a href="http://dismoiou.fr/">Dismoiou.fr</a>. Jusque là, rien de révolutionnaire! Mais
la particularité de ce service est d'afficher des adresses uniquement autour
des villes que vous allez croiser autour des horaires de repas. Ainsi, si vous
faites Paris-Marseille en partant à 8h, le site vous proposera de déjeuner près
de lyon, car vous devrez y être autour de midi!
<p style="font-size: 1.2em; text-align: center"><strong>Arretetoi.la est un
service à la fois géolocalisé mais surtout horodaté!</strong></p>
Les site marche aussi bien sur ordinateur que sur téléphone. La version sur
ordinateur vous permettra de préparer un parcours et d'estimer où vous pourriez
manger. Tandis que la version mobile vous permettra de mettre à jour
l'estimation en fonction de votre progression. En effet, votre téléphone pourra
transmettre votre position exacte et mettre à jour la zone de
déjeuner/diner.<br />
Voici un aperçu de la version ordinateur:
<p style="text-align: center">Indication des lieux de départ/arrivée ainsi que
l'heure de départ <img src="/public/arretetoila/desktop-1.png" style="display: block; margin: auto;" /> Votre trajet horodaté <img src="/public/arretetoila/desktop-2.png" style="display: block; margin: auto;" />
Lieu où vous serez à midi ou 20h, avec les restaurants recommandés <img src="/public/arretetoila/desktop-3.png" style="display: block; margin: auto;" /></p>
La version mobile:
<p style="text-align: center">Indication de votre lieu d'arrivée, le reste se
remplit automatiquement <img src="/public/arretetoila/mobile-1.png" style="display: block; margin: auto;" /> Liste des restaurants recommandés autour de
midi ou 20h <img src="/public/arretetoila/mobile-2.png" style="display: block; margin: auto;" /> Affichage du restaurant sélectionné sur une
carte avec votre trajet <img src="/public/arretetoila/mobile-3.png" style="display: block; margin: auto;" /></p>
Enfin, notez que vous pouvez ajouter cette application à votre écran d'accueil
sur iphone et android. Ainsi que sur ordinateur avec le navigateur chrome via
le <a href="https://chrome.google.com/webstore/detail/dmjmddodainnogedepioklhnhdmebfhk">chrome
web store</a>.
Google maps hacks part1 - auto-suggest location in <input>
2010-11-04T07:00:00.000Z
http://techno-barje.fr/post/2010/11/04/Google-maps-hacks-part1-auto-suggest-location-in
<p>As I'm hacking around Google Maps API, I tried to write some libraries around
it in order to ease futher developments around this very cool API! The first
library brings auto-suggest in html inputs, so users can see a dynamic list of
cities name when they start typing in a form :)<br />
<br />
Here is the source code of it, licenced under LGPL:<br />
<a href="https://github.com/ochameau/google-map-api-suggest">https://github.com/ochameau/google-map-api-suggest</a><br /></p>
<p>And what users may expect: <img src="/public/gmaps/gmapi-suggest-sc1.png" alt="gmapi-suggest-sc1.png" style="margin: 0 auto; display: block;" title="gmapi-suggest-sc1.png, nov. 2010" /> <img src="/public/gmaps/gmapi-suggest-sc2.png" alt="gmapi-suggest-sc2.png" style="margin: 0 auto; display: block;" title="gmapi-suggest-sc2.png, nov. 2010" /></p>
Google maps hacks part2 - cool function to power up route, directions
2010-11-04T07:00:00.000Z
http://techno-barje.fr/post/2010/11/04/Google-maps-hacks-part2-cool-function-to-power-up-route-directions
<p>Another day, another library!<br />
This new library brings some usefull functions and one of them is just awesome
:)<br />
Start with these usefull functions:</p>
<ul>
<li>return the distance between two points,</li>
<li>retrieve the a point at a given distance between two another,</li>
<li>compute the square box with a given size around a point</li>
</ul>
And the awesome function allows you to find the point at a given duration on a
direction computed by google maps API. For example, you may retrieve the
precive point where you are going to be after 1hour driving on the road from
Paris to Berlin!<br />
For more information, I suggest you to check at the README available on github:
<a href="https://github.com/ochameau/google-map-api-path-tools">https://github.com/ochameau/google-map-api-path-tools</a><br />
<p>As usual, source code is available under LGPL Licence.</p>
UIWebView secrets - part3 - How to properly call ObjectiveC from Javascript
2010-10-06T07:00:00.000Z
http://techno-barje.fr/post/2010/10/06/UIWebView-secrets-part3-How-to-properly-call-ObjectiveC-from-Javascript
<p>Let's change the subject: this time no more talks about memory but always on
UIWebView component. When we use this component for something else than just
displaying webpages, like building UI with HTML, Javascript, ... We often want
to call Javascript functions from objective C and the opposite.<br />
<br /></p>
<h2>Call Javascript function from Objective-C:</h2>
The first move is easily done with the following piece of code:
<pre>
// In your Javascript files:
function myJavascriptFunction () {
<pre><code> // Do whatever your want!
}
</code></pre>
<p> // -----------------------------------</p>
<p> // And in your Objective-C code:
// Call Javascript function from Objective-C:
[webview stringByEvaluatingJavaScriptFromString:@"myJavascriptFunction()"];
</pre>
<br />
<br /></p>
<h2>Call Objective-C function from Javascript:</h2>
But calling objective-c from a Javascript function is not easy as Iphone SDK
doesn't offer any native way to do this! So we have to use any king of hack to
do this ...<br />
The most known, used <ins>and buggy</ins> practice is to register a
<em>UIWebViewDelegate</em> on your web view and « catch-and-immediatly-cancel »
a location change done in javascript.
<p style="text-align:right">(a <a href="http://stackoverflow.com/questions/3275093/call-objective-c-method-from-javascript-with-parameter">
very</a> <a href="http://stackoverflow.com/questions/243459/uiwebview-expose-objective-c-to-javascript">
extremely</a> <a href="http://stackoverflow.com/questions/2767902/what-are-some-methods-to-debug-javascript-inside-of-a-uiwebview">
plenty</a> <a href="http://tetontech.wordpress.com/2008/08/14/calling-objective-c-from-javascript-in-an-iphone-uiwebview/">
much</a> <a href="http://www.iphonedevsdk.com/forum/iphone-sdk-development/14501-javascript-interaction-uiwebview-app.html">
advised</a> practice!)</p>
<pre>
// In Objective-C
- someFunctionOnInit {
<pre><code>webView = [[UIWebView alloc] init];
// Register the UIWebViewDelegate in order to shouldStartLoadWithRequest to be called (next function)
webView.delegate = self;
</code></pre>
<p> }</p>
<p> // This function is called on all location change :</p>
<ul>
<li><p>(BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
// Intercept custom location change, URL begins with "js-call:"
if ([[[request URL] absoluteString] hasPrefix:@"js-call:"]) {</p>
<p> // Extract the selector name from the URL
NSArray *components = [requestString componentsSeparatedByString:@":"];
NSString *function = [components objectAtIndex:1];</p>
<p> // Call the given selector
[self performSelector:NSSelectorFromString(functionName)];</p>
<p> // Cancel the location change
return NO;
}</p>
<p>// Accept this location change
return YES;</p>
</li>
</ul>
<p> }</p>
<ul>
<li><p>(void)myObjectiveCFunction {</p>
<p>// Do whatever you want!</p>
</li>
</ul>
<p> }</p>
<p> // -----------------------------------</p>
<p> // Now in your javascript simply do this to call your objective-c function:
// /!\ But for those who just read title and code, take care, this is a buggy practice /!\n window.location = "js-call:myObjectiveCFunction";</p>
<p></pre>
<br />
<br /></p>
<h2>What's wrong with UIWebViewDelegate, shouldStartLoadWithRequest and
location change ?</h2>
There is weird but apprehensible bugs with this practice:<br />
a lot of javascript/html stuff get broken when we cancel a location change:
<ul>
<li>All setInterval and setTimeout immediatly stop on location change</li>
<li>Every innerHTML won't work after a canceled location change!</li>
<li>You may get other really weird bugs, really hard to diagnose ...</li>
</ul>
<p style="text-align: center"><a href="/public/iphone-sdk/NativeBridge/NativeBridge-bug.zip" style="font-size:2em">Sample application highlighting these bugs</a></p>
Key files of this example:
<ul>
<li><strong>MyWebview.m:</strong> Objective-c part, that inherit from
UIWebView. Set the UIWebViewDelegate and catch requests in
shouldStartLoadWithRequest selector.</li>
<li><strong>NativeBridge.js:</strong> Tiny javascript library in order to
change the location and offer a way to send arguments and receive a
response.</li>
<li><strong>webview-script.js:</strong> Test case script, that highlight these
bugs.</li>
</ul>
In webview-script.js: InnerHTML stop working whereas textContent continues to
...
<pre>
document.getElementById("count").innerHTML = i;
document.getElementById("count2").textContent = i;
</pre>
<br />
But we can't charge Apple on this bug. I mean we try to load another location
in the document we are working on! The webview component may start doing stuff
before the delegate call, which cancel the load ...<br />
We have to find alternative way to communicate with the native code!<br />
<br />
<h2>Better way to call Objective-C</h2>
The only thing we have to change is in Javascript code. Instead of changing the
document location, we create an IFrame and set its location to a value that
trigger the shouldStartLoadWithRequest method.<br />
And voilà!
<pre>
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "js-frame:myObjectiveCFunction";
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
</pre>
Here is another sample application, with exactly the same structures and test
file.<br />
But this time you are going to see innerHTML and setTimeout working! Again,
this demo contains a library (NativeBridge.js) that allow to send arguments to
native code and get back a result in javascript asynchronously, with a callback
function.
<p style="text-align: center"><a href="/public/iphone-sdk/NativeBridge/NativeBridge-non-buggy.zip" style="font-size:2em;">Best practice example!</a></p>
<br />
<br />
<h2>Free Objective-C<->Javascript library</h2>
Finally I provide the communication library under LGPL licence so it can ease
your work on iphone platform! As I know that it's really not easy ;-)<br />
<ul>
<li><a href="http://github.com/ochameau/NativeBridge/blob/master/MyWebView.m" style="font-weight:bold;">MyWebView.m</a>: ObjectiveC part,</li>
<li><a href="http://github.com/ochameau/NativeBridge/blob/master/NativeBridge.js" style="font-weight:bold;">NativeBridge.js</a>: Javascript side.</li>
</ul>
The code is full of comment, so you may easily use and tweak it!
<p style="text-align: center"><a href="http://github.com/ochameau/NativeBridge">Github repo</a></p>
UIWebView secrets - part1 - memory leaks on xmlhttprequest
2010-10-04T07:00:00.000Z
http://techno-barje.fr/post/2010/10/04/UIWebView-secrets-part1-memory-leaks-on-xmlhttprequest
<p>My first blog post on iphone subject reveal a big memory bug when using
UIWebView component. This is the (only one) component to display some HTML
content in an iphone interface. UIWebView object has a lot of differents issues
and I'm going to highlight the biggest of them. Actually, all XMLHttpRequests
used in javascript code are fully leaking!!! I mean when you do a request that
retrieve 100ko of data, your memory used grow up for 100ko! This bug is not
always active, but almost always. In fact the trigger to enable it is to simply
open one link in your UIWebView. For example, clicking on a <a> link.</p>
<p>But let's look at a memory usage graph while we execute this <a href="/public/iphone-sdk/UIWebViewLeaks.zip">simple test application</a>: <img src="/public/iphone-sdk/profile-xmlhttprequest-0-then-1-labeled.png" alt="memory usage graph" style="margin: 0 auto; display: block;" title="memory usage graph" /></p>
<ol>
<li>Create the UIWebView object</li>
<li>Load a local HTML test file</li>
<li>Execute 3 XMLHttpRequest to google.com, notice how the memory is freed
three times after each request!</li>
<li>Trigger the leak by opening a page that redirect back to our test file</li>
<li>Execute the same 3 XMLHttpRequest and look how much memory is used and
totally leaked :/</li>
<li>We clean the HTML document with document.body.innerHTML=''; (sometimes free
some memory, when we have a lot of DOM objects)</li>
<li>release the UIWebView (almost no memory freed, next post is going to
analyse that)</li>
</ol>
<p style="text-align: center"><a href="/public/iphone-sdk/UIWebViewLeaks.zip">Test Application</a></p>
<br />
So, to sum up, usually, when you execute this Javascript in a UIWebView:
<pre>
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// Do whatever you want with the result
<pre><code>}
</code></pre>
<p> };
xmlhttp.open("GET", "<a href="http://your.domain/your.request/">http://your.domain/your.request/</a>...", true);
xmlhttp.send();
</pre>
Your are going to have a big memory usage and leak a lot of data!<br />
<br /></p>
<p>But there is a hack to solve this problem: revert what is done when you open
a link.<br />
In fact, the key property which leads to this leak is the
<em>WebKitCacheModelPreferenceKey</em> application setting. And when you open a
link in a UIWebView, this property is automatically set to the value
<em>"1"</em>. So, the solution is to set it back to <em>0</em> everytime you
open a link. You may easily do this by adding a <em>UIWebViewDelegate</em> to
your UIWebView :</p>
<pre>
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[[NSUserDefaults standardUserDefaults] setInteger:0 forKey:@"WebKitCacheModelPreferenceKey"];
}
</pre>
So are you going to have much less crash due to "Low Memory" :)
UIWebView secrets - part2 - leaks on release
2010-10-04T07:00:00.000Z
http://techno-barje.fr/post/2010/10/04/UIWebView-secrets-part2-leaks-on-release
<p>Continue on iphone with leaks around UIWebView! Here is another big one with
no apparent solution. When we try to release a UIWebView component, very few
memory is freed. So any application using this object to display webpages is
going to crash quickly with Low memory exception :(</p>
<br />
I think this memory usage graph gives an idea on how big is this new king of
leak: <img src="/public/iphone-sdk/profile-uiwebview-leak-onrelease.png" alt="memory usage" style="margin: 0 auto; display: block;" title="memory usage" />
<ol>
<li>Create a UIWebView</li>
<li>Load http://www.google.com/</li>
<li>Release the webview (UIWebView dealloc is correctly called!)<br />
Look how so few memory is freed :/</li>
</ol>
<p style="text-align: center"><a href="/public/iphone-sdk/UIWebViewLeaks2.zip">Test Application</a></p>
<p>The leak is all but tiny! Before the loading of the webpage, the application
was using 630kB of memory, and after the release of the UIWebview, 1150kB! So
we have a 500KB leak in order to simply display the home of Google.com!</p>
<p>This time, I didn't manage to find any hack to solve this bug.<br />
So if you have any tips to fix this, don't hesitate to post a comment!</p>
I've tried a lot of differents hacks to free more memory (or use less), like:
<pre>
// Setup the webview to disable some fancy effect on phone number, but doesn't change anything on memory released ...
webview.dataDetectorTypes = UIDataDetectorTypeNone;
webview.allowsInlineMediaPlayback = NO;
</pre>
or
<pre>
// Remove and disable all URL Cache, but doesn't seem to affect the memory
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
</pre>
or
<pre>
// Remove all credential on release, but memory used doesn't move!
NSURLCredentialStorage *credentialsStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSDictionary *allCredentials = [credentialsStorage allCredentials];
for (NSURLProtectionSpace *protectionSpace in allCredentials) {
NSDictionary *credentials = [credentialsStorage credentialsForProtectionSpace:protectionSpace];
for (NSString *credentialKey in credentials) {
[credentialsStorage removeCredential:[credentials objectForKey:credentialKey] forProtectionSpace:protectionSpace];
}
}
</pre>
or
<pre>
// Cleanup the HTML document by removing all content
// This time, this hack free some additional memory on some websites, mainly big ones with a lot of content
[webview stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML='';"];
</pre>
<br />
<br />
But I never reach complete release of memory used by the web component :(
CSS animation+CSS transform+SVG powered by Firefox = Kaleidoscope
2010-09-27T07:00:00.000Z
http://techno-barje.fr/post/2010/09/27/CSS-animationCSS-transformSVG-powered-by-Firefox-Kaleidoscope
<p><a href="/public/kaleidoscope/kaleidoscope-tutorial.html" style="font-size: 1em"><img src="/public/kaleidoscope/kaleidoscope.jpg" alt="kaleidoscope result" style="margin: 0 auto; display: block;" title="kaleidoscope result" /></a><br />
The amazing power of web technologies like CSS, HTML and SVG comes when you mix
them all together! The arrival of the new mozilla specific CSS property
<em>-moz-element</em> totally unleashed the power of CSS animation/transition
when it comes to doing graphical effects. And I'm pretty sure that we may go
even more futher by using others standards like HTML and SVG ...<br />
In order to show you how simple and powerfull these technologies mix can be,
I've add two extra features on top of this kaleidoscope:</p>
<ul>
<li>The first one, use a video instead of an image: Video Kaleidoscope</li>
<li>The second one allow you to specify a custom image by select or drag'n
drop: Custom image</li>
</ul>
I find these two features quite powerfull, and what's totally awesome is how
simple they are: only 3 lines of HTML in the first case, tens lines of
javascript in the second one!
<p style="text-align: center"><a href="/public/kaleidoscope/kaleidoscope-demo.html" style="font-size: 1em">Kaleidoscope demo</a></p>
<p style="text-align: right">Need <a href="http://www.mozilla.com/fr/firefox/all-beta.html">Firefox 4 Beta 6</a> or a
nightly!<br /></p>
Finally, you may look at this tutorial that explain point by point how to
achieve a Kaleidoscope by mixing <em>-moz-element+SVG+CSS animations and
transform</em>:<br />
<p style="text-align: center"><a href="/public/kaleidoscope/kaleidoscope-tutorial.html" style="font-size: 1em">Kaleidoscope tutorial</a></p>
jsctypes + win32api + jetpack = jetintray
2010-08-27T07:00:00.000Z
http://techno-barje.fr/post/2010/08/27/jsctypes-win32api-jetpack-jetintray
<p><a href="https://wiki.mozilla.org/Jsctypes/api">JSCtypes</a>! What a powerfull
tool, that allows to call native libraries with our simple Javascript.<br />
<a href="https://jetpack.mozillalabs.com/">Jetpack</a>! What a powerfull tool,
that allows to build reliably javascript applications, with unittests, memory
profiling, web IDE, ...<br />
And <a href="http://en.wikipedia.org/wiki/Windows_API">WinAPI</a> ... a giant C
library still in production in 2010 that allows to do very various things on
Windows platform.<br />
<br />
Mix all that and you get:</p>
<p style="text-align: center"><a href="http://github.com/ochameau/jetintray" style="font-size: 2em;">JetInTray</a></p>
A jetpack API for adding Tray icons on windows via jsctypes and on linux with a
binary xpcom component (I didn't had time to work on a jsctypes version).<br />
You may checkout this jetpack package directly from <a href="http://github.com/ochameau/jetintray">github</a>.<br />
Or if you want to learn jsctypes I suggest you to look at files in <em>lib</em>
directory and to read my two previous posts on jsctypes.
<ul>
<li>I explained on the <a href="http://blog.techno-barje.fr/post/2010/08/06/JSctypes-reboot">first one</a> how
to start playing with jsctypes, how to create C-structures and call
functions.</li>
<li>Then I showed in the <a href="http://blog.techno-barje.fr/post/2010/08/24/jsctypes-unleashed">second
post</a>, how to create a JS callback passed to the native library as a
function pointer.</li>
</ul>
<br />
<br />
That said, I wanted to highlight some underground hacks around win32api! In
WinAPI, there is no addEventListener/setEventCallback/addActionListener/... In
fact, there is the well known <a href="http://www.toymaker.info/Games/html/wndproc.html">WndProc messages
function</a>, that receives absolutely all event of the application!! (Yes for
real!) We define this function as a static function named <em>WndProc</em>. But
in Jsctypes case, that's impossible to define a static function, we can only
create function pointers. That's where comes <strong>the</strong> not so known
hack which allow to register dynamically such event listener.<br />
<ul>
<li>First we have to define our listener function following the <a href="http://msdn.microsoft.com/en-us/library/ms633573.aspx">WinAPI datatypes</a>
<pre>
Components.utils.import("resource://gre/modules/ctypes.jsm");
var libs = {};
libs.user32 = ctypes.open("user32.dll");
<p>// Define the function pointer type
var WindowProcType =
ctypes.FunctionType(ctypes.stdcall_abi, ctypes.int,
[ctypes.voidptr_t, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t]).ptr;</p>
<p>// Bind a usefull API function
var DefWindowProc = libs.user32.declare("DefWindowProcA", ctypes.winapi_abi, ctypes.int,
ctypes.voidptr_t, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t);</p>
<p>// Set our javascript callback
function windowProcJSCallback(hWnd, uMsg, wParam, lParam) {</p>
<p> // ... do something smart with this event!</p>
<p> // You HAVE TO call this api function when you don't known how to handle an event
// or your apply is going to crash or do nothing
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}</p>
<p>// Retrieve a C function pointer for our Javascript callback
var WindowProcPointer = WindowProcType(windowProcJSCallback);
</pre></li></p>
<li>Then we may fill a WNDCLASS structure with our fresh function pointer. This
structure is used to create a new <em>window class</em> that use it own WndProc
(not the default static function). See <a href="http://msdn.microsoft.com/en-us/library/ms633586%28VS.85%29.aspx">msdn doc</a>
for more information.
<pre>
var WNDCLASS =
ctypes.StructType("WNDCLASS",
[
{ style : ctypes.uint32_t },
{ lpfnWndProc : WindowProcType }, // here is our function pointer!
{ cbClsExtra : ctypes.int32_t },
{ cbWndExtra : ctypes.int32_t },
{ hInstance : ctypes.voidptr_t },
{ hIcon : ctypes.voidptr_t },
{ hCursor : ctypes.voidptr_t },
{ hbrBackground : ctypes.voidptr_t },
{ lpszMenuName : ctypes.char.ptr },
{ lpszClassName : ctypes.char.ptr }
]);
var wndclass = WNDCLASS();
wndclass.lpszClassName = ctypes.char.array()("class-custom-wndproc");
wndclass.lpfnWndProc = WindowProcType(windowProcCallback); // <---- here it is!
RegisterClass(wndclass.address());
</pre></li>
<li>After that we may create a hidden window that is created only to catch
events.
<pre>
var CreateWindowEx =
libs.user32.declare( "CreateWindowExA", ctypes.winapi_abi, ctypes.voidptr_t,
ctypes.long,
ctypes.char.ptr,
ctypes.char.ptr,
ctypes.int,
ctypes.int,
ctypes.int,
ctypes.int,
ctypes.int,
ctypes.voidptr_t,
ctypes.voidptr_t,
ctypes.voidptr_t,
ctypes.voidptr_t
);
var HWND_MESSAGE = -3; // This is the code for message-only window
// http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
var win = CreateWindowEx(
0, wndclass.lpszClassName,
ctypes.char.array()("messages-only-window"),
0, 0, 0, 0, 0,
ctypes.voidptr_t(HWND_MESSAGE), null, null, null);
</pre></li>
<li>Finally, we only have to bind this window to any component which dispatch
messages/events in order to receive them in our <em>windowProcJSCallback</em>
callback. That's it!</li>
</ul>
jsctypes unleashed
2010-08-24T07:00:00.000Z
http://techno-barje.fr/post/2010/08/24/jsctypes-unleashed
<p>As bugs <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=573066" hreflang="en">573066</a> and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=585175" hreflang="en">585175</a>
are fixed and available in last Firefox <a href="http://nightly.mozilla.org/" hreflang="en">nightlies</a>, we can now use JSCtypes at full power!<br /></p>
<p style="text-align: right"><em>Thanks to <a href="http://blog.mozilla.com/dwitte/" hreflang="en">dwitte</a> for quick
fixes!</em></p>
That means :
<ul>
<li>Complex C-struct usage,</li>
<li>The possibility to define a JS callback seen by C library as a function
pointer, and,</li>
<li>Full Win32 API (also called MFC) supports!</li>
</ul>
Lets see how to practice all that on our previous example: TrayIcon via
Win32api. We were able to <em>just</em> display an icon in the <a href="/post/2010/08/06/JSctypes-reboot">previous blogpost</a>. Now we are able to
intercept events from win32api thanks to ctypes.FunctionType. First we define a
plain old javascript function like this one:
<pre>
function windowProcCallback(hWnd, uMsg, wParam, lParam) {
if (lParam == WM_LBUTTONDOWN) {
Components.utils.reportError("Left click!");
/* 0 means that we handle this event */
return 0;
}
else if (lParam == WM_RBUTTONDOWN) {
Components.utils.reportError("Right click!");
return 0;
}
/* Mandatory use default win32 procedure! */
return DefWindowProc(hWnd, uMsg, wParam, lParam);
};
</pre>
This <em>windowProcCallback</em> is a javascript implementation for a WNDPROC
callback as <a href="http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx" hreflang="en">defined in MSDN</a>. WNDPROC is a key part of Win32Api. These functions
receive all kind of events. They are similar to differents listeners existing
in Javascript/web world, but here in win32api, we often have only one super big
listener which receive all events :/<br />
Next, we have to define this WNDPROC data type with jsctypes, like this:
<pre>
var WindowProcType =
ctypes.FunctionType(ctypes.stdcall_abi, ctypes.int,
[ctypes.voidptr_t, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t]).ptr;
</pre>
We simply describe a function pointer type, for a function which return an int
and accept 4 arguments: hWnd as pointer, uMsg as int, wParam as int and lParam
as int. Then, in our case, we give this function pointer via a structure. So we
may first describe this C-structure, and simply use our previous data type as
type of a structure attribute:
<pre>
var WNDCLASS =
ctypes.StructType("WNDCLASS",
[
{ style : ctypes.uint32_t },
{ lpfnWndProc : WindowProcType}, // <-- Here is the function pointer attribute
{ cbClsExtra : ctypes.int32_t },
{ cbWndExtra : ctypes.int32_t },
{ hInstance : ctypes.voidptr_t },
{ hIcon : ctypes.voidptr_t },
{ hCursor : ctypes.voidptr_t },
{ hbrBackground : ctypes.voidptr_t },
{ lpszMenuName : ctypes.char.ptr },
{ lpszClassName : ctypes.char.ptr }
]);
</pre>
And finally, we convert our Javascript function to a C-Function pointer by
using the datatype as a function and giving our callback as an argument.
<pre>
var wndclass = WNDCLASS();
wndclass.lpszClassName = ctypes.char.array()("class-trayicon");
wndclass.lpfnWndProc = WindowProcType(windowProcCallback); // <---- here it is!
RegisterClass(wndclass.address());
</pre>
All this hard work to be able to detect clicks on our tray icon! I've built a
full example file <a href="/public/demo/jsctypes/example-jsctypes-full-power.txt">right here</a> (with a
lot of comments). And here is one hack that allow you to test it remotly in
your Javascript Console. You just have to copy an icon in c:\default.ico. Here
is a sample <a href="/public/demo/jsctypes/default.ico">ico file</a>.
<pre>
var x=new XMLHttpRequest(); x.open("GET","http://blog.techno-barje.fr/public/demo/jsctypes/example-jsctypes-full-power.txt",false); x.send(null); window.parent.eval(x.responseText);
</pre>
Or if you want to play with this script locally, here is another magic code:
<pre>
var x=new XMLHttpRequest(); x.open("GET","file://C:/Users/YourUsername/Downloads/example-jsctypes-full-power.txt",false); x.send(null); window.parent.eval(x.responseText);
</pre>
JSctypes round two
2010-08-06T07:00:00.000Z
http://techno-barje.fr/post/2010/08/06/JSctypes-reboot
<p>Jsctypes has been introduced in Firefox 3.6 with simple C function call and
only simple types: int, char, string, ... But the next iteration of jsctypes
that is coming in Firefox 4 is going to allow full C binding, with support of C
structures and the ability to define a javascript function and give it to C
library as a function pointer.</p>
<p>No more compilation, no more mozilla sdk download, nor XPCOM stuff, just
plain javascript and only a tiny part of function and datatype declaration
before doing a native binding!</p>
<p>But let the code talk! Here is an example that display a tray icon on
windows. You can copy and paste this code in your Javascript Console in Firefox
4 beta, just do not forget to change the icon path defined in the loadimage
function call.</p>
<pre>
/* simply load "ctypes" object */
Components.utils.import("resource://gre/modules/ctypes.jsm");
<p> /* Load libraries that we are going to use */
var libuser32 = ctypes.open("user32.dll");
var libshell32 = ctypes.open("shell32.dll");</p>
<p> /* Here is the tedious work of declaring functions arguments types and struct attributes types <em>/
/</em> In fact it's quite easy, you just have to find which precise type are using your native functions/struct <em>/
/</em> but it may be hard to known, for example in windows API, which precise type is behing their "HANDLE" type ... <em>/
/</em> I recommend you to find and look at python ctype binding source code because they already had done this work */</p>
<p> /*
HANDLE WINAPI LoadImage(
__in_opt HINSTANCE hinst,
__in LPCTSTR lpszName,
__in UINT uType,
__in int cxDesired,
__in int cyDesired,
__in UINT fuLoad
);
*/
var loadimage = libuser32.declare("LoadImageA",
ctypes.stdcall_abi,
ctypes.int,
ctypes.int,
ctypes.char.ptr,
ctypes.int,
ctypes.int,
ctypes.int,
ctypes.int);
const LR_LOADFROMFILE = 16;
const IMAGE_ICON = 1;</p>
<p> var notificationdata = ctypes.StructType("NOTIFICATIONDATA",
[{ cbSize : ctypes.int },
{ hWnd : ctypes.int },
{ uID : ctypes.int },
{ uFlags : ctypes.int },
{ uCallbackMessage : ctypes.int },
{ hIcon : ctypes.int },
{ szTip : ctypes.char.array(64) },
{ dwState : ctypes.int },
{ dwStateMask : ctypes.int },
{ szInfo : ctypes.char.array(256) },
{ uTimeoutOrVersion : ctypes.int },
{ szInfoTitle : ctypes.char.array(64) },
{ dwInfoFlags : ctypes.int },
{ guidItem : ctypes.int },
{ hBalloonIcon : ctypes.int }
]);
const NIF_ICON = 0x00000002;</p>
<p> /*
BOOL Shell_NotifyIcon(
__in DWORD dwMessage,
__in PNOTIFYICONDATA lpdata
);
*/
var notifyicon = libshell32.declare("Shell_NotifyIcon",
ctypes.stdcall_abi,
ctypes.bool,
ctypes.int,
notificationdata.ptr);
const NIM_ADD = 0x00000000;</p>
<p> /* And now, the "real" code that is calling C functions */</p>
<p> /* load our ico file */
var hIcon = loadimage(0, "c:\default.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);</p>
<p> /* create a C struct that is defining a notification in tray */
var icon = notificationdata();
icon.cbSize = notificationdata.size;
icon.uFlags = NIF_ICON;
icon.szTip = "My Tray Icon";
icon.hIcon = hIcon;</p>
<p> /* Display this notification! */
notifyicon(NIM_ADD, icon.address());</p>
<p></pre></p>
<p>We will be able to go futher and define a function callback to handle click
events on the trayicon, but there is currently a bug which cause some crashes
when using ctypes.FunctionType on windows. (ctypes.FunctionType allow to
transform a custom Javascript function to a C function pointer)<br />
Here is related bugs, which are still in process:</p>
<ul>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=573066" hreflang="en">Bug 573066 - Fix ctypes stdcall closure tests</a></li>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=585175" hreflang="en">Bug 585175 - Don't automangle ctypes stdcall symbols for WINAPI</a></li>
</ul>
The first leads to crashes with FunctionType, and the second may lead to
lib.declare with unfindable symbols errors when using ctypes.stdcall_abi.
Freemonkeys
2010-02-12T08:00:00.000Z
http://techno-barje.fr/post/2010/02/12/Freemonkeys
<p style="text-align:center"><a href="https://addons.mozilla.org/en-US/firefox/addon/46873" style="font-size: 2em;">Freemonkeys</a></p>
Yet another mozilla powered project! This time, a graphical unit tests
editor/executer which enables you to spellcast an army of monkeys always happy
to work hard on your projects and find bugs for free! <img src="/public/demo/fm/asserts.png" alt="asserts.png" style="margin: 0 auto; display: block; border: 2px solid black; max-width: 300px; max-height: 300px" title="asserts.png, fév. 2010" /><br />
<strong>Here is what they can do:</strong>
<ul>
<li>Launch any Mozilla application: Firefox, Thunderbird and any xulrunner
app,</li>
<li>Use an empty profile for each test execution, or an existing one,</li>
<li>Speak fluently "assert" language: isTrue, isFalse, isDefined, equals, ...
etc,</li>
<li>Report you in real time test execution directly in your test source
code,</li>
<li>They are always ready to work. You don't need to restart Freemonkeys on
each test execution, nor on your application reboot. Freemonkeys is an
independant xulrunner app, which launch yours and then controls it remotly with
a network connexion,</li>
<li>Spot easily any window, any tab and any DOM element with usefull
distinctive parameters: XPath, windows attributes, zIndex order, ... etc,</li>
<li>Offer a way to facilitate node identification by simply clicking on it, and
seeing in real time what are selected node parameters,</li>
<li>They are able to write down some debug messages, inspect javascript objects
with DOM Inspector, take screenshots of any elements,</li>
<li>Ease you some kind of tests, by providing you a simple HTTP webserver in
order to simulate some websites answers,</li>
<li>They are not narrow-minded to synchronous tests but offers an assert
library and some usefull API embracing asynchronous execution of your
code!</li>
</ul>
<br />
Now let's highlight some of these cool features ...<br />
<br />
<h2>Node selection</h2>
Here I was overing the tip of the day image. Freemonkeys spot it by
highlighting it in red and show me all parameters which are going to be used to
find this node later:<br />
<img src="/public/demo/fm/inspector.png" alt="inspector.png" title="inspector.png, fév. 2010" /> You just have to click on it to get back to test
editor and have all javascript code needed to get a reference to this node,
something like this:
<pre>
var top = windows.getRegistered("firefox-window", "topmost");
var tab = top.tabs.current;
var element = elements.xpath(tab, "id('frame')/tbody[1]/tr[5]/td[1]/table[1]/tbody[1]/tr[1]/td[1]/table[1]/tbody[1]/tr[1]/td[1]/img[1]");
</pre>
<br />
<br />
<h2>Elements screenshots</h2>
<br />
Simply write:
<pre>
element.screenshot();
</pre>
And get a screenshot directly in the test editor:<br />
<img src="/public/demo/fm/element.screenshot.png" alt="element.screenshot.png" title="element.screenshot.png, fév. 2010" /><br />
<br />
<h2>Live test execution reporting</h2>
<br />
Your monkeys report each assert status in the test editor, allowing you to keep
the focus on test writing and not losing time by switching from your app to
your terminal, then to your code editor, your terminal and your app ... etc,
etc. <img src="/public/demo/fm/asserts.png" alt="asserts.png" style="margin: 0 auto; display: block; border: 2px solid black; margin: 5px;" title="asserts.png, fév. 2010" /> <img src="/public/demo/fm/debug-msg.png" alt="debug-msg.png" title="debug-msg.png, fév. 2010" /><br />
<br />
<h2>HTTP API</h2>
<br />
<pre>
// Get a reference to a firefox tab
var top = windows.getRegistered("firefox-window", "topmost");
var tab = top.tabs.current;
<p>// Start an HTTP server on port 81
http.start("81");</p>
<p>// A successfull test
// Create an assert objet which is going to wait for a request on root path of our http server
var test = http.assertGetRequest("/");
// Open this page in the tab
tab.open("<a href="http://localhost:81/">http://localhost:81/</a>");
// Now wait for this request
test.wait();</p>
<p>// The same test but with a non-existant page on our local server, so a failing test!
var test = http.assertGetRequest("/");
tab.open("<a href="http://localhost:81/foo">http://localhost:81/foo</a>");
test.wait();
</pre>
<br />
<br /></p>
<h2>Asynchronous tests</h2>
<br />
<pre>
// A usefull function which allow you to block test execution for an amount of time in ms
wait.during(1000);
<p>// The simpliest asynchronous test
wait.forTrue(function () {
return true;
});</p>
<p>// Another, which is going to pass after 3s, with this setTimeout
var v=true;
wait.setTimeout(function() {
v=false;
},3000);
wait.forFalse(function () {
return v;
});</p>
<p>// Finally, a test which will pass when the test function is going to be called ten times
// (wait for the anonymous function returns 10)
var i=0;
wait.forEquals(function () {
return i++;;
}, 10);
</pre>
<br />
<br /></p>
<h1>How to get it ?</h1>
Source code is available on github: <a href="http://github.com/ochameau/freemonkeys">http://github.com/ochameau/freemonkeys</a>
(LGPL licence)<br />
If you are on windows:
<ul>
<li>Download this package: <a href="http://github.com/downloads/ochameau/freemonkeys/freemonkeys-0.1-win.zip">freemonkeys-0.1-win.zip</a></li>
<li>Extract this zip file somewhere</li>
<li>Launch freemonkeys.exe</li>
</ul>
And for linux and mac:
<ul>
<li>Download this one: <a href="http://github.com/downloads/ochameau/freemonkeys/freemonkeys-0.1.zip">freemonkeys-0.1.zip</a></li>
<li>Extract it somewhere</li>
<li>If you don't have xulrunner, download it from <a href="http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/1.9.2rc1/runtimes/">
here</a></li>
<li>Finally, launch Freemonkeys with this command:
<pre>
/path/to/your/xulrunner/dir/xulrunner /path/to/freemonkeys/application.ini
</pre></li>
</ul>
Session per tab in Firefox
2009-12-05T08:00:00.000Z
http://techno-barje.fr/post/2009/12/05/Session-per-tab-in-Firefox
<p>The brand new version of Yoono, <a href="http://www.yoono.com">Yoono7</a>
brings a <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=117222">long-awaited</a> Firefox
feature: the possibility to sign-in on the same website with multiple accounts
in different tabs or windows.<br />
<br />
Althought this new feature is a big technical challenge, for the end user, it's
really simple and non-intrusive! This feature only add one simple "profile"
selector on the left of the URL bar : <img src="/public/accounts/urlbar-global.png" alt="urlbar-global.png" style="margin: 0 auto; display: block;" title="urlbar-global.png, déc. 2009" /> This
is the default status of the selector, it simply says that the current firefox
behavior is "as before", using the same global session for all tabs.<br />
Let's look it when we enable session per tab on different profiles : <img src="/public/accounts/urlbar-gmail.png" alt="urlbar-gmail.png" style="margin: 0 auto; display: block;" title="urlbar-gmail.png, déc. 2009" />
<img src="/public/accounts/urlbar-facebook.png" alt="urlbar-facebook.png" style="margin: 0 auto; display: block;" title="urlbar-facebook.png, déc. 2009" /> <img src="/public/accounts/urlbar-witter.png" alt="urlbar-witter.png" style="margin: 0 auto; display: block;" title="urlbar-witter.png, déc. 2009" /></p>
<p>When you start the browser, open a bookmark, do a search or enter manually
an URL, your request is loaded with the default global session (ie "as
before"). But when you select a profile with this selector, the current tab is
reloaded in order to use one specific totally independant session. Futhermore,
if this website open a new tab or window, or if you click on a link, the new
tab, window or webpage is going to use the same specific session.</p>
<br />
But let's see how to use this feature from the beginning.
<ol>
<li style="clear:both"><img src="/public/accounts/perso-add.png" alt="perso-add.png" style="float: right; margin: 0 0 1em 1em;" title="perso-add.png, déc. 2009" /> <img src="/public/accounts/default-menu.png" alt="default-menu.png" style="float: right; margin: 0 0 1em 1em;" title="default-menu.png, déc. 2009" /> First, we create one session linked to a
personnal account. In this example I take gmail, but it can be any website :
twitter, facebook, flickr, ... whatever! To do so we click on the profile
selector and get menu on the left and we click on "+ new profiles" and get the
right's one.</li>
<li style="clear:both">We are redirected to the homepage URL, where we must
sign-in for this personnal account: poirot.alex. <img src="/public/accounts/perso-signin.png" alt="perso-signin.png" style="margin: 0 auto; display: block;" title="perso-signin.png, déc. 2009" /><br />
And we are now signed in for this "Personnal gmail" session : <img src="/public/accounts/private-signed.png" alt="private-signed.png" style="margin: 0 auto; display: block;" title="private-signed.png, déc. 2009" /><br /></li>
<li style="clear:both"><img src="/public/accounts/add-pro.png" alt="add-pro.png" style="float: right; margin: 0 0 1em 1em;" title="add-pro.png, déc. 2009" /> Then, we do the same for one professional account:
yoono.test.<br />
<img src="/public/accounts/header-pro-signin.png" alt="header-pro-signin.png" style="margin: 0 auto; display: block;" title="header-pro-signin.png, déc. 2009" /><br />
<img src="/public/accounts/header-pro-signed.png" alt="header-pro-signed.png" style="margin: 0 auto; display: block;" title="header-pro-signed.png, déc. 2009" /><br /></li>
<li style="clear:both"><img src="/public/accounts/home-private.png" alt="home-private.png" style="float: right; margin: 0 0 1em 1em;" title="home-private.png, déc. 2009" /> Later, we can reopen one of these sessions
directly to the homepage with the profile selector and get automatically signed
in. The "switch to" link doesn't go to the homepage and only reload the current
tab with the selected session (very usefull for Facebook connect, sharing,
...). <img src="/public/accounts/private-signed.png" alt="private-signed.png" style="margin: 0 auto; display: block;" title="private-signed.png, déc. 2009" /></li>
</ol>
<br />
<p style="text-align: center;font-weight: bold; font-size: 1.2em">So we can
open as many different account in multiple tabs or windows!</p>
<br />
<br />
<h2>Important note:</h2>
The current version of Yoono7 doesn't allow custom profile creation. The only
profiles you can use is the ones automatically created for each account
registered in the yoono's sidebar. But watch for the next minor releases of
Yoono7, we are going to ship all this very soon!<br />
<br />
<h2>Another demo of this feature in video:</h2>
<div class="external-media" style="margin: 1em auto; text-align: center;">
<object type="application/x-shockwave-flash" data="http://www.youtube.com/v/V51S6BxTPiw&hl=en_US&fs=1" width="480" height="385"><param name="movie" value="http://www.youtube.com/v/V51S6BxTPiw&hl=en_US&fs=1" />
<param name="wmode" value="transparent" /></object></div>
Mozilla memory profiling, part 2: a working tool
2009-11-26T08:00:00.000Z
http://techno-barje.fr/post/2009/11/26/Mozilla-memory-profiling-part-2
<p>Here is another part of my work on memory analysis in Mozilla :</p>
<p style="text-align: center"><a href="/public/another-profiler/another-profiler_techno-barje.fr.xpi" style="font-size: 2em;">another-profiler_techno-barje.fr.xpi</a></p>
<br />
This new version of "Another memory profiler" is now an effective tool, which
display a lot of information about all objects living in your Firefox instance!
By <em>all</em> I mean not only website javascript objects, but all objects
used by Firefox in its windows, sidebars, extensions, all tabs, iframes, etc.
The previous version allowed you only to select one component : a DOM
window(website, sub-iframe or any XUL window), a XPCOM service or a JS Module
:<br />
<br />
<img src="/public/another-profiler/another-components-list.png" alt="another-components-list.png" style="margin: 0 auto; display: block; border: 1px solid black" title="another-components-list.png, nov. 2009" />
<div style="text-align: center; font-weight: bold">Component selection</div>
<br />
Now you can get a report about currently living objects : the ones that are
still used because there is at least one reference to each of them. This report
first display all javascript files involved in your component :<br />
<br />
<img src="/public/another-profiler/another-lines-browser.png" alt="another-lines-browser.png" style="margin: 0 auto; display: block; border: 1px solid black" title="another-lines-browser.png, nov. 2009" />
<div style="text-align: center; font-weight: bold">File selection</div>
<br />
By selecting one file, you are seeing the number of living object sorted by
there instantiation line :<br />
<br />
<img src="/public/another-profiler/another-objects-browser.png" alt="another-objects-browser.png" style="margin: 0 auto; display: block;" title="another-objects-browser.png, nov. 2009" />
<div style="text-align: center; font-weight: bold">Living objects
information</div>
<br />
Finally, this tool display objects counts for each line sorted by there type.
But Javascript is not a strongly typed language, so it's not really easy to
sort its objects by a type! That's why there are several way to describe a JS
object :
<ul>
<li>Define a JS object by its attributes, like Atul Varma is doing in <a href="http://www.toolness.com/wp/?p=709">its current work</a>,</li>
<li>By its JS Prototype name, very usefull "typing" when you are using
Prototype and build Object-Oriented JS,</li>
<li>We are facing some specialized objects like all DOM objects :
HTMLFormElement, HTMLDivElement, ...</li>
<li>And finally all native types, like Array, String, Date, RegExp, ...
etc.</li>
</ul>
<br />
<br />
Finally, let's see how to make this extension work :
<ul>
<li><strong>First</strong> It contains a binary component which is only built
for Firefox 3.5 and 3.6 for Windows and Linux-32.</li>
<li><strong>Secondly</strong> The memory profiling component is a patched
version of the Mozilla Jetpack's one, so take care to disable Jetpack, before
testing this!</li>
<li><strong>Then</strong> In order to get the maximum information about your
living JS object, I strongly encourage you to set these two prefs to false :
<pre>
javascript.options.jit.content = false
javascript.options.jit.chrome = false
</pre>
(That's because Tracemonkey optimise loops and seems to embed empty stack frame
information on these loop's execution ...)</li>
<li>That being said, you just have to install this extension <a href="/public/another-profiler/another-profiler_techno-barje.fr.xpi">another-profiler_techno-barje.fr.xpi</a>,
go to your Tools menu and click on "Open another memory profiler".</li>
</ul>
<br />
<br />
<strong>Come back for the next post for some more explanation on displayed
results with simple scripts examples.</strong>
Firefox Everywhere
2009-11-23T08:00:00.000Z
http://techno-barje.fr/post/2009/11/23/Firefox-Everywhere
<p>After <a href="/post/2009/11/06/Yoono-Desktop-Portable">Yoono Desktop
Portable</a>, here is Firefox Everywhere : a customized Firefox package which
can be executed on any OS : Windows, Linux, MacOS and with a shared profile
directory allowing you to travel with all your Firefox data on a usb stick!</p>
<p style="text-align: center">Check out this surf session starting on Windows,
<img src="/public/firefox-portable/firefox-portable-win.png" alt="firefox-portable-win.png" style="margin: 0 auto; display: block; border: 1px solid black;" title="firefox-portable-win.png, nov. 2009" /> Then on Linux, <img src="/public/firefox-portable/firefox-portable-linux.png" alt="firefox-portable-linux.png" style="margin: 0 auto; display: block; border: 1px solid black;" title="firefox-portable-linux.png, nov. 2009" /> And finally on a Mac! <img src="/public/firefox-portable/firefox-portable-mac.png" alt="firefox-portable-mac.png" style="margin: 0 auto; display: block; border: 1px solid black" title="firefox-portable-mac.png, nov. 2009" /></p>
<p style="text-align: center;font-size: 1.2em;">FirefoxEverywhere</p>
<strong>Download:</strong>
<ul>
<li>The <a href="http://bit.ly/firefox-portable-all">Full edition</a> (69MB)
which works with all OS</li>
<li>or the <a href="http://bit.ly/firefox-portable-win">Windows only
version</a> (13MB)</li>
<li>or the <a href="http://bit.ly/firefox-portable-mac">MacOs only version</a>
(26MB)</li>
</ul>
<strong>Tips:</strong>
<ul>
<li><ins>Don't forget to properly disconnect your usb stick, or your profile
may explode!</ins></li>
<li>This version is based on Firefox 3.6 branch in order to gain startup
performance improvements of this incoming version! So don't be afraid to have a
browser named Namoroka instead of Firefox.</li>
<li>On Windows, execute: launch-windows.exe</li>
<li>On MacOS, launch-macos.command</li>
<li>On Linux, either launch-linux-64.sh or launch-linux.sh(for 32bits)</li>
</ul>
Mozilla memory profiling
2009-11-16T08:00:00.000Z
http://techno-barje.fr/post/2009/11/16/Mozilla-memory-profiling
<p>As a Mozilla hacker, extension developer and Javascript expert, I've been
really exited to see the <a href="http://www.toolness.com/wp/?p=709">current
work of Atul Varma</a> on memory profiling in Firefox! It's naturally the next
step of tool to build after <a href="https://addons.mozilla.org/en-US/firefox/addon/9954">XUL Profiler</a>, which
track CPU consumption and Javascript functions calls.<br />
So, instead of waiting for web developers to describe their future new "memory"
firebug tab :), I've searched what information we can retrieve from JS API. And
I've not limited my scope to web content but I take all Browser objects into
account.<br />
<br />
First I've tried to find a meaningful parent for <strong>every</strong> living
object.<br />
In the Mozilla planet we may face with three main types of parents :</p>
<ul>
<li>window : chrome (browser.xul, popups, jsconsole, sidebars, ...) or content
(websites,popups,iframes)</li>
<li>xpcom services</li>
<li>JS modules</li>
</ul>
(But there is also XBL, sandboxes and some others strange things like
"Block")<br />
<br />
Here is the first result of this work :
<p style="text-align: center"><a href="/public/another-profiler_techno-barje.fr-1.0.xpi" style="font-size: 2em;">another-profiler_techno-barje.fr-1.0.xpi</a></p>
<strong>This extension need <a href="https://addons.mozilla.org/fr/firefox/addon/12025">Jetpack 0.6+</a>.</strong>
It adds a "Open another memory profiler" item in Tools menu and display all
living windows, xpcoms and modules. Then when you select one of them, it
displays the simplest profiling ever: number of js objects group by C++ native
class. I'll show you in the next blog post how to display a better
profiling!<br />
<img src="/public/another-profiler-1.0.png" alt="another-profiler-1.0.png" style="margin: 0 auto; display: block;" title="another-profiler-1.0.png, nov. 2009" /> But for now, I'm going to show you all
the code needed to make this first version.<br />
<br />
For the living windows, there is a lot of cases, but it's simple :
<pre>
// Get the list of absolutery ALL windows living in a Firefox session, stored as a Tree
function getAllWindows() {
var windows = [];
<p> // Begin by iterating over all top chrome windows (browser, jsconsole, dominspector, etc.)
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var enumerator = wm.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()) {
var win = enumerator.getNext();
if (win instanceof Components.interfaces.nsIXULWindow) {
// Search for all children windows (sidebar, content, iframes, ...)
parseDocshell(win.docShell);
}
}</p>
<p> function getWindowByDocShell(docShell) {
if (!(docShell instanceof Components.interfaces.nsIInterfaceRequestor))
return;
return docShell.getInterface(Components.interfaces.nsIDOMWindow);
}
function parseDocshell(docShell) {
if (!docShell) return;
var domWindow = getWindowByDocShell(docShell);</p>
<pre><code>var topWindow = {
type : &quot;window&quot;,
name : domWindow.document.title,
href : domWindow.location.href,
object: domWindow,
children: []
};
windows.push(topWindow);
var topWindows = [topWindow];
var treeItemType = Components.interfaces.nsIDocShellTreeItem.typeAll;
// From inspector@mozilla.org inspector.js appendContainedDocuments
// Load all the window's content docShells
var containedDocShells = docShell.getDocShellEnumerator(treeItemType,
Components.interfaces.nsIDocShell.ENUMERATE_FORWARDS);
while (containedDocShells.hasMoreElements())
{
var childShell = containedDocShells.getNext().QueryInterface(Components.interfaces.nsIDocShell);
if (childShell == docShell) {
// It's the current topWindow
continue;
}
var childDOMWindow = getWindowByDocShell(childShell);
if (!childDOMWindow) continue;
var parent;
for(var i=0; i&lt;topWindows.length; i++) {
if (topWindows[i].object == childDOMWindow.parent) {
parent = topWindows[i];
break;
}
}
var newWindow = {
type : &quot;window&quot;,
name : childDOMWindow.document.title,
href : childDOMWindow.location.href,
object: childDOMWindow,
children : []
};
topWindows.push(newWindow);
if (parent)
parent.children.push(newWindow);
else
topWindow.children.push(newWindow);
}
delete topWindows;
</code></pre>
<p> }</p>
<p> // Finally, don't forget <em>the</em> hidden window, it's a big one used by many extensions!
var hiddenWindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
.getService(Components.interfaces.nsIAppShellService)
.hiddenWindow;
if (hiddenWindow instanceof Components.interfaces.nsIXULWindow) {
parseDocshell(hiddenWindow.docShell);
}</p>
<p> return windows;
}
</pre>
<br />
<br />
For XPCOM services, it's shorter, but it' an unknown practice :</p>
<pre>
// Get the list of all XPCOM services (not the xpcom objects, only services) in a Firefox session
function getAllXPCOMServices() {
var instanciatedServices = [];
var serviceManager=Components.manager.QueryInterface(Components.interfaces.nsIServiceManager);
var supports = Components.interfaces.nsISupports;
for(var cl in Components.classes) {
try {
if (serviceManager.isServiceInstantiated(Components.classes[cl],supports)) {
var service=Components.classes[cl].getService(supports);
if (service.wrappedJSObject) {
// Get the global object
service=service.wrappedJSObject.__parent__;
if (!service)
service=Components.classes[cl].getService(supports).__parent__;
instanciatedServices.push({
type : "xpcom",
name : cl,
object : service
});
}
}
} catch(e) {
// serviceManager.isServiceInstantiated is throwing if there is no instance ...
}
}
return instanciatedServices;
}
</pre>
<br />
<br />
But for JS Modules, I've not found any way to get those ...<br />
The only solution I've got was to do a quick profiling and identify them :
<pre>
function searchJSModules () {
var jsmodules = [];
<p> var roots=getGCRoots();
for(var r in roots) {
var id = roots[r];
var info = getObjectInfo(id);
var properties = getObjectProperties(id);
/*
// We can also identify XPCOM by reading global NSGetModule function
var nsgetmodule = getObjectProperty(id,"NSGetModule").NSGetModule;
if (nsgetmodule) {
print (" --> is an XPCOM");
print (" --> defined in : "+getObjectInfo(nsgetmodule).filename);
continue;
}
*/
// See if the current object has a EXPORTED_SYMBOLS object
// We suppose every JS Module has one ...
var exportedsymbols = getObjectProperty(id,"EXPORTED_SYMBOLS").EXPORTED_SYMBOLS;
if (!exportedsymbols) continue;</p>
<pre><code>// Then search for the first declared function
// Which will allow us to get the file of this module!
// Begin to search in EXPORTED_SYMBOLS
var symbols = getObjectProperties(exportedsymbols);
var filename;
for(var i in symbols) {
var s = getObjectProperty(id,symbols[i])[symbols[i]];
var inf = getObjectInfo(s);
if (!inf) continue;
if (inf.nativeClass==&quot;Function&quot; &amp;&amp; inf.filename) {
filename=inf.filename;
break;
} else if (inf.nativeClass=&quot;Object&quot;) {
var subprops = getObjectProperties(s);
for(var j in subprops) {
var subs = subprops[j];
var subinf = getObjectInfo(subs);
if (!subinf) continue;
if (subinf.nativeClass==&quot;Function&quot; &amp;&amp; subinf.filename) {
filename = subinf.filename;
break;
}
}
if (filename) break;
}
}
if (!filename) {
// Unable to found a function in exported_symbols objects
// now try to find a function defined in global context
var table = getObjectTable();
var count=0;
for (var subid in table) {
var subinf = getObjectInfo(parseInt(subid));
if (subinf &amp;&amp; subinf.parent == id &amp;&amp; subinf.nativeClass==&quot;Function&quot; &amp;&amp; subinf.filename) {
filename = subinf.filename;
break;
}
}
}
if (filename) {
var file = filename;
var res = filename.match(/\/([^\/]+\/[^\/]+\/[^\/]+\.\w+)$/);
if (res)
file = decodeURIComponent(res[1]);
jsmodules.push({
type : &quot;jsmodule&quot;,
name : file,
file : filename,
object: id
});
} else {
// we were unable to find any function, we may try to search deeper
}
</code></pre>
<p> }</p>
<p> return JSON.stringify(jsmodules);
}</p>
<p>function getAllJSModules() {
var factory = Components.classes["@labs.mozilla.com/jetpackdi;1"]
.createInstance(Components.interfaces.nsIJetpack);
var endpoint = factory.get();
var json = endpoint.profileMemory(searchJSModules.toSource()+"
searchJSModules()", "find-jsmodules.js", 1, null);
return JSON.parse(json);
}
</pre>
<br />
<br />
Finally, here is the function which retrieve objects counts for one parent. It
use the Jetpack memory profiler XPCOM.</p>
<pre>
function profileFunction() {
var namedObjects=getNamedObjects();
// namedObjects["parent"] is null ... why ?!
var parent;
for(var i in namedObjects) {
if (i=="parent") {
parent = parseInt(namedObjects[i]);
}
}
// Remove web content windows js wrapper
var inf = getObjectInfo(parent);
if (inf && inf.nativeClass=="XPCSafeJSObjectWrapper") {
parent = inf.wrappedObject;
}
var children = {};
// Check every JS object
var table = getObjectTable();
for(var i in table) {
var info = getObjectInfo(parseInt(i));
// Search if this one is related to the selected parent
// ie walk throught all parents in order to find if the current object is a descendant of selected parent
if ( info.parent != parent ) {
var parentMatch = false;
var p = info.parent;
while(true) {
var subinfo = getObjectInfo(p);
if (!subinfo) break;
if ( subinfo.id == parent || subinfo.parent == parent ) {
// Answer= Yes
parentMatch = true;
break;
}
// Walk throught encapsulated objects
if (subinfo.outerObject && subinfo.outerObject!=p) {
p = subinfo.outerObject;
continue;
}
p = subinfo.parent;
}
// Answer= Yes
if (!parentMatch) continue;
}
if (!children[info.nativeClass])
children[info.nativeClass] = 0;
children[info.nativeClass]++;
}
return JSON.stringify(children);
}
function profileParent(parent) {
var factory = Components.classes["@labs.mozilla.com/jetpackdi;1"]
.createInstance(Components.interfaces.nsIJetpack);
var endpoint = factory.get();
var json = endpoint.profileMemory(profileFunction.toSource()+"
profileFunction()", "profile.js", 1, {parent: parent});
return JSON.parse(json);
}
</pre>
<p style="text-align: right"><a href="https://wiki.mozilla.org/Labs/Jetpack/Binary_Components#Memory_Profiling">More
information</a></p>
<br />
Come back for the next blog post to get the 2.0 version :)
Hackability test : Google Chrome vs Mozilla Firefox (with Jetpack)
2009-11-10T08:00:00.000Z
http://techno-barje.fr/post/2009/11/10/Google-Chrome-vs-Mozilla-Firefox(with-Jetpack)
<p>Here is a small summary of what we are able to extend in Chrome and
Firefox(with Jetpack).<br />
<br />
<br /></p>
<h1>How to add a new item in context menu (right click menu)</h1>
<strong>In Chrome</strong>
<p>You just can't, <a href="http://www.google.com/support/forum/p/Chrome/thread?tid=375371626e2ba749&hl=en">
see here</a>.</p>
<strong>In Firefox, with Jetpack</strong>
<pre>
jetpack.future.import("menu");
jetpack.future.import("selection");
<p>jetpack.menu.context.page.add(function(context)({
label: "My context menu item",
command: function(target) {</p>
<pre><code> // Do something fun with this selection
jetpack.notifications.show( &quot;Current selection : &quot;+jetpack.selection.text );
</code></pre>
<p> }
}));
</pre>
<img src="/public/jetpack-context.png" alt="jetpack-context.png" style="margin: 0 auto; display: block; border: 1px solid black" title="jetpack-context.png, nov. 2009" /> <a href="https://wiki.mozilla.org/Labs/Jetpack/JEP/14">A lot more information
here</a><br />
<br />
<br /></p>
<h1>How to add a sidebar ?</h1>
<strong>In Chrome</strong>
<p>You just can't, it's a well known limitation, but nobody say it loud.</p>
<strong>In Firefox, with Jetpack</strong>
<pre>
jetpack.future.import("slideBar");
jetpack.slideBar.append({
url: "about:blank",
width: 220,
persist: true,
autoReload: false,
onReady: function(slide){
<pre><code> // Do something fun with this sidebar
var doc = slide.contentDocument;
doc.body.innerHTML=&quot;Hello world from techno-barje!&quot;
</code></pre>
<p> }
});
</pre>
<img src="/public/jetpack-slidebar.png" alt="jetpack-slidebar.png" style="margin: 0 auto; display: block; border: 1px solid black" title="jetpack-slidebar.png, nov. 2009" /> <a href="https://wiki.mozilla.org/Labs/Jetpack/JEP/16">More details here</a><br />
<br />
<br /></p>
<h1>How to have settings and display them to users ?</h1>
<strong>In Chrome</strong>
<p>You have to add custom menu somewhere, as you want. So each extension may
display a different way to fill up their settings ...</p>
<strong>In Firefox, with Jetpack</strong>
<pre>
var manifest = {
settings: [
{
name: "twitter",
type: "group",
label: "Twitter",
settings: [
{ name: "username", type: "text", label: "Username" },
{ name: "password", type: "password", label: "Password" }
]
},
{
name: "facebook",
type: "group",
label: "Facebook",
settings: [
{ name: "username", type: "text", label: "Username", default: "jdoe" },
{ name: "password", type: "password", label: "Secret" }
]
},
{ name: "music", type: "boolean", label: "Music", default: true },
{ name: "volume", type: "range", label: "Volume", min: 0, max: 10, default: 5 },
{ name: "size", type: "number", label: "Size" },
{ name: "mood", type: "member", label: "Mood", set: ["happy", "sad", "nonchalant"] }
]
};
</pre>
<img src="/public/jetpack-settings.png" alt="jetpack-settings.png" style="margin: 0 auto; display: block; border: 1px solid black" title="jetpack-settings.png, nov. 2009" /> <a href="https://wiki.mozilla.org/Labs/Jetpack/JEP/24">Full planned API</a>(work in
progress)<br />
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=511764">You can track
progress here</a><br />
<a href="http://mykzilla.org/jetpack/settings-test.html">Jetpack demo
here</a><br />
<br />
<br />
<h1>How to display a system notification (or something like) ?</h1>
<strong>In Chrome</strong>
<p>You may display a custom HTML popup, but you will have to handle youself
display/hide of this popup, his style and each extension will have his
notification system ...</p>
<strong>In Firefox, with Jetpack</strong>
<pre>
jetpack.notifications.show({title: 'hai2u', body: 'o hai.', icon: 'http://www.mozilla.org/favicon.ico'});
</pre>
<img src="/public/jetpack-notifications.png" alt="jetpack-notifications.png" style="margin: 0 auto; display: block; border: 1px solid black" title="jetpack-notifications.png, nov. 2009" /> <a href="https://jetpack.mozillalabs.com/api.html">More info here</a><br />
<br />
<br />
Yoono Desktop Portable
2009-11-06T08:00:00.000Z
http://techno-barje.fr/post/2009/11/06/Yoono-Desktop-Portable
<p>Here is a new Yoono feature that really need some emphasis!</p>
<p style="text-align: center"><a href="http://blog.yoono.com/2009/10/get-yoono-desktop-to-go/" style="font-size: 1.5em">Yoono Desktop Portable</a></p>
<p>This new version allows you to launch the Yoono's social network aggregation
tool directly from a usb key. And the most important point is that your profile
is saved on it, so no personal data is saved in the computer you're using and
all your settings and socials updates travel with you.<br />
The icing on the cake is that it's even more "portable" than just that! You can
even launch this application on Windows <ins>AND</ins> MacOS!!!</p>
<p><strong>Only two steps to test this :</strong></p>
<ul>
<li>Unzip <a href="http://cache.yoono.com/download/desktop/yoono-desktop-portable.zip">yoono-desktop-portable.zip</a>
somewhere on a usbkey</li>
<li>On windows, launch <em>"Yoono Desktop Portable.exe"</em></li>
<li>On macos, launch <em>"Yoono Desktop Portable"</em></li>
</ul>
<img src="/public/YoonoDesktopShort.png" alt="YoonoDesktopShort.png" style="margin: 0 auto; display: block;" title="YoonoDesktopShort.png, nov. 2009" />
<p>The only drawback is usb drive read/write performance. Depending on drive
model Yoono Desktop appears to take long time to load. But we are going to gain
a huge speed up with <a href="http://autonome.wordpress.com/tag/performance/">current work</a> on mozilla
platform about startup performance. So don't forget to check out yoono desktop
portable when Firefox 3.6 is out! (release planned before the end of the
year)</p>
SabnzbFox
2009-11-03T08:00:00.000Z
http://techno-barje.fr/post/2009/11/03/sabnzbFox
<p>Now, a real world usage of my <a href="/post/2009/10/30/Catch-all-requests-to-a-specific-mime-type/file-extension-in-Firefox">
previous JS Module</a>.<br />
<br /></p>
<p style="text-align:center"><a href="https://addons.mozilla.org/en-US/firefox/addon/46873" style="font-size: 2em;">SabnzbdFox</a></p>
<br />
<strong>SabnzbdFox features</strong>
<ul>
<li>Catch absolutely all NZB file requests</li>
<li>Automatically save all these files to a specific directory (not limited to
sabnzbd usage!)</li>
<li>Or Automatically upload to sabnzbd server via its <a href="http://sabnzbd.wikidot.com/automation-support">web-API</a></li>
<li>Display current downloads count</li>
</ul>
<strong>SabnzbdFox prerequisites</strong>
<ul>
<li>Firefox</li>
<li><a href="http://www.sabnzbd.org/">Sabnzbd</a> or any newsreader which
automatically read nzb files in a directory.</li>
</ul>
<p style="text-align: center">install <a href="https://addons.mozilla.org/en-US/firefox/addon/46873">SabnzbdFox on
addons.mozilla.org</a></p>
<img src="/public/sabnzbd-screenshot.png" alt="sabnzbdfox" style="margin: 0 auto; display: block; border: 2px solid black; margin-bottom: 5px;" title="sabnzbdfox" />
Catch all requests to a specific mime type/file extension in Firefox
2009-11-02T08:00:00.000Z
http://techno-barje.fr/post/2009/11/02/Catch-all-requests-to-a-specific-mime-type-file-extension-in-Firefox
<p>Here is a Mozilla Javascript Module which allow to catch <ins>absolutely</ins>
all requests based on their Content-Type Http header (ie Mime type) or their
filename.</p>
<p style="text-align:center"><strong><a href="/public/contentTypeObserver.js">contentTypeObserver.js</a></strong><br />
(under LGPL License)</p>
Normally, it would be simple to catch all firefox request by adding a
nsIURIContentListener like this :
<pre>
Components.classes["@mozilla.org/uriloader;1"].getService(Components.interfaces.nsIURILoader).registerContentListener( ...nsIURIContentListener... );
</pre>
But for some reason, this listener is bypassed <a href="http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsURILoader.cpp#403">
here</a> when the HTTP request contains a Content-Disposition header :(<br />
So I give you there all Mozilla black magic needed to catch really all
requests.<br />
<p style="font-weight: bold; text-align: center">Hello world</p>
<pre>
Components.utils.import("resource://your-extension/contentTypeObserver.js");
var contentTypeObserver = {};
<p>// Tell if we must catch requests with this content-type
// requestInfo is an object with 3 attributes : contentType, contentLength and fileName.
contentTypeObserver.getRequestListener = function (requestInfo) {
// Return a new instance of nsIWebProgressListener
// (a new instance to avoid conflicts with multiple simultaneous downloads)
return {
onStartRequest : function (request, context) {</p>
<pre><code>},
onStopRequest : function (request, context, statusCode) {
},
onDataAvailable : function (request, context, inputStream, offset, count) {
}
</code></pre>
<p> };
// There is an helper function that allow to automatically save this request to a file,
// you just have to pass destinationFile argument which hold a nsIFile instance :
return createSaveToFileRequestListener(requestInfo, destinationFile, function () { dump("file : "+destinationFile.spec+" downloaded!
"); }
}</p>
<p>addContentTypeObserver(contentTypeObserver);
</pre></p>
Ocaml native code debugging
2009-11-02T08:00:00.000Z
http://techno-barje.fr/post/2009/11/02/Ocaml-native-code-debugging
<p>Note: english translation of my <a href="/post/2008/11/09/Ocaml-native-code-debugging">previous post</a></p>
<p><img src="/public/./.t-caml-valid-callgraph_s.jpg" alt="t-caml-valid-callgraph.png" style="float:right; margin: 0 0 1em 1em;" title="t-caml-valid-callgraph.png, juin 2008" /></p>
<p>Now that <a href="http://caml.inria.fr/mantis/bug_view_page.php?bug_id=4642" hreflang="en">Improve gnu ELF</a> bug is commited in ocaml 3.11+, KCachegrind
can generate beautifull callgraphs.</p>
<p>This patch consist in adding .size instructions (in ELF assembly code) in
order to allow valgrind to interpret all symbols (camlT_entry, camlT_foo,
camlT_bar, ..) and it can so display symbols names instead of their hexadecimal
numbers!!!</p>
<h2>ELF instructions for debug</h2>
<p>Now, we may want these tools to be able to display file name and line number
for all functions.(File name is present in symbols name but it doesn't allow
full usage of these tools)</p>
<p>Let's see how's gcc working :</p>
<pre>
int bar(int a) {
return 1+a;
}
<p> int foo(int a) {
return 2+bar(a);
}</p>
<p> int main() {
foo(3);
}
</pre></p>
<pre>
$ gcc -O0 -g -S t.c
</pre>
<pre>
.globl bar
.type bar, @function
bar:
.LFB2:
.file 1 "t.c"
.loc 1 1 0
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl %edi, -4(%rbp)
.loc 1 2 0
movl -4(%rbp), %eax
incl %eax
.loc 1 3 0
leave
ret
.LFE2:
.size bar, .-bar
.globl foo
.type foo, @function
</pre>
<p>Usefull instructions are .file and .loc :</p>
<ul>
<li>.file define a file path and bind it to an id<br /></li>
</ul>
<p>-> .file $file_id"$file_path"</p>
<ul>
<li>.loc define a file, a line and column number to the next
instructions<br /></li>
</ul>
<p>-> .loc $file_id$ $line$ $columnENTRIESlt;/p>
<h2>Suggested solution</h2>
<p>The compiler module which emits these instructions is : <a href="http://camlcvs.inria.fr/cgi-bin/cvsweb/ocaml/asmcomp/i386/emit.mlp?rev=1.41.2.2;only_with_tag=release311" hreflang="en">asmcomp/i386/emit.mlp</a><br />
And especially this "fundecl" function :</p>
<pre>
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
(* ... *)
` .globl {emit_symbol fundecl.fun_name}
`;
`{emit_symbol fundecl.fun_name}:
`;
if !Clflags.gprofile then emit_profile();
let n = frame_size() - 4 in
if n > 0 then
` subl ${emit_int n}, %esp
`;
`{emit_label !tailrec_entry_point}:
`;
emit_all true fundecl.fun_body;
List.iter emit_call_gc !call_gc_sites;
emit_call_bound_errors ();
List.iter emit_float_constant !float_constants;
match Config.system with
"linux_elf" | "bsd_elf" | "gnu" ->
` .type {emit_symbol fundecl.fun_name},@function
`;
` .size {emit_symbol fundecl.fun_name},.-{emit_symbol fundecl.fun_name}
`
| _ -> ()
</pre>
<p>Except that the only data we have is the fundecl variable :</p>
<pre>
type fundecl =
{ fun_name: string;
fun_body: instruction;
fun_fast: bool }
type instruction =
{ mutable desc: instruction_desc;
mutable next: instruction;
arg: Reg.t array;
res: Reg.t array;
dbg: Debuginfo.t;
live: Reg.Set.t }
</pre>
<p>There is a dbg attribute on instructions but it's rarely set. (One
compilation with -dlinear option allow to see this fact)</p>
<p>I've decided to add a <em>fun_dbg : Debuginfo.t</em> attribute on
"fundecl" type and fill it in all compilation steps. It may more clever to work
on this (often-empty) "dbg" attribute ? (it would allow to add position
information on all instructions, it can be usefull for valgrind and gdb) This
patch is not optimised because it repeats .file instruction for each .loc and
so it repeats it on each function header.</p>
<p>-> <a href="/public/patch-file-and-loc-v1-cvs-2008-11-11.patch">Patch
based on release311 branch, but works on current trunk</a><br />
<br /></p>
<p>Now let's see what brings this patch</p>
<h2>gdb results</h2>
<pre>
$ ocamlopt -g -inline 0 t.ml
$ gdb a.out
(gdb) break t.ml:6
Breakpoint 1 at 0x8049940: file t.ml, line 6.
(gdb) run
Starting program: /home/alex/callgraph/a.out
<p> Breakpoint 1, camlT__foo_60 () at t.ml:6
6 let foo i =
Current language: auto; currently asm</p>
<p> (gdb) backtrace
#0 camlT__foo_60 () at t.ml:7
#1 0x0804c570 in camlT__entry () at t.ml:12
#2 0x0806e4b7 in caml_start_program ()</p>
<p> (gdb) step 1
camlT__bar_58 () at t.ml:2
2 let bar i =</p>
<p> (gdb) list
1
2 let bar i =
3 1+i
4 ;;
5
6 let foo i =
7 2+(bar i )
8 ;;
9
10 let () =
</pre></p>
<h2>gprof results</h2>
<pre>
$ ocamlopt -g -p -inline 0 t.ml
$ ./a.out
$ gprof -A
*** File /home/alex/callgraph/t.ml:
<pre><code> 1 -&gt; let bar i =
Thread.delay 3.0;
1+i
;;
1 -&gt; let foo i =
2+(bar i )
;;
let () =
1 -&gt; let closure() = 3 in
print_int ( foo (closure()) )
;;
</code></pre>
<p> Top 10 Lines:</p>
<pre><code> Line Count
2 1
7 1
12 1
</code></pre>
<p> Execution Summary:</p>
<pre><code> 3 Executable lines in this file
3 Lines executed
</code></pre>
<p> 100.00 Percent of the file executed</p>
<pre><code> 3 Total number of line executions
1.00 Average executions per line
</code></pre>
<h2 id="-valgrindkcachegrind-results-----ocamlopt--g--inline-0-tml----valgrind---toolcallgrind-aout----callgrind_annotate-callgrindout2152-tml"></pre>
<h2>valgrind/kcachegrind results</h2>
<pre>
$ ocamlopt -g -inline 0 t.ml
$ valgrind --tool=callgrind ./a.out
$ callgrind_annotate callgrind.out.2152 t.ml</h2>
<h2 id="---user-annotated-source-tml"> -- User-annotated source: t.ml</h2>
<p> .<br> 8 let bar i =
77,715 => thread.ml:camlThread__delay_75 (1x)
. Thread.delay 3.0;
. 1+i
. ;;
.<br> 3 let foo i =
77,723 => t.ml:camlT__bar_58 (1x)
. 2+(bar i )
. ;;
.<br> . let () =
13 let closure() = 3 in
1,692 => pervasives.ml:camlPervasives__output_string_215 (1x)
2,312 => pervasives.ml:camlPervasives__string_of_int_154 (1x)
77,726 => t.ml:camlT__foo_60 (1x)
. print_int ( foo (closure()) )
. ;;
.<br> $ kcachegrind callgrind.out.2152
</pre></p>
<p><img src="/public/./.kcachegrind-file-and-line_m.jpg" alt="kcachegrind-file-and-line.png" style="display:block; margin:0 auto;" title="kcachegrind-file-and-line.png, nov 2008" /></p>
<p><br />
<br /></p>
<h2>And next ?</h2>
<p>We first need to wait approval for this new feature by ocaml community, I've
submitted it <a href="http://caml.inria.fr/mantis/view.php?id=4888" hreflang="en">there</a>.<br />
I someone from INRIA read this ... Don't hesitate to contact me, I'm open to
work on a different approach.<br />
After that, we may hope a lot of new features, like :</p>
<ul>
<li>breakpoints on any caml line (not only function call)</li>
<li>gdb plugin allowing to read value in a breakpoint!</li>
</ul>
FoobarFox : JSCtypes putting Foobar into your Firefox!
2009-10-16T07:00:00.000Z
http://techno-barje.fr/post/2009/10/16/FoobarFox-JSCtypes-connecing-Foobar-into-Firefox!
<p style="text-align:center"><a href="/public/foofox_techno-barje.fr.xpi" style="font-size: 2em;">FoobarFox</a></p>
<strong>FoobarFox features</strong>
<ul>
<li>Retrieve currently playing song information into your Firefox</li>
<li>Automatically post to your twitter account all listening tracks</li>
<li>Search for information on wikipedia, myspace or google</li>
</ul>
<strong>FoobarFox prerequisites</strong>
<ul>
<li>You have to be on Windows ...</li>
<li>Need <a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-1.9.2/">Firefox
3.6b1pre nightly</a> or <a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/">Firefox
3.7a1pre nightly</a></li>
<li>Use <a href="http://www.foobar2000.org/">Foobar</a> music player</li>
</ul>
<p style="text-align:center">install <a href="/public/foofox_techno-barje.fr.xpi" style="font-size: 1em;">foofox@techno-barje.fr.xpi</a></p>
<img src="/public/foobarfox-1.png" alt="foobarfox" style="margin: 0 auto; display: block; border: 2px solid black; margin-bottom: 5px;" title="foobarfox" /> <img src="/public/foobarfox-2.png" alt="foobarfox" style="margin: 0 auto; display: block; border: 2px solid black;" title="foobarfox" /><br />
<br />
<br />
<h3>Real world JSCtypes usage</h3>
FoobarFox is not so usefull, it's mainly a proof of concept for JSCtypes
capabilities.<br />
Let's see how use JSCtypes to do fun things. You can copy paste this sample
code into your JS Console while your foobar is playing something :
<pre>
Components.utils.import("resource://gre/modules/ctypes.jsm");
<p>/* Change the dll path if your main windows directory is not on C:\WINDOWS! */
var lib = ctypes.open("C:\WINDOWS\system32\user32.dll");</p>
<p>/* Declare the signature of FindWindows function */
var findWindowEx = lib.declare("FindWindowW",
ctypes.stdcall_abi,
ctypes.int32_t,
ctypes.ustring,
ctypes.int32_t);</p>
<p>/* Search for Foobar windows by it's id <em>/
/</em> this ID is often changing of value at each release :/ */
var win = findWindowEx("{DA7CD0DE-1602-45e6-89A1-C2CA151E008E}/1", 0);
if (!win)
win = findWindowEx("{DA7CD0DE-1602-45e6-89A1-C2CA151E008E}", 0);
if (!win)
win = findWindowEx("{97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}", 0);
if (!win)
win = findWindowEx("{E7076D1C-A7BF-4f39-B771-BCBE88F2A2A8}", 0);</p>
<p>/* Define another signature of windows API function */
var getWindowText = lib.declare("GetWindowTextW",
ctypes.stdcall_abi,
ctypes.int32_t,
ctypes.int32_t,
ctypes.ustring,
ctypes.int32_t);</p>
<p>/* Fill the string buffer we give to JSCtypes call */
var text="";
var max_len = 100;
for(var i=0; i < max_len; i++)
text+=" ";
var text_len = getWindowText(win,text,100);</p>
<p>/* Extract song information from foobar window title <em>/
var m = text.match(/(.</em>) - (?:[([^#]+)([^]]+)] |)(.*) [foobar2000.*]/);</p>
<p>var musicinfo = {
artist : m[1],
album : m[2],
trackNumber : m[3],
track : m[4]
};
alert(musicinfo.toSource());
</pre></p>
<h3>JSCtypes current capabilities</h3>
<p>As I said in my <a href="/post/2009/10/12/JS-Ctypes">previous post</a>,
ctypes support in Firefox is limited and we can't use <i>struct</i>. So we're
able to play only with libraries that wait string and int. You can even pass
objects/structures, but only if they can be created by a function whose
arguments support the same limitation. As you can see in the previous example,
we're able to retrieve a pointer to a HWND object with FindWindowEx because it
only wait string arguments. After that we give this pointer to getWindowText as
a ctypes.int32_t.</p>
<h3>JSCtypes current limitations</h3>
Now, let's see a C++ code that can't be mapped into JSCtypes :
<pre>
// Add a notification to the tray.
NOTIFYICONDATA nid = {0};
<p>nid.cbSize = sizeof(nid);
nid.uID = 100;<br>nid.uFlags = NIF_ICON;
nid.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SAMPLEICON));</p>
<p>Shell_NotifyIcon(NIM_ADD, &nid);
</pre></p>
<p style="text-align: right">source: <a href="http://msdn.microsoft.com/en-us/library/aa453686.aspx">msdn</a></p>
In this case, we are neither able to create any NOTIFYICONDATA object, nor to
set attributes on this structure.<br />
<br />
<b>For more information</b>
<ul>
<li><a href="https://wiki.mozilla.org/Jsctypes/api">JSCtypes api</a>. But take
care, this is a work in progress!</li>
<li>Bug <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=513788">513788</a> - Revise
js-facing API for js-ctypes.</li>
<li>Bug <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=429551">429551</a> - add struct
support to js-ctypes.</li>
</ul>
JS Ctypes
2009-10-12T07:00:00.000Z
http://techno-barje.fr/post/2009/10/12/JS-Ctypes
<blockquote>Bug 518721 - Implement jsctypes with raw JSAPI<br />
Status: SOLVED FIXED!!!<br /></blockquote>
<p>That's an awesome news for mozilla's developpers!<br />
<a href="https://wiki.mozilla.org/JSctypes">JS Ctypes</a> aims to provide the
same library than <a href="http://docs.python.org/library/ctypes.html">Python
Ctypes</a> :</p>
<p>You can load any dynamic library (dll, so, dylib) and call
<b>C</b>-functions directly from your Javascript. In the current implementation
only simple types are supported : numbers, string, boolean. For now, we can't
play with pointers, nor structures, but these features are <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=513788">planned</a>.</p>
<br />
<h3>simpler code -> less work -> simpler review</h3>
<p>This new feature is going to greatly ease platform specific developpement
like windows management, system calls, ... For example we're able to call
Windows API directly from Javascript, without having to create, compile and
maintain any C++ XPCOM!<br />
One side effect is that it will ease code review for
https://addons.mozilla.org/ too! Instead of shipping an obscure dynamic library
with our extension, we may build only a JS-Ctypes wrapper and call directly OS
libraries or call a common library that can be validated by reviewers with some
MD5 checks.</p>
<br />
<h2>simpler code -> less knownledge -> better learning curve</h2>
<p>This is going to simplify the use of native code too! You can now build
native code without having to learn any mozilla "things" (XPCOM, specific build
layout/system, ...) You will just have to expose your library with a C api and
write a simple JS-CTypes wrapper.</p>
<br />
<h2>Hello World!</h2>
<ul>
<li>First retrieve <a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-1.9.2/">Firefox
3.6b1pre nightly</a> or <a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/">Firefox
3.7a1pre nightly</a></li>
<li>On windows, copy and paste this code in your JS console.<br />
This will display an OS native dialog.<br />
<i>(Change the dll path if your main windows directory is not on
C:\WINDOWS!)</i>
<pre>
/* Load JS Ctypes Javascript module */
Components.utils.import("resource://gre/modules/ctypes.jsm");
var Types = ctypes.types;
<p>/* Load windows api dll */
var lib = ctypes.open("C:\WINDOWS\system32\user32.dll");</p>
<p>/* Declare the signature of the function we are going to call */
var msgBox = lib.declare("MessageBoxW",
ctypes.stdcall_abi,
ctypes.int32_t,
ctypes.int32_t,
ctypes.ustring,
ctypes.ustring,
ctypes.int32_t);
var MB_OK = 3;</p>
<p>/* Do it! */
var ret = msgBox(0, "Hello world", "title", MB_OK);</p>
<p>/* Display the returned value */
alert("MessageBox result : "+ret);</p>
<p>lib.close();
</pre></li></p>
</ul>
<br />
How to setup a mozilla extension update server
2009-10-08T07:00:00.000Z
http://techno-barje.fr/post/2009/10/08/How-to-setup-a-mozilla-extension-update-server
<p>I've shared in the previous post a command line version of Mccoy. Here is a new
tutorial on how to use it!<br /></p>
<h3>Prerequisite</h3>
<ul>
<li>a HTTP server</li>
<li>Patched version of mccoy, with command line capabilities : <a href="/public/mccoy.tar.gz">mccoy.tar.gz</a></li>
<li>This start kit : <a href="/public/mccoy-test.tar.gz">mccoy-test.tar.gz</a>
which bundle a sample extension and one update.xml file</li>
</ul>
<pre>
$ cd /one/of/your/htdocs/dir
$ wget http://blog.techno-barje.fr/public/mccoy.tar.gz
$ tar zxvf mccoy.tar.gz
$ wget http://blog.techno-barje.fr/public/mccoy-test.tar.gz
$ tar zxvf mccoy-test.tar.gz
$ cd mccoy-test/
$ ls
update.xml workdir xpis
</pre>
<br />
<br />
<h3>Setup your XPI with valid update information</h3>
<strong>Create a new key in Mccoy</strong>
<pre>
mccoy-test $ cd workdir/
workdir $ ls
chrome chrome.manifest install.rdf
workdir $ ../../mccoy -createKey myextensionkey
Creating key with name : myextensionkey
Public key : MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbV+ZGXs658dOm/+4YtT+VzT5JWzMFYiQ8155fnMkOJCina2yDEBq8Lvi5qF5SyoMDkqaYeO51LR+B4p1g7oWmBW9HbOz3eA9lD/AHUR1SHiJAX7RQq8v9sPSkYta+LyVrCMFgpTmhOWPUXOnwalmL7syGkXyjxHqHCYz+s3d22QIDAQAB
The key has been successfully created!
</pre>
Remember the name of your key (if you forgot the name, you can later execute
<em>mccoy -listKeys</em>)<br />
<strong>Inject the public key in your extension</strong>
<pre>
workdir $ ./mccoy -installKey install.rdf -key myextensionkey
Public key inserted!
</pre>
This will set the <em>updateKey</em> attribute with the public key. (you can
later retrieve the public key with <em>mccoy -publicKey myextension</em>)<br />
<strong>Set the updateURL attribute of the install.rdf with the URL of the
update.xml file located in mccoy-test/update.xml</strong>
<pre>
workdir $ vi update.rdf
</pre>
<strong>Build the first xpi</strong>
<pre>
$ zip -r ../xpis/mccoy-test-0.1.xpi .
</pre>
<br />
<strong>»» now install this XPI!</strong> This sample extension will just
display an alert with message <em>"Mccoy 0.1!"</em><br />
<br />
<h3>Create a new version of your extension</h3>
<strong>Alter the sample extension alert message with something new</strong>
<pre>
workdir $ vi chrome/content/firefoxOverlay.xul
</pre>
<strong>Update the version number with 0.2</strong>
<pre>
workdir $ vi install.rdf
</pre>
<strong>Build the new xpi</strong>
<pre>
workdir $ zip -r ../xpis/mccoy-test-0.2.xpi .
</pre>
<strong>Update the update xml file</strong>
<pre>
workdir $ cd ..
mccoy-test $ vi update.xml
### change version with 0.2
### change updatelink with mccoy-test-0.2.xpi
### change updatehash with result of <em>sha1sum xpis/mccoy-test-0.2.xpi</em>
</pre>
<strong>Sign the update file with mccoy</strong>
<pre>
mccoy-test $ ../mccoy/mccoy -signRDF update.xml -key myextensionkey
Sign < update.xml > with key < myextensionkey >
Sign addon : urn:mozilla:extension:mccoy-test@techno-barje.fr
File signed!
</pre>
This will set the <em>signature</em> attribute with computed with your private
key.<br />
<br />
<strong>»» You can now force the update in your firefox, relaunch it and
voilà!<br />
<br /></strong>
<h3><strong>Some tips for debugging</strong></h3>
Enable this two about:config entries in order to get some message in JS console
about update process :<br />
<pre>
extensions.logging.enabled = true
javascript.options.showInConsole = true
</pre>
Mozilla Mccoy tool from the command line
2009-10-05T07:00:00.000Z
http://techno-barje.fr/post/2009/10/05/Mozilla-Mccoy-tool-from-the-command-line
<p>We have seen in the <a href="/post/2009/09/30/Headless-xulrunner" hreflang="en">previous post</a> how to build an headless xulrunner application.<br />
My first use case was enabling the Mozilla <a href="https://developer.mozilla.org/en/McCoy" hreflang="en">Mccoy</a> application to
launch from command line. That allows me to build a bash script which create
nightly builds of a firefox extension automatically updated by Firefox every
day!.<br />
<br />
Here is the resulting Mccoy version :</p>
<ul>
<li><strong>Linux i686 patched build of Mccoy:</strong> <a href="/public/mccoy.tar.gz">mccoy-0.5-command-line.tar.gz</a></li>
<li><strong>Patch based on 0.5 official release:</strong> <a href="/public/mccoy-0.5-command-line.patch">mccoy-0.5-command-line.patch</a></li>
</ul>
<br />
<strong>Now, a summary of each command line arguments :</strong>
<pre>
# Create a new named public/private key
$ ./mccoy -createKey mykey
Creating key with name : mykey
Public key : MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbV+ZGXs658dOm/+4YtT+VzT5JWzMFYiQ8155fnMkOJCina2yDEBq8Lvi5qF5SyoMDkqaYeO51LR+B4p1g7oWmBW9HbOz3eA9lD/AHUR1SHiJAX7RQq8v9sPSkYta+LyVrCMFgpTmhOWPUXOnwalmL7syGkXyjxHqHCYz+s3d22QIDAQAB
The key has been successfully created!
<h1 id="list-all-keys-in-the-current-mccoy-profile">List all keys in the current mccoy profile</h1>
<h1 id="-dont-forget-to-save-your-profile">/!\ Don't forget to save your profile!</h1>
<p>$ ./mccoy -listKeys
Registered keys :</p>
<ul>
<li>mykey</li>
</ul>
<h1 id="inject-the-public-key-in-your-extensions-installrdf">Inject the public key in your extension's install.rdf</h1>
<p>$ ./mccoy -installRDF myextension_workdir/install.rdf -key myextensionkey
Public key inserted!</p>
<h1 id="update-the-signature-of-the-update-xml-file">Update the signature of the update xml file</h1>
<p>$ ./mccoy -signRDF update.xml -key mykey
Sign < update.xml > with key < mykey >
Sign addon : urn:mozilla:extension:<a href="mailto:mccoy@techno-barje.fr">mccoy@techno-barje.fr</a>
File signed!</p>
<h1 id="check-if-the-update-xml-file-is-correctly-signed">Check if the update xml file is correctly signed</h1>
<p>$ ./mccoy -verifyRDF update.xml -key myextensionkey
Check rdf : update.xml with key myextensionkey
Valid signature!</p>
<p></pre></p>
Headless xulrunner
2009-09-30T07:00:00.000Z
http://techno-barje.fr/post/2009/09/30/Headless-xulrunner
<p>Here is a summary on how to run a xulrunner application on an headless
computer, or more commonly just launch xulrunner in a command line with no
windows.<br />
<br />
By default, xulrunner try to open a XUL window defined in the
""toolkit.defaultChromeURI"" preference, so you have to set this one to an
empty value.<br />
<br />
Then you need to have a running X server, even if it never open any window ...
one simple and lightweight solution is running Xvfb. But any other X server
will do the work!<br />
<br />
Finally, I suggest you to take the <a hreflang="en" href="http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/1.9.0.14/runtimes/">
1.9.0.14 xulrunner release</a> which has less painfull dependencies like
libalsa (due to ogg support) and libdbus-glib.<br />
<br />
This will avoid this kind of errors :<br /></p>
<blockquote>
<p>./xulrunner-bin: error while loading shared libraries: libasound.so.2:
cannot open shared object file: No such file or directory<br />
./xulrunner-bin: error while loading shared libraries: libdbus-glib-1.so.2:
cannot open shared object file: No such file or directory</p>
</blockquote>
How to write such a tutorial without a complete working out of the box hello
world ?<br />
Here is a sample command line application with linux xulrunner binaries :
<a href="/public/headless-runner.tar.gz">headless-runner.tar.gz</a><br />
<pre>
$ tar zxvf headless-runner.tar.gz
$ cd headless-runner
$ Xvfb :2
$ export DISPLAY=:2
$ ./headless -ls -filesize application.ini
LS :
- application.ini
- a.out
- tests
- components
- defaults
- updates
- extensions
- xulrunner-1.9.2a2pre.en-US.linux-x86_64.tar.bz2
- xulrunner
- headless
<p>$ ./headless -filesize application.ini
File size of : application.ini
243
</pre>
The main code is in the file components/nsCommandLineHandler.js</p>
<pre>
CommandLineHandler.prototype.handle = function(aCmdLine){
var toggle = aCmdLine.handleFlag("ls", false);
if (toggle) {
dump("LS :
");
var list = aCmdLine.workingDirectory.directoryEntries;
while(list.hasMoreElements()) {
var file = list.getNext().QueryInterface(Components.interfaces.nsIFile);
dump(" - "+file.leafName+"
");
}
dump("
");
}
var filesize = aCmdLine.handleFlagWithParam("filesize", false);
if (filesize) {
dump("File size of : "+filesize+"
");
var file = aCmdLine.resolveFile(filesize);
if (!file)
return dump("Unable to find this file
");
dump(" "+file.fileSize+"
");
}
}
</pre>
<p>For more information, check the <a hreflang="en" href="http://mxr.mozilla.org/mozilla-central/source/toolkit/components/commandlines/public/nsICommandLine.idl">
nsICommandLine</a> interface of the aCmdLine object.<br />
<br />
Last but not least, why do I try to use Xulrunner in command line whereas
<a href="https://developer.mozilla.org/en/xpcshell">xpcshell</a> and "<a href="https://developer.mozilla.org/En/SpiderMonkey/Introduction_to_the_JavaScript_shell">js</a>"
commands exists?!</p>
<ul>
<li><strong>First:</strong> Some tools like <a href="https://developer.mozilla.org/en/McCoy">Mccoy</a> are bundled as xulrunner
application. And you may launch these tools on headless servers in order to
build, for example, continuous integration tools!</li>
<li><strong>Second:</strong> JS shell allow you tu use only pure Javascript;
Xpcshell expose all XPCOM but some part of Mozilla environnement are disabled!
I was unabled to create a <canvas> element in xpcshell. There is no way
to create a XUL/HTML document/element with XPCOM and hiddenWindow is disabled
... So the only solution to build a tool which takes website screenshots with
<canvas> was using xulrunner!</li>
</ul>
XUL Profiler
2008-12-11T08:00:00.000Z
http://techno-barje.fr/post/2008/12/11/XUL-Profiler
<p><img src="/public/addon_thumb.png" alt="addon_thumb.png" style="margin: 0 0 1em 1em; float: right;" title="addon_thumb.png, dec 2008" /> XUL
Profiler ? Qu'est-ce donc ? Et bien c'est l'extension Firefox que
j'ai pu développer chez <a href="http://www.yoono.com">Yoono</a>. Elle a pour
but de donner des pistes aux développeurs XUL (mais aussi aux développeurs Web)
pour optimiser les performances de leurs applications.</p>
<p>Pour l'instant, cette extension permet de récolter deux
informations :</p>
<ul>
<li>un callgraph Javascript : Chaque appel de fonction est consigné dans
un arbre et classé par son temps d'exécution. On peut ainsi rapidement repérer
les fonctions qui ralentissent le navigateur.</li>
<li>une vidéo des rafraichissements de Firefox : Toutes les opérations de
mise à jour graphique de firefox sont enregistrées dans une vidéo qui nous
permet d'apprendre à optimiser notre Javascript ainsi que les CSS afin de
soulager Firefox dans son travail de layout.</li>
</ul>
<p>L'extension est disponible sur <a href="https://addons.mozilla.org/fr/firefox/addon/9954">Mozilla addons</a></p>
<p>Voici quelques résultats sur des exemples simples.</p>
<h2>Callgraph Javascript</h2>
<p>(<a href="/public/test-xulprofiler-callgraph.html">Fichier html de
test</a>)</p>
<pre>
function fun_root() {
fun_A();
fun_B();
fun_C();
}
function fun_A() {
dump("fun A");
}
function fun_B() {
dump("fun B");
var s="";
fun_D();
for(var i=0; i<1000; i++) {
s+="CPU INTENSIVE FUNCTION";
fun_D();
}
fun_D();
}
function fun_C() {
dump("fun C");
}
function fun_D() {
dump("fun D");
}
</pre>
<p><img src="/public/test-xulprofiler-callgraph.png" alt="test-xulprofiler-callgraph.png" style="margin: 0 auto; display: block;" title="test-xulprofiler-callgraph.png, dec 2008" /></p>
<p>On voit ici la hiérarchie des appels entre les fonction grâce à la
présentation sous forme d'arbre, et l'on peut conclure que la majorité du temps
de calcul de ce script est effectué dans la fonction "fun_B".</p>
<h2>Paint events</h2>
<p>(<a href="/public/test-xulprofiler-paint.html">Fichier html de test</a>)</p>
<pre>
function delayEachInserts() {
for(var i=0;i<20;i++) {
window.setTimeout(insertItem,100*i,i);
}
}
function insertItem(i) {
var container=document.getElementById("container");
var item=document.createElement("div");
item.setAttribute("class","item");
item.textContent="Item "+i;
container.appendChild(item);
}
window.addEventListener("load",delayEachInserts,false);
</pre>
<p>  <strong>=></strong> <a style="font-weight: bold;" href="/public/test-xulprofiler-paint-result.html">Résutat</a></p>
<p>Cet exemple montre que lorsqu'on ajoute un élement DOM, Firefox est obligé
de rafraichir son conteneur à chaque ajout.</p>
Ocaml native code debugging
2008-11-09T08:00:00.000Z
http://techno-barje.fr/post/2008/11/09/Ocaml-native-code-debugging
<p><img src="/public/./.t-caml-valid-callgraph_s.jpg" alt="t-caml-valid-callgraph.png" style="float:right; margin: 0 0 1em 1em;" title="t-caml-valid-callgraph.png, juin 2008" /> Maintenant que le bug <a href="http://caml.inria.fr/mantis/bug_view_page.php?bug_id=4642" hreflang="en">Improve gnu ELF</a> est corrigé, kcachegrind nous génère de beaux
callgraphs.</p>
<p>Ce patch consistait à ajouter des instructions .size (dans l'assembleur ELF)
pour que valgrind interprète tous les symboles (camT_entry, camlT_foo,
camlT_bar, ...) et puisse ainsi afficher leurs noms au lieu d'un nombre
hexadécimal!</p>
<h2>Les standards ELF pour le debug</h2>
<p>Maintenant, nous souhaiterions que ces outils puissent savoir de manière
standard dans quels fichiers et à quelles lignes sont déclarés les fonctions.
(Le nom du fichier est bien présent dans les symboles, mais cela ne permet pas
d'exploiter le plein potentiel de ces outils)</p>
<p>Voyons un peu comment se débrouille GCC :</p>
<pre>
int bar(int a) {
return 1+a;
}
<p> int foo(int a) {
return 2+bar(a);
}</p>
<p> int main() {
foo(3);
}
</pre></p>
<pre>
$ gcc -O0 -g -S t.c
</pre>
<pre>
.globl bar
.type bar, @function
bar:
.LFB2:
.file 1 "t.c"
.loc 1 1 0
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl %edi, -4(%rbp)
.loc 1 2 0
movl -4(%rbp), %eax
incl %eax
.loc 1 3 0
leave
ret
.LFE2:
.size bar, .-bar
.globl foo
.type foo, @function
</pre>
<p>Les instructions clés sont .file et .loc :</p>
<ul>
<li>.file déclare un chemin vers un fichier et l'associe à un
identifiant<br /></li>
</ul>
<p>-> .file $identifiant$ "$chemin_du_fichier"</p>
<ul>
<li>.loc associe un fichier, un numéro de ligne et de colonne aux instructions
la succédant<br /></li>
</ul>
<p>-> .loc $identifiant_de_fichier$ $ligne$ $colonneENTRIESlt;/p>
<h2>Solution proposée</h2>
<p>Le module du compilateur qui génère les instructions est : <a href="http://camlcvs.inria.fr/cgi-bin/cvsweb/ocaml/asmcomp/i386/emit.mlp?rev=1.41.2.2;only_with_tag=release311" hreflang="en">asmcomp/i386/emit.mlp</a><br />
Et plus particulièrement la fonction <em>fundecl</em> :</p>
<pre>
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
(* ... *)
` .globl {emit_symbol fundecl.fun_name}
`;
`{emit_symbol fundecl.fun_name}:
`;
if !Clflags.gprofile then emit_profile();
let n = frame_size() - 4 in
if n > 0 then
` subl ${emit_int n}, %esp
`;
`{emit_label !tailrec_entry_point}:
`;
emit_all true fundecl.fun_body;
List.iter emit_call_gc !call_gc_sites;
emit_call_bound_errors ();
List.iter emit_float_constant !float_constants;
match Config.system with
"linux_elf" | "bsd_elf" | "gnu" ->
` .type {emit_symbol fundecl.fun_name},@function
`;
` .size {emit_symbol fundecl.fun_name},.-{emit_symbol fundecl.fun_name}
`
| _ -> ()
</pre>
<p>Hors nous n'avons à disposition que la variable fundecl :</p>
<pre>
type fundecl =
{ fun_name: string;
fun_body: instruction;
fun_fast: bool }
type instruction =
{ mutable desc: instruction_desc;
mutable next: instruction;
arg: Reg.t array;
res: Reg.t array;
dbg: Debuginfo.t;
live: Reg.Set.t }
</pre>
<p>Il y a bien un attribut dbg sur les instructions mais il est rarement
renseigné. (une compilation avec l'option -dlinear permet de le voir)</p>
<p>J'ai décidé d'ajouter un attribut <em>fun_dbg : Debuginfo.t</em> sur
<em>fundecl</em> et de le remplir dans toutes les phases de compilation. Il
serait peut être plus judicieux de travailler sur l'attribut <em>dbg</em> des
instructions ? (car cela permettrait par la suite d'indiquer les lignes de
chaque instruction à valgrind, mais aussi gdb!) Ce patch n'est pas optimisé car
il répète l'instruction .file pour chaque .loc, donc à chaque entête de
fonction, nous avons un .file et un .loc.</p>
<p>-> <a href="/public/patch-file-and-loc-v1-cvs-2008-11-11.patch">Patch sur
la branche release311</a><br />
<br /></p>
<p>Voyons maintenant ce qu'apporte ce patch.</p>
<h2>Résultats avec gdb</h2>
<pre>
$ ocamlopt -g -inline 0 t.ml
$ gdb a.out
(gdb) break t.ml:6
Breakpoint 1 at 0x8049940: file t.ml, line 6.
(gdb) run
Starting program: /home/alex/callgraph/a.out
<p> Breakpoint 1, camlT__foo_60 () at t.ml:6
6 let foo i =
Current language: auto; currently asm</p>
<p> (gdb) backtrace
#0 camlT__foo_60 () at t.ml:7
#1 0x0804c570 in camlT__entry () at t.ml:12
#2 0x0806e4b7 in caml_start_program ()</p>
<p> (gdb) step 1
camlT__bar_58 () at t.ml:2
2 let bar i =</p>
<p> (gdb) list
1
2 let bar i =
3 1+i
4 ;;
5
6 let foo i =
7 2+(bar i )
8 ;;
9
10 let () =
</pre></p>
<h2>Résultats avec gprof</h2>
<pre>
$ ocamlopt -g -p -inline 0 t.ml
$ ./a.out
$ gprof -A
*** File /home/alex/callgraph/t.ml:
<pre><code> 1 -&gt; let bar i =
Thread.delay 3.0;
1+i
;;
1 -&gt; let foo i =
2+(bar i )
;;
let () =
1 -&gt; let closure() = 3 in
print_int ( foo (closure()) )
;;
</code></pre>
<p> Top 10 Lines:</p>
<pre><code> Line Count
2 1
7 1
12 1
</code></pre>
<p> Execution Summary:</p>
<pre><code> 3 Executable lines in this file
3 Lines executed
</code></pre>
<p> 100.00 Percent of the file executed</p>
<pre><code> 3 Total number of line executions
1.00 Average executions per line
</code></pre>
<h2 id="-résultats-avec-valgrindkcachegrind-----ocamlopt--g--inline-0-tml----valgrind---toolcallgrind-aout----callgrind_annotate-callgrindout2152-tml"></pre>
<h2>Résultats avec valgrind/kcachegrind</h2>
<pre>
$ ocamlopt -g -inline 0 t.ml
$ valgrind --tool=callgrind ./a.out
$ callgrind_annotate callgrind.out.2152 t.ml</h2>
<h2 id="---user-annotated-source-tml"> -- User-annotated source: t.ml</h2>
<p> .<br> 8 let bar i =
77,715 => thread.ml:camlThread__delay_75 (1x)
. Thread.delay 3.0;
. 1+i
. ;;
.<br> 3 let foo i =
77,723 => t.ml:camlT__bar_58 (1x)
. 2+(bar i )
. ;;
.<br> . let () =
13 let closure() = 3 in
1,692 => pervasives.ml:camlPervasives__output_string_215 (1x)
2,312 => pervasives.ml:camlPervasives__string_of_int_154 (1x)
77,726 => t.ml:camlT__foo_60 (1x)
. print_int ( foo (closure()) )
. ;;
.<br> $ kcachegrind callgrind.out.2152
</pre></p>
<p><img src="/public/./.kcachegrind-file-and-line_m.jpg" alt="kcachegrind-file-and-line.png" style="display:block; margin:0 auto;" title="kcachegrind-file-and-line.png, nov 2008" /></p>
<p><br />
<br /></p>
<h2>Et après ?</h2>
<p>On peut espérer encore un tas d'améliorations, comme :</p>
<ul>
<li>des breakpoints sur n'importe quelle ligne de caml ...</li>
<li>un pluging gdb pour pouvoir lire des valeurs pendant un break!</li>
</ul>
Functional Programming and Finance
2008-11-07T08:00:00.000Z
http://techno-barje.fr/post/2008/11/07/Functional-Programming-and-Finance
<p>Qui l'eut cru ? Le petit Eddy, vous savez celui au fond de la classe
qui adore les proba et supporte tout juste les délires de geek; et bien il est
tombé lui aussi dans la spirale des langages modernes!</p>
<p>Il vient même de lancer tout seul un blog sur la promotion du F# dans la
finance :<br />
<a href="http://it-quant.blogspot.com/" hreflang="en">IT quant</a><br />
Et oui, ils sont encore dans les solutions propriétaires en C ... ++ !!!<br />
Enfin, je le promets, je n'y suis pour pas grand chose pour son intérêt au
F#<br /></p>
ocaml callgraph
2008-06-20T07:00:00.000Z
http://techno-barje.fr/post/2008/06/20/ocaml-callgraph
<p><a href="http://kcachegrind.sourceforge.net/" hreflang="en">KCacheGrind</a> : un outil simple mais diablement efficace pour
analyser les callgraph de programmes en C (ou python, php, perl, ...) afin de
faire du débogage ou encore de l'analyse de performance.</p>
<h3>Situation actuelle avec un programme caml simple</h3>
<p><strong>Exemple utilisé :</strong></p>
<pre>
let bar i =
1+i
;;
<p>let foo i =
2+(bar i )
;;</p>
<p>let () =
print_int ( foo 3 )
;;
</pre></p>
<ul>
<li>D'abord nous compilons en désactivant les optimisations qui pourraient
éliminer ces fonctions triviales</li>
</ul>
<pre>
$ ocamlopt -inline 0 -o foo t.ml
</pre>
<ul>
<li>Puis nous exécutons ce test via <a href="http://valgrind.org/" hreflang="en">valgrind</a> qui génère un fichier au format "callgrind", contenant
l'ordre d'appel des fonctions ainsi que les estimations du temps passé dans
chacune d'entre elles :</li>
</ul>
<pre>
$ valgrind --tool=callgrind ./foo
</pre>
<ul>
<li>Enfin, on regarde le résultat sous kcachegrind</li>
</ul>
<pre>
$ kcachegrind callgrind.out.10624
</pre>
<p><img src="/public/t-caml-without-patch-callgr.png" alt="t-caml-without-patch-callgr.png" style="display:block; margin:0 auto;" /></p>
<p>Mais voilà, l'assembleur généré par le compilateur caml ne contient pas
toutes les instructions utilisées par valgrind lors de l'analyse du programme.
Ainsi, aucun label de fonction n'apparait et nous n'avons le droit qu'à des
adresses mémoire en hexadécimal :(</p>
<p><strong>/!\ Problème corrigé pour la version 3.11 : <a href="http://caml.inria.fr/mantis/bug_view_page.php?bug_id=4642" hreflang="en">détails</a></strong></p>
<h3>Exemple du fonctionnement normal en C</h3>
<p>Voyons comment valgrind fonctionne avec du C :</p>
<pre>
int bar(int a) {
return 1+a;
}
<p>int foo(int a) {
return 2+bar(a);
}</p>
<p>int main() {
foo(3);
}
</pre></p>
<pre>
$ gcc -O0 -o foo t.c
$ valgrind --tool=callgrind ./foo
$ kcachegrind callgrind.out.10719
</pre>
<p><img src="/public/t-c-callgraph.png" alt="t-c-callgraph.png" style="display:block; margin:0 auto;" /></p>
<p>Cette fois-ci, nous obtenons un graphe correct avec le nom des fonctions;
Regardons maintenant l'assembleur généré par gcc</p>
<pre>
$gcc -O0 -S t.c
</pre>
<p><strong>Assembleur de GCC :</strong></p>
<pre>
.file "t.c"
.text
.globl bar
.type bar, @function
bar:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
incl %eax
popl %ebp
ret
.size bar, .-bar
.globl foo
.type foo, @function
foo:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call bar
addl $2, %eax
leave
ret
.size foo, .-foo
</pre>
<p>Comparons maintenant celui-ci à l'assembleur généré par le compilateur
ocaml : <strong>Assembleur OCaml</strong></p>
<pre>
.text
.align 16
.globl camlT__bar_58
.type camlT__bar_58,@function
camlT__bar_58:
.L100:
addl $2, %eax
ret
.text
.align 16
.globl camlT__foo_60
.type camlT__foo_60,@function
camlT__foo_60:
.L101:
call camlT__bar_58
.L102:
addl $4, %eax
ret
</pre>
<p>Dans les deux cas, nous retrouvons bien nos deux fonctions foo et bar avec
les instructions : .globl, .type mais il manque .size à la fin des
fonctions caml! Ceci est la source du problème pour valgrind, car après analyse
de son code source, il ignore les fonctions de taille nulle ...</p>
<h3>Solution</h3>
<p>Il suffit d'appliquer ce minuscule patch sur le compilateur ocaml pour
générer des exécutables ELF valides aux yeux de valgrind : <a href="/public/patch-alter_elf_for_valgrind-cvs-080620.patch">patch-alter_elf_for_valgrind-cvs-080620</a><br />
<p>(Patch réalisé sur la version CVS, qui correspond à la futur version 3.11)</p></p>
<p>Nous obtenons alors le callgraph suivant sur le premier exemple :
<img src="/public/t-caml-valid-callgraph.png" alt="t-caml-valid-callgraph.png" style="display:block; margin:0 auto;" /></p>
<h3>Exemple sur un vrai projet utilisant ocamlnet :</h3>
<p><a href="/public/callgraph-with-patch2.png"><img src="/public/./.callgraph-with-patch2_m.jpg" alt="callgraph-with-patch2.png" style="display:block; margin:0 auto;" /></a></p>
OCaml, embarqué et flash
2008-03-04T08:00:00.000Z
http://techno-barje.fr/post/2008/03/04/OCaml-embarque-et-flash
<p>Qu'on le veuille ou non, OCaml est utilisable dans l'embarqué!</p>
<h2>Chumby</h2>
<p>Cette sorte de nabaztag en forme de radio reveil propose de développer des
interfaces/widgets en flash, à l'aide du compilateur ActionScript de Motion
twin : <a href="http://www.mtasc.org/" hreflang="en">MTASC</a><br />
Donc l'équipe a intégré dans son sdk le compilateur MTASC et ... le compilateur
OCaml puisqu'il est lui même écrit en caml!<br />
<a href="http://www.chumby.com/" hreflang="fr">Chumby</a><br />
<a href="http://forum.chumby.com/viewtopic.php?pid=9801#9801" hreflang="fr">Annonce sur le forum chumby</a></p>
<h2>Ming</h2>
<p>Tant que nous parlons de flash, il faut noter l'existence d'un binding pour
la librairie ming pour générer des animations flash :<br />
<a href="http://www.linux-nantes.org/~fmonnier/OCaml/ming/" hreflang="en">Binding ming</a><br />
<a href="http://www.linux-nantes.org/~fmonnier/OCaml/ming/set_line.ml.php" hreflang="en">Exemple d'animation</a></p>
<h2>IPhone</h2>
<p>L'interpréteur caml est aussi cross compilable pour l'iphone.<br />
<a href="http://web.yl.is.s.u-tokyo.ac.jp/~tosh/ocaml-on-iphone/" hreflang="en">Patch et guide tutorial de compilation</a></p>
Un noyau en OCaml
2008-02-26T08:00:00.000Z
http://techno-barje.fr/post/2008/02/26/Un-noyau-en-OCaml
<p>Cette idée doit souvent revenir dans l'esprit des développeurs Haskell ou
OCaml : Pourquoi n'entendons-nous pas parler d'expériences de noyaux écris
avec un langage fonctionnel ?</p>
<p>En effet, le fait de ne jamais avoir de segfault ou de null pointer
exception est tout de même un sacré avantage dans un noyau!!! Alors oui, il y a
à coup sûr d'énormes problèmes comme le garbage collector, on perdrait surement
en contrôle et en efficacité. Mais je reste convaincu que des fonctions en
fonctionnel pur (donc sans effets de bords) seraient une sacrée avancée dans le
développement kernel où l'on manipule plusieurs threads et plusieurs coeurs
d'exécution en même temps!</p>
<p>Et bien après une longue recherche, on peut trouver deux projets en
OCaml :</p>
<ul>
<li><a href="http://mynos.sourceforge.net/" hreflang="en">MyNOS</a></li>
<li><a href="http://home.gna.org/funk/" hreflang="en">Funk</a></li>
</ul>
<p>Malheureusement, ces projets n'ont plus d'activité notable depuis 2005
:(</p>
dynamique vs statique
2008-02-21T08:00:00.000Z
http://techno-barje.fr/post/2008/02/21/dynamique-vs-statique
<p>Les langages dynamiques comme Python, PHP, Ruby ou encore Javascript ont le
vent en poupe, car ils apportent un net gain en <em>productivité
<del>éphémère</del> instantanée</em>.<br />
Lorsque l'on développe dans ces langages, on boucle sur des cycles :
<strong>coder puis regarder</strong> :</p>
<ul>
<li>on modifie le code source du programme, et,</li>
<li>on va directement voir le résultat en exécutant l'application ...</li>
</ul>
<p>Dans le cas des langages à typage statique, nous sommes obligés de rajouter
une étape intermédiaire : <strong>coder, compiler et éventuellement
regarder</strong> :</p>
<ul>
<li>on commence toujours pas modifier notre code source, puis,</li>
<li>nous sommes obligés de cliquer sur un bouton ou de lancer un make pour la
compilation, et enfin,</li>
<li>suivant le résultat de la compilation, soit on se lance dans un nouveau
cycle en cas d'erreur, ou alors, on exécute l'application pour valider des
besoins fonctionnels ou visuels.<br /></li>
</ul>
<p>Donc oui, un programme développé à l'aide d'un langage statique prendra plus
de temps car l'étape de compilation nous oblige à supprimer toute erreur de
typage, de syntaxe et souvent bon nombre d'erreurs d'inattention. De plus cette
étape de compilation ne supprime pas l'étape "lancer le programme", que nous
devons toujours effectuer afin de valider les specs fonctionnelles ou
graphiques.<br /></p>
<p>Mais ce temps passé à faire valider notre code par le compilateur n'est pas
sans bénéfices, puisque notre programme sera plus fiable et n'émettra aucune
erreur purement informatique : typage, syntaxe, ...<br />
Ainsi, <strong>les erreurs sont présentées au développeur et en aucun cas à
l'utilisateur</strong> qui ne devrait en aucun cas avoir à gérer, ni
comprendre, ni même voir une telle erreur! <sup>[<a href="#pnote-212155-1" id="rev-pnote-212155-1" name="rev-pnote-212155-1">1</a>]</sup></p>
<h3>Tests unitaires</h3>
<p>Oui mais certains diront : on peut développer en mode <em><a href="http://fr.wikipedia.org/wiki/Extreme_programming" hreflang="fr">Extreme
programming</a></em> ou encore <em>Programmation agile</em> en agrémentant une
base de tests unitaires tout au long du développement. Cela se tient si l'on
fait des tests unitaires doublés de <a href="http://fr.wikipedia.org/wiki/Couverture_de_code" hreflang="fr">couverture de
code</a> maintenue à 100% afin de s'assurer d'exécuter l'intégralité du code
source par les tests et ainsi éviter l'affichage de moultes erreurs aux yeux
ébahis de nos utilisateurs.<br />
Mais cela engendre un volume de travail supplémentaire loin d'être négligeable!
On peut même se demander si les tests ne font pas que reproduire l'action d'un
compilateur analysant notre programme ?<br />
Pour rester objectif, je dirais que de tels tests vont plus loin et permettent
par exemple de valider des specs fonctionelles et nous obtenons au final un
logiciel modulaire, <ins>spécifié</ins> et solide.<sup>[<a href="#pnote-212155-2" id="rev-pnote-212155-2" name="rev-pnote-212155-2">2</a>]</sup>'<br />
Mais je reste perplexe quand au temps de développement nécessaire si l'on
compare à n'importe quel langage statique agrémenté de quelques tests
unitaires/fonctionnels (naturellement plus succincts).<br />
D'autre part, il ne faut pas oublier que la <a href="http://fr.wikipedia.org/wiki/Loi_de_Pareto" hreflang="fr">règle des 80/20</a>
s'applique à tout, y compris à l'informatique et à notre sujet de
discussion : on peut souvent réaliser une application fonctionnelle à 80%
avec 20% du temps nécessaire pour faire l'application finale : fiable et
maintenable par d'autres développeurs.</p>
<h3>Refactoring et évolution <sup>[<a href="#pnote-212155-3" id="rev-pnote-212155-3" name="rev-pnote-212155-3">3</a>]</sup></h3>
<p>Enfin, je tiens à mettre en avant un problème sérieux lorsque l'on doit
faire évoluer une application écrite à l'aide d'un langage dynamique. En effet,
dans ce cas de figure, nous devons à coup sûr modifier des structures de
données : faire évoluer un attribut entier vers un objet plus complexe, ou
encore modifier le nom d'une fonction ou ses paramètres, ou pire encore :
changer le type de sortie d'une fonction! Et à moins d'utiliser des outils
avancés comme IntelliJ ou Eclipse, <strong>les étapes de refactoring(réécriture
de code) seront laborieuses, incomplètes et vont décupler le nombre d'erreur
signalées à l'exécution</strong> ...<br />
Enfin, il faut rester conscient que ces outils ne pourront jamais rejoindre le
niveau de vérification effectué par le compilateur d'un langage typé
statiquement, et ce, à cause de la nature dynamique du langage.</p>
<div class="footnotes">
<h4>Notes</h4>
<p>[<a href="#rev-pnote-212155-1" id="pnote-212155-1" name="pnote-212155-1">1</a>] <a href="http://pinderkent.blogsavy.com/archives/157" hreflang="en">Billet sur les erreurs à l'exécution</a></p>
<p>[<a href="#rev-pnote-212155-2" id="pnote-212155-2" name="pnote-212155-2">2</a>] les tests unitaires sont une bonne chose et je
reviendrais la dessus</p>
<p>[<a href="#rev-pnote-212155-3" id="pnote-212155-3" name="pnote-212155-3">3</a>] <a href="http://blogs.tedneward.com/2008/01/24/Can+Dynamic+Languages+Scale.aspx" hreflang="en">Billet sur l'évolutivité du dynamique</a></p>
</div>
La 3D et le fonctionnel
2008-01-29T08:00:00.000Z
http://techno-barje.fr/post/2008/01/29/La-3D-et-le-fonctionnel
<h3><a href="http://en.wikipedia.org/wiki/Tim_Sweeney_(game_developer)" hreflang="en">Tim sweeney</a> - <a href="/tag/unreal">unreal</a> engine -
pourquoi utiliser le fonctionnel ?</h3>
<p>Ce cher monsieur intitule sa présentation : <a href="http://www.st.cs.uni-sb.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf?reredd" hreflang="en">The Next Mainstream Programming Language</a>, en citant à maintes
reprises Haskell comme exemple à reproduire pour son langage spécialisé dans le
développement de moteur 3D qui sera à même d'exploiter les multiples coeurs des
processeurs graphiques. On pourra rapidement faire le lien avec le <a href="http://blog.techno-barje.fr/post/2008/01/07/Le-fonctionnel-arrive" hreflang="fr">billet précédent</a> et intel ... les raisons d'utiliser le fonctionnel
pour la 3D et les processeurs mutli-core sont les mêmes.<br /></p>
<h3><a href="/tag/Ocaml">Ocaml</a> + <a href="/tag/SDL">SDL</a> + <a href="/tag/OpenGL">OpenGL</a></h3>
<p>C'est possible, et cela a l'air de marcher plutôt bien : <a href="http://itmmetelko.com/blog/2008/01/07/compiling-ocaml-sdl-and-opengl-on-windows-mini-tutorial/" hreflang="en">Compiler le triplet sous windows</a> <a href="http://itmmetelko.com/blog/2008/01/13/ocaml-mini-sdl-example-2/" hreflang="en">Exemple 1</a> <a href="http://itmmetelko.com/blog/2008/01/24/ocaml-mini-sdl-example-v3/" hreflang="en">Exemple 2</a><br /></p>
<h3>Des jeux 3D en OCaml ?</h3>
<p>Oui, cela existe aussi : <a href="http://freetennis.sourceforge.net/" hreflang="en">Freetennis</a>. Il y'a même des étudiants qui ont réalisé un jeu
pour enfants : <a href="http://www.nongnu.org/rigobot/">Rigobot</a> où ils
pourront programmer des robots dans un univers en 3D!</p>
Mais pourquoi ocaml ?
2008-01-14T08:00:00.000Z
http://techno-barje.fr/post/2008/01/14/Pourquoi-ocaml
<h2>Ses performances</h2>
<p>C'est un des langages les plus performant avec un garbage collector :
<a href="http://shootout.alioth.debian.org/gp4sandbox/benchmark.php?test=all&lang=all&calc=Calculate&xfullcpu=1&xmem=1&xloc=0&binarytrees=1&chameneosredux=0&fannkuch=1&fasta=1&knucleotide=1&mandelbrot=1&meteor=0&nbody=0&nsieve=1&nsievebits=1&partialsums=1&pidigits=0&recursive=1&regexdna=1&revcomp=1&spectralnorm=1&hello=0&sumcol=1&threadring=0">
Benchmark avec comme critère : vitesse et consommation mémoire</a><br />
Au final OCaml consomme peu de mémoire tout en restant dans les plus rapides et
tout cela sans demander au développeur de gérer la mémoire!</p>
<h2>L'environnement par défaut, ses outils :</h2>
<p>Mine de rien, lorsque vous télécharger les sources
<em>ocaml-3.10.1.tar.bz2</em> de 2.2 Mo, vous aller avoir accès à un nombre
important d'outils avancés :</p>
<ul>
<li>un compilateur en <strong>code natif</strong> (x86, amd64, power pc, alpha,
mips, sparc, ...),</li>
<li>un compilateur qui génère du <strong>bytecode</strong> caml. Le programme
pourra intégrer l'interpréteur de bytecode afin d'être exécutable n'importe où.
L'application peut aussi intégrer un toplevel afin de pouvoir exécuter des
commandes <strong>dans</strong> le programme.(une sorte de shell),</li>
<li>Un <strong>débugueur pas à pas</strong> permettant de revenir en arrière!
(uniquement en bytecode),</li>
<li>Un toplevel indépendant permettant d'apprendre facilement le langage en
exécutant des instruction caml comme dans une ligne de commande,</li>
<li>camlp4, un preprocesseur permettant d'étendre le langage en fonction du
domaine de notre programme,</li>
<li>des générateurs de parser lex/yacc,</li>
<li>un outil de création automatique de documentation à partir de code source
.ml,</li>
<li>ocamlbuild, un assistant de compilation, comme make, mais spécialisé pour
OCaml et très simple,</li>
<li>et enfin, des outils de profiling.</li>
</ul>
<h2>Sa richesse</h2>
<p>C'est à coup sûr un des seuls langage à permettre de choisir le paradigme le
plus adapté pour chaque partie de son programme!<br />
Nous pouvons ainsi utiliser selon le contexte du <strong>fonctionnel, de
l'impératif ou de l'objet</strong>.<br />
Mais ce langage va encore plus loin, et intègre des concepts de programmation
que tout langage digne de ce nom devrait intégrer:</p>
<ul>
<li>polymorphisme paramétré (ie generics de java et templates de C++),</li>
<li><a href="http://fr.wikipedia.org/wiki/Fonction_d%27ordre_sup%C3%A9rieur">fonctions
d'ordre supérieur</a> (une fonction peut prendre en paramètre ou retourner une
fonction, et ce de manière très naturelle),</li>
<li><a href="http://fr.wikipedia.org/wiki/Fermeture_%28informatique%29">fermetures</a>
(closure in english, difficile à résumer!),</li>
<li>prise en charge des exceptions,</li>
<li><a href="http://fr.wikipedia.org/wiki/Filtrage_par_motif">filtrage par
motifs</a> (pattern matching in english),</li>
<li><a href="http://fr.wikipedia.org/wiki/Inf%C3%A9rence_de_types">l'inférance
de types</a></li>
<li>...</li>
</ul>
<p>Voici comment obtenir un quicksort clair et simple avec du filtrage par
motif, du fonctionnel, des types inférés ainsi que des fermetures et une
fonction d'ordre supérieur :</p>
<pre>
let rec quicksort = function (* filtrage par motif du paramètre d'entrée qui est la liste à trier *)
| [] -> [] (* [] est une liste vide *)
| pivot :: rest -> (* pivot = premier element, rest = le reste de la liste *)
let is_less x = x < pivot in (* fermeture, car nous utilisons la variable pivot *)
let left, right = List.partition is_less rest in (* partition est une fonction d'ordre supérieur car is_less est une fonction *)
(quicksort left) @ [pivot] @ (quicksort right) (* @ permet de concaténer des listes *)
(* le tout sans avoir à préciser les types ... *)
(* List.partition p l returns a pair of lists (l1, l2), where l1 is the list of all the elements of l that satisfy the predicate p, and l2 is the list of all the elements of l that do not satisfy p. *)
</pre>
Le fonctionnel arrive
2008-01-07T08:00:00.000Z
http://techno-barje.fr/post/2008/01/07/Le-fonctionnel-arrive
<h3><a href="/tag/F">F#</a></h3>
<p>Le tout dernier langage de Microsoft, F# est basé à 80% sur Ocaml!<br />
<a href="http://research.microsoft.com/fsharp/manual/ml-compat.aspx" hreflang="en">Détail des différences entre ocaml et F#</a><br /></p>
<h3><a href="/tag/Haskell">Haskell</a>, <a href="/tag/HOP">HOP</a> et l'INRIA
dans la presse du monde php/java</h3>
<p>Les magazines de vulgarisation se mettent à parler d'<a href="http://fr.wikipedia.org/wiki/Haskell" hreflang="fr">Haskell</a> (langage
fonctionnel pur)<br />
<a href="http://www.programmez.com/magazine_articles.php?id_article=980&&titre=Introduction%20%C3%A0%20la%20programmation%20fonctionnelle%20avec%20Haskell" hreflang="fr">Introduction sur programmez.com</a></p>
<p>Il vont même jusqu'à parler de <a href="http://hop.inria.fr/" hreflang="fr">HOP</a>, un projet de l'inria qui propose un framework de développement
d'applications web (html/js) avec un langage basé en grande partie sur Scheme.
Et pour ceux qui ne le savent pas, <a href="http://fr.wikipedia.org/wiki/Scheme" hreflang="fr">Scheme</a> est un autre
langage fonctionnel!<br />
<a href="http://www.programmez.com/magazine_articles.php?id_article=974&&titre=Hop,%20un%20langage%20de%20programmation%20pour%20le%20Web%20(1re%20partie)" hreflang="fr">Intro de l'article sur HOP</a><br /></p>
<h3><a href="/tag/Intel">Intel</a> et <a href="/tag/Ct">Ct</a></h3>
<p>Intel planche sur le calcul parallèle (il y'a effectivement de quoi faire
avec les multi core!) et a créé un nouveau langage : <em><a href="http://fr.wikipedia.org/wiki/Intel_Ct" hreflang="fr">Ct</a> is strict and
purely functional</em> (<a href="http://techresearch.intel.com/articles/Tera-Scale/1514.htm" hreflang="en">description très détaillée de Ct</a>)<br />
C'est un langage pour simplifier le développement sur les machines multicore,
et plus particulièrement sur les serveurs de calculs du projet <a href="http://techresearch.intel.com/articles/Tera-Scale/1421.htm" hreflang="en">TeraScale</a> (processeur avec 80cores).<br />
<a href="http://cufp.galois.com/slides/2007/AnwarGhuloum.pdf" hreflang="en">Présentation de Ct par un de ses créateurs</a><br /></p>
OCaml est là
2008-01-07T08:00:00.000Z
http://techno-barje.fr/post/2008/01/07/OCaml-est-la
<p><a href="/tag/Ocaml">Ocaml</a> et la <a href="/tag/programmation%20fonctionnelle">programmation fonctionnelle</a> sont déjà
parmis nous!</p>
<ul>
<li><a href="http://fr.wikipedia.org/wiki/MediaWiki#Le_contenu_.C3.A9labor.C3.A9" hreflang="fr">MediaWiki</a> (le logiciel faisant tourner wikipedia) utilise ocaml pour
afficher ses formules mathématiques!</li>
</ul>
<ul>
<li>KDE fait de même avec son application de tableau périodique des elements
chimiques : <a href="http://www.framasoft.net/article4052.html" hreflang="fr">kalzium</a>, pour résoudre des équations chimiques!</li>
</ul>
<blockquote>
<p>a CH3CH2OH + b O2 -> c H2O + d CO2 => 1 CH3CH2OH + 3 O2 -> 3 H2O +
2 CO2<br />
<a href="http://websvn.kde.org/trunk/KDE/kdeedu/kalzium/src/solver/README?revision=437725&view=markup" hreflang="en">Code sources</a></p>
</blockquote>
<ul>
<li><a href="http://fr.wikipedia.org/wiki/XSLT" hreflang="fr">XSLT</a>
<strong>est</strong> une langage fonctionnel décrit en XML!</li>
</ul>
<ul>
<li>Enfin, un phénomène majeur est l'adoption massive des libraries
<em>ajax</em> comme <a href="http://jquery.com/" hreflang="en">jQuery</a>.</li>
</ul>
<p>Figurez vous que ces libraries utilisent fortement les aspects fonctionnels
de javascript.<br />
Examples avec jQuery :</p>
<pre>
$('#myButton').bind('click', function() {
// 'this' is the DOM element that triggered the event
alert(this.id == 'myButton');
});
</pre>
<pre>
$('div').each(function() {
// 'this' is a DOM element
alert(this.tagName.toLowerCase() == 'div');
});
</pre>
OCaml "existe".
2007-12-26T08:00:00.000Z
http://techno-barje.fr/post/2007/12/26/OCaml-existe
<p>Commençons simplement en montrant que <q>OCaml EXISTE</q>.</p>
<h2><a href="/tag/Miniville">Miniville</a></h2>
<p>Vous avez surement entendu parlé de <a href="http://montcuq.miniville.fr/" hreflang="fr">Miniville</a>, mais si, le jeu en flash où chaque visite permet
d'agrandir une ville. C'est sans intérêt, certes, mais cela a le mérite
d'exister dans la vraie vie. Donc vous ne le devinerez jamais, mais l'équipe
qui a développé ce jeu, Motion twin, utilise <a href="/tag/OCaml">OCaml</a>!</p>
<p>En effet, il suffit d'aller sur <a href="http://tech.motion-twin.com/ocaml.html" hreflang="fr">leur site</a> pour se
rendre compte qu'ils ont assimilé les qualité de ce langage. Ils ont ainsi
développé une plateforme/langage maison qui sert à générer des applications
flash ainsi que le webservice côté serveur.<br />
Le nom de se projet ? <a href="http://haxe.org/fr/intro" hreflang="fr">Haxe</a><br />
Un <a href="http://haxe.org/projects" hreflang="fr">tas de projets</a>
gravitent déjà autour!<br />
(Disponible sur linux, mac et win)</p>
<p>Pas convaincu ? Vous n'avez peut être pas le plugin flash ?<br />
Et bah ça tombe bien, j'ai un autre exemple pile poil pour vous!</p>
<h2><a href="/tag/Xen">Xen</a></h2>
<p><a href="http://www.citrixxenserver.com/Pages/default.aspx" hreflang="en">Xen</a>, l'hyperviseur de machines virtuelles embarque avec lui quelques
100 000 lignes d'<a href="/tag/OCaml">OCaml</a>!<br />
La cerise sur le cadeau, c'est que ce logiciel libre n'est pas développé par
des frenchies de l'INRIA! Non, Xen vient tout droit de l'université de
Cambridge.</p>
<p>Source de cette info, <a href="http://groups.google.com/group/fa.caml/browse_frm/thread/5b3755174580829e/7ea42655927e438e#7ea42655927e438e" hreflang="en">la recherche de profil "ocaml hacker"</a> par Xen.</p>
<h2><a href="/tag/EDOS">EDOS</a></h2>
<p><q><a href="http://www.edos-project.org/" hreflang="en">Environment for the
development and Distribution of Open Source software</a></q><br />
Ce projet a pour but de faciliter le développement, la maintenance et les tests
des distributions libres en améliorant les outils de gestion de paquets. En
voici un projet intéressant! Oui, c'est un projet en partie français, oui
l'INRIA et des universités sont sur le coup, <strong>mais</strong> cela a déjà
des retombées concrètes : <a href="http://edos.debian.net/">Assurance
qualité de Debian</a> et les outils développés peuvent potentiellement devenir
des applications courantes.<br />
J'allais oublier de préciser: OCaml est le langage de programmation principal
de ce projet.</p>
<p><a href="http://www.edos-project.org/xwiki/bin/view/Main/Tools" hreflang="en">Liste des outils développés</a><br />
<a href="https://protactinium.pps.jussieu.fr:12345/svn/edos/software/dependencies/" hreflang="en">Accès direct aux sources</a></p>
<p>Pour ceux qui sont passés à jussieu, il est important de noter la
participation de <a href="http://www.pps.jussieu.fr/~dicosmo/">M. Di
cosmo</a></p>
<p><br /></p>
<pre>
Conclusion > <strong>OCaml existe.</strong>
</pre>
let's go
2007-12-21T08:00:00.000Z
http://techno-barje.fr/post/2007/12/21/lets-go
<p>Bon aller, je me lance!<br />
Rien de bien original en cette fin d'année 2007 à lancer un blog, néanmoins,
j'espère l'agrémenter régulièrement de contenu passionné afin de promouvoir ma
vision très personnelle de l'informatique.</p>
<p>Ainsi, je vous invite vivement à passer dans le coin si vous êtes intéressés
soit par les technologies employées par <strong><a href="/tag/Mozilla">Mozilla</a></strong> : xul, html, xbl, rdf, xpcom, svg, …,
ou si, à l'opposé de l'univers du développement logiciel, vous voyez dans le
langage <strong><a href="/tag/OCaml">OCaml</a></strong> de grands espoirs.</p>
<p>En effet, je vous montrerais par le biais de projets personnels ou
professionnels le potentiel, la viabilité, les défauts et les qualités de ces
technologies.</p>
<p>PS : le thème n'est pas super, mais un tout beau arrive, merci
Takyon!</p>