Put OOMPH product versions in separate files

Just a quick tip for those who have big setup files for OOMPH products. I recently split up one by putting product versions in other files. Here is how to proceed.

One big setup file would look like this…

<?xml version="1.0" encoding="UTF-8"?>
<setup:ProductCatalog
    xmi:version="2.0"
    xmlns:xmi="http://www.omg.org/XMI"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:setup="http://www.eclipse.org/oomph/setup/1.0"
    name="my.product"
    label="some label">
    
  <!-- ... -->

  <product name="myproduct" label="Custom Eclipse">
    <annotation
        source="http://www.eclipse.org/oomph/setup/BrandingInfo">
      <detail
          key="folderName">
        <value>eclipse</value>
      </detail>
      <detail
          key="folderName.macosx">
        <value>Eclipse</value>
      </detail>
    </annotation>
    
    <version name="neon"
        label="Latest Neon"
        requiredJavaVersion="1.8">

        <!-- ... -->

    </version>

    <!-- Maybe with several versions. -->

    <description>...</description>
  </product>
</setup:ProductCatalog>

Now, to split it up, just add a reference to another file.

<?xml version="1.0" encoding="UTF-8"?>
<setup:ProductCatalog
    xmi:version="2.0"
    xmlns:xmi="http://www.omg.org/XMI"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:setup="http://www.eclipse.org/oomph/setup/1.0"
    name="my.product"
    label="some label">
    
  <!-- ... -->

  <product name="myproduct" label="Custom Eclipse">
    <annotation
        source="http://www.eclipse.org/oomph/setup/BrandingInfo">
      <detail
          key="folderName">
        <value>eclipse</value>
      </detail>
      <detail
          key="folderName.macosx">
        <value>Eclipse</value>
      </detail>
    </annotation>
    
    <version href="neon/my.products.neon.setup#/" />
    <description>...</description>
  </product>
</setup:ProductCatalog>

The important part is the reference to a sub-model file: version href=”neon/my.products.neon.setup#/”. And here is its content.

<?xml version="1.0" encoding="UTF-8"?>
<setup:ProductVersion
    xmi:version="2.0"
    xmlns:xmi="http://www.omg.org/XMI"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:setup="http://www.eclipse.org/oomph/setup/1.0"
    name="neon"
    label="Latest Neon"
    requiredJavaVersion="1.8">

      <!-- Force the loading of the parent when we open this file directly. -->
      <annotation source="ProductReference">
            <reference href="../my-other-product.setup#/" />
      </annotation>

      <!-- ... -->
      
</setup:ProductVersion>

The essential part here is the ProductReference annotation. It is has no meaning for the EMF model itself, but it forces EMF to load the parent. If you drop this annotation, and that you open this setup file, you will have an error stating that the required feature ‘product’ of ‘Custom Eclipse’ must be set. With it, no matter which setup file you open, everything will be resolved correctly, without an error in the setup editor.

I made this summary after asking on Eclipse’s forums.
Many thanks to Ed Merks for his help.

Getting the source code of a tab from a web extension

Just a quick snippet to show how to retrieve the source code of a page from a web extension for Firefox. As a reminder, in web extensions, there are background scripts (that run as a global background process) and content scripts (that run in each tab, along with the loaded HTML pages).

Background scripts cannot directly query the source code of a page. They run in background, they do not have access to pages information. However, background and content scripts can communicate together. So, the idea is that the background script will query the source code to a content script.

Let’s start with the interesting parts of the manifest:

"background": {
  "scripts": ["path/to/background/script.js"]
},

"content_scripts": [
{
  "matches": ["<all_urls>"],
  "js": ["path/to/content/script.js"]
}
],

"permissions": ["tabs"]

Notice the tabs permission.
We need it as this is not the main messaging system. Communications between background and content scripts must go through the tab API.

Here is the content of the background script.
It queries the source code and displays it then.

// Invoke the function...
getSourceCode();

// ... defined here
function getSourceCode() {
  
  // Get the page's source code.
  // Background scripts cannot directly get it, so we ask it to our content
  // script (in the currently active tab). So we have to go through the tab API.
  browser.tabs.query({active: true, currentWindow: true}).then( tabs => {
    browser.tabs.sendMessage( tabs[0].id, {'req':'source-code'}).then( response => {
      console.log('url = ' + tabs[0].url);
      console.log('source code = ' + response.content);
    });
  });
}

As you can see, we first find the active tab.
Then, we send a request to this tab. Like all the tabs, our content script runs in it. Here is its code. It receives the query and sends the source code (within a promise).

browser.runtime.onMessage.addListener(request => {
  var response = '';
  if(request.req === 'source-code') {
    response = document.documentElement.innerHTML;
  }

  return Promise.resolve({content: response});
});

That’s it.
The web extension will be able to manipulate the source code, as an example for a deeper analysis.

Downloading a file from a Firefox’s web extension

Just a small upgrade from a previous article.
If you want your web extension to download a file from a remote location, there are only few modifications to perform.

Make sure to add the <all_urls> permission in your manifest.

"permissions": ["<all_urls>"]

And then the request will work easily.

var url = 'http://google.fr';
   
const req = new XMLHttpRequest();
// To parse the remote document directly as a DOM document
// req.responseType = "document";
 
req.onreadystatechange = function(event) {
  if (this.readyState === XMLHttpRequest.DONE) {
    if (this.status === 200) {
      console.log("Received: %s", this.responseText);
    } else {
      console.log("Error: %d (%s)", this.status, this.statusText);
    }
  }
};
 
req.open('GET', t, true);
req.send(null);

What really matters is to set the right permission in the manifest.
Otherwise, you get a 0 return code and no content.

Loading the content of a local file in Firefox’s web extensions

Since Firefox 57, browser extensions must be web extensions.
For security reasons, these API do not allow to interact with the local file system. There are several strategies described on the web site as alternatives (the IndexedDB being one of the most interesting one IMO).

In this article, I will only describe how one of your script can load a local resource that was put within your extension. I used it to load a XML file in a background script.

First, make sure you make your resource available for web access.
Add this in your manifest:

"web_accessible_resources": ["path/to/my-resource.xml"],

And then, in your script:

var localUrl = browser.extension.getURL('path/to/my-resource.xml');
  
const req = new XMLHttpRequest();
// To parse the remote document directly as a DOM document
// req.responseType = "document";

req.onreadystatechange = function(event) {
  if (this.readyState === XMLHttpRequest.DONE) {
    if (this.status === 200) {
      console.log("Received: %s", this.responseText);
    } else {
      console.log("Error: %d (%s)", this.status, this.statusText);
    }
  }
};

req.open('GET', t, true);
req.send(null);

And that’s it.
This snippet will only print the file’s content, but you can adapt it for you needs.