User:Asqueella/JEP 107: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
(update for Myk's comments <http://groups.google.com/group/mozilla-labs-jetpack/msg/decd886a1ae37018>, except for "include" syntax)
Line 75: Line 75:
* [http://wiki.greasespot.net/Include_and_exclude_rules 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 [http://github.com/greasemonkey/greasemonkey/blob/master/content/convert2RegExp.js convert2RegExp]), which is then matched against every URL loaded in the browser.
* [http://wiki.greasespot.net/Include_and_exclude_rules 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 [http://github.com/greasemonkey/greasemonkey/blob/master/content/convert2RegExp.js convert2RegExp]), which is then matched against every URL loaded in the browser.
* [http://code.google.com/chrome/extensions/match_patterns.html Match patterns for Google Chrome's content scripts] are similar to Greasemonkey's, but force to specify domain (either fully, any domain, or <code>*.domain</code>) and don't have the magic tld domain.
* [http://code.google.com/chrome/extensions/match_patterns.html Match patterns for Google Chrome's content scripts] are similar to Greasemonkey's, but force to specify domain (either fully, any domain, or <code>*.domain</code>) and don't have the magic tld domain.
** Also of interest: [http://code.google.com/p/chromium/issues/detail?id=18259], [http://groups.google.com/a/chromium.org/group/chromium-extensions/browse_thread/thread/9e3903c0817b5837/3d305eb340f01763]
* When specifying CSS styling [https://developer.mozilla.org/en/Using_the_Stylesheet_Service 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. [https://developer.mozilla.org/index.php?title=En/CSS/%40-moz-document @-moz-document] rule. It allows to specify domain, exact URL, or the URL prefix.
* When specifying CSS styling [https://developer.mozilla.org/en/Using_the_Stylesheet_Service 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. [https://developer.mozilla.org/index.php?title=En/CSS/%40-moz-document @-moz-document] rule. It allows to specify domain, exact URL, or the URL prefix.


I claim the requirements for the include rules format are:
[http://groups.google.com/group/mozilla-labs-jetpack/msg/decd886a1ae37018 Myk's thoughts], "I need to take a closer look at this, but at first glance it looks like Chrome's format balances these goals best."
# Easy to use for the common case (unfortunately, no stats on which cases are common were collected, so we'll guess).
# 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.
# If possible, the <code>include</code> 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 <code>filter</code> 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.
 
<nowiki>#3</nowiki> suggests we use a format that can be transformed to <code>@-moz-document</code> 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.


===== Other issues =====
===== Other issues =====

Revision as of 22:34, 11 April 2010

This is an alternative version of Labs/Jetpack/Reboot/JEP/107 (page mods).

Here's in progress implementation (currently script 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.

This proposal focuses on the JavaScript-based approach.

Script Mods

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

var ScriptMod = require("page-mod").ScriptMod;
var myMod = new ScriptMod({
  include: ["*.example.com",
            "http://example.org/a/specific/url",
            "http://example.info/*"],
  onWindowCreate: 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;
  },
  onDOMReady: 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.

The onWindowCreate 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).

The onDOMReady callback is called as soon as the page's DOM is ready (on the DOMContentLoaded event)

Dependencies

  • This module requires bug 549539's fix, which (as of 2010-04-02) has only landed on mozilla-central (Firefox versions after 3.6.x). The plans are to land it on 3.6.x too, though.
  • It's not clear to me where the dependencies in the original JEP come from, perhaps some of them apply to this case too.

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 script mod should run on.
    • The 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.
  • onWindowCreate, onDOMReady: optional parameters specifying the code to run on the matched pages.
    • No code is run if these parameters are 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:
      • for onWindowCreate - 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)
      • for onDOMReady - when a DOMContentLoaded event fires for the matching page.
    • 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). It goes without saying that with this syntax the callbacks are run in the calling module's scope, not in the content page's scope.

Creating a ScriptMod instance automatically enables it.

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.
  • Match patterns for Google Chrome's content scripts are similar to Greasemonkey's, but force to specify domain (either fully, any domain, or *.domain) and don't have the magic tld domain.
  • 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.

Myk's thoughts, "I need to take a closer look at this, but at first glance it looks like Chrome's format balances these goals best."

Other issues
  • There's no easy way to clean up objects referenced from the web page (event listeners, exported APIs) when a jetpack using script mods is unloaded.
  • Since the design makes a promise to let one module hosting the script mod to touch a random set of sites, it's not clear if this is going to work well in the out of process tabs world.
  • Should provide an example of using jQuery in a script mod. It's likely possible, but not obvious, given that the script mod runs in a hosting module's scope.

ScriptMod APIs

  • Instance properties
    • scriptMod.disable(). Call this to stop a script mod from running on further pages. This does not undo the mod's effects on already loaded pages.
    • scriptMod.enable(). Enabling a script mod makes it take effect on any matching pages that start to load after the script mod is enabled. Enabling a script mod does not apply it to existing matching pages.
    • add/remove/empty from the original proposal appear inessential, especially if the changes are not applied instantly, as in this proposal.
  • Helper functions available as properties on the ScriptMod constructor.
    • TBD other helpers (insert <style>s, <script>s, etc.)

Style mods

TBD