User:Adw/e10s-context-menu

From MozillaWiki
Jump to: navigation, search

We can't avoid content scripts. We can either encourage (or require) people to write content scripts in separate files or not. It's our design decision to make, and it has implications for the context-menu API. We should apply this decision and its consequences consistently across all Jetpack APIs, even if APIs use content scripts in different ways.

If we don't encourage (or require) people to write content scripts in separate files, then the alternative is to write them in jetpack scripts and serialize them somehow. I've argued that the best way to do that is to require people to write their content script code in strings. What would the context-menu API look like?

require("context-menu").Item({
  label: "Remove Image",
  context: '
    var context = {
      match: "img"
    };
  ',
  onClick: '
    node.parentNode.removeChild(node);
  '
});

Each bit of content script is pretty simple, but writing code in strings sucks I say.

What if we encourage (or require) content scripts to be in separate files? We should minimize the number of files people have to make. You shouldn't need one for context and another for onClick(). You shouldn't need one for each Item you add to the menu. If we accept those constraints, then:

// jetpack script

require("context-menu").Item({
  label: "Remove Image",
  id: "remove-image",
  contentScript: "my-content-script.js"
});
// content script

setupItem({
  id: "remove-image",
  context: "img",
  onClick: function (contextObj) {
    contextObj.node.parentNode.removeChild(contextObj.node);
  }
});

Note:

  • Items now need IDs so the jetpack script and content script can (declaratively) speak about the same Item.
  • A setupItem() function is in the scope of the content script.

Where does setupItem() come from? Or, what if people could use a single content script for all the various Jetpack APIs they use? Then:

// content script

require("context-menu").setupItem({
  id: "remove-image",
  context: "img",
  onClick: function (contextObj) {
    contextObj.node.parentNode.removeChild(contextObj.node);
  }
});

The content script is also evaluated in a CommonJS environment. The content script has access to the "content half" of the context-menu module.

Here's an example with an onClick() in the jetpack script:

// jetpack script

require("context-menu").Item({
  label: "Download Image",
  id: "download-image",
  contentScript: "my-content-script.js",
  onClick: function (imgSrc) {
    require("image-downloader").download(imgSrc);
  }
});
// content script

require("context-menu").setupItem({
  id: "remove-image",
  context: "img",
  onClick: function (contextObj) {
    return contextObj.node.src;
  }
});

If onClick() is defined on the Item in the jetpack script, it is passed the return value of onClick() in the content script. If there is no onClick() in the content script, onClick() in the jetpack script isn't passed anything.