User:Asqueella/JEP 107

From MozillaWiki
Jump to navigation Jump to search

This is an edited version of Labs/Jetpack/Reboot/JEP/107.

Page Mods

There are two common ways to modify web pages from the browser:

  • by specifying custom CSS to apply to specific pages, sites, or all web content. (This is what Stylish extension does.)
  • by letting specified scripts run modify the web page (see Greasemonkey extension)

The CSS-only approach is less powerful, yet often simpler and allows to dynamically apply and cancel custom style modifications to the page.

The JavaScript-based approach allows virtually any changes to the page to be implemented, but requires special (and non-trivial) effort to implement instant application and undoing of these modifications.

ISSUE: Therefore, it might make sense to provide separate APIs for these two types of modifications. We'll call them "Script mods" and "Style mods" below.

API

Script Mods

Here's an example of how the ScriptMod API can be used in a jetpack:

var ScriptMod = require("script-mod").ScriptMod;
var myMod = new ScriptMod({
  include: ["*.example.com",
            "http://example.org/a/specific/url",
            "http://example.info/*"],
  script: [
    function(wrappedWindow) {
      // this runs each time a new content document starts loading, but
      // before the page starts loading, so we can't interact with the
      // page's DOM here yet.
      wrappedWindow.wrappedJSObject.newExposedProperty = 1;
    },
    ScriptMod.onDOMContentLoaded(function(wrappedWindow) {
      // at this point we can work with the DOM
      wrappedWindow.document.body.innerHTML = "<h1>Jetpack Page Mods</h1>";
    })
  ]
});

In this proposal, a script mod specifies the pages it might modify, and the scripts to run on these pages.

By default, the provided callback function gets called at the earliest possible moment (before the page even started loading, which is made possible by the notifications added in bug 549539), but there are helpers provided to do common tasks, such as:

  • running a script when the page's DOM is ready (ScriptMod.onDOMContentLoaded(callback))
  • TBD: inserting <script> and <link> tags

ScriptMod constructor

The ScriptMod constructor takes a single options parameter which is an object that may define the following properties:

  • include: an optional parameter, specifying the pages the scripts in this ScriptMod should run on.
    • Default behavior is to run the mod's scripts on all pages.
    • Providing a string value str is equivalent to providing a single-item array [str].
    • The mod's scripts run on pages, matching any of include rules.
    • Each include rule is a string using one of the following formats (see discussion below):
      1. * (a single asterisk) - any page
      2. *.domain.name - pages from the specified domain and all its subdomains, regardless of their scheme.
      3. http://example.com/* - any URLs with the specified prefix.
      4. http://example.com/test - the single specified URL
  • TBD: filter function instead of (or in addition) to exclude.
  • script: an optional parameter specifying the code to run on the matched pages.
    • No code is run if this parameter is not specified.
    • Providing a single function func is equivalent to providing a single-item array [func]
    • When the provided value is an array, its items are expected to be functions. Non-function values are ignored.
    • The specified functions are called in order when a page matching the include rules starts to load (but before any content is loaded in the page -- i.e. when the content-document-global-created notification implemented in bug 549539 is issued). An exception thrown from one of the functions does not stop the rest of functions from executing.
    • The specified callbacks are called with a single wrappedWindow parameter -- the content's window object wrapped in an XPCNativeWrapper. The callback's this is the page mod object (TBD not currently implemented).
ISSUE: What format should be chosen for include rules?

First, a short survey of existing formats:

  • Greasemonkey scripts specify include and exclude URLs, each may contain wildcards ("*") in any location and may use a special ".tld" domain. These rules get compiled to a regular expression (see convert2RegExp), which is then matched against every URL loaded in the browser.
  • When specifying CSS styling Using the Stylesheet Service, which is an easy and robust way to apply CSS to all content and is also what Stylish uses, you have to describe the filters using CSS, i.e. @-moz-document rule. It allows to specify domain, exact URL, or the URL prefix.

I claim the requirements for the include rules format are:

  1. Easy to use for the common case (unfortunately, no stats on which cases are common were collected, so we'll guess).
  2. An efficient implementation that determines which script mods to run on a page should be possible, since there may be many mods installed and each navigation requires us to determine which page mods apply to the page.
  3. If possible, the include rules for the script and style mods should use the same format.

A non-requirement is high level of control over which URLs are matched, since the highest level of control can be provided via a filter function, but it's important for performance to narrow down the set of URLs before running possibly expensive test determining whether to run the mod on a given page.

#3 suggests we use a format that can be transformed to @-moz-document rules, i.e. a format that allows to specify the exact URL, the URL prefix, or the domain. This format also can be efficiently implemented (unlike e.g. a format that allows wildcards in the random positions of the URL).

This is how I've arrived at the format described above.

ScriptMod APIs

Style mods

TBD

Use Cases

  1. Creation of CSS-based add-ons like Stylish, EditCSS, etc...
  2. Creation of JS-based add-ons like Execute JS, JS Exec etc...
  3. In General: Any Greasemonky-style add-on, with the advantage that this API would allow for far greater flexibility - turning on and off only certain parts of a mod, automatically flashing a new url/web-page with the active parts of a mod by using the add method to include a new match to the matches white-list

Common Actions

The API, if done in this fashion, give the developer the ability to dramatically simplify application actions such as:

  • Creating an instance of Page Mods that adds script or styles to a set of matched urls
  • Further extending and existing instance of Page Mods with additional styles and script
  • Toggling on and off specific styles or script within a Page Mods instance
  • Adding new matches to a Page Mods instance, which in turn instantly applies active styles and script within that instance to the newly added matches.
  • Multiple instances of Page Mods can be instantiated, which enables a whole cadre of functionality that the object-bound 'singleton' implementation neglects.