Permalink
Browse files

Merge pull request #411 from ochameau/fix688127

Bug 688127: Add property to provide JSON set of global variables for initial state of content scripts r=@ochameau
  • Loading branch information...
2 parents 677a214 + 3df59ea commit 0006f26a99b3b9016ac8def642e125de31b87492 @ochameau ochameau committed Apr 24, 2012
@@ -62,3 +62,4 @@ We'd like to thank our many Jetpack project contributors! They include:
* Piotr Zalewa
* [David Guo](https://github.com/dglol)
* [Nils Maier](https://github.com/nmaier)
+* [Louis-Rémi Babé](https://github.com/louisremi)
@@ -9,9 +9,10 @@ The constructors for content-script-using objects such as panel and page-mod
define a group of options for loading content scripts:
<pre>
- contentScript string, array
- contentScriptFile string, array
- contentScriptWhen string
+ contentScript string, array
+ contentScriptFile string, array
+ contentScriptWhen string
+ contentScriptOptions object
</pre>
We have already seen the `contentScript` option, which enables you to pass
@@ -71,3 +72,8 @@ has been loaded, at the time the
fires.
The default value is "end".
+
+The `contentScriptOptions` is a json that is exposed to content scripts as a read
+only value under `self.options` property.
+
+Any kind of jsonable value (object, array, string, etc.) can be used here.
@@ -352,6 +352,11 @@ Creates a PageMod.
fires
This property is optional and defaults to "end".
+ @prop [contentScriptOptions] {object}
+ Read-only value exposed to content scripts under `self.options` property.
+
+ Any kind of jsonable value (object, array, string, etc.) can be used here.
+ Optional.
@prop [contentStyleFile] {string,array}
The local file URLs of stylesheet to load. Content style specified by this
@@ -187,6 +187,11 @@ loaded until its `destroy` method is called or the add-on is unloaded.
fires
This property is optional and defaults to "end".
+ @prop [contentScriptOptions] {object}
+ Read-only value exposed to content scripts under `self.options` property.
+
+ Any kind of jsonable value (object, array, string, etc.) can be used here.
+ Optional.
@prop [onMessage] {function}
Use this to add a listener to the page worker's `message` event.
@@ -246,6 +251,14 @@ load.
</api>
+<api name="contentScriptOptions">
+@property {object}
+Read-only value exposed to content scripts under `self.options` property.
+
+Any kind of jsonable value (object, array, string, etc.) can be used here.
+Optional.
+</api>
+
<api name="destroy">
@method
Unloads the page worker. After you destroy a page worker, its memory is freed
@@ -399,6 +399,11 @@ Creates a panel.
fires
This property is optional and defaults to "end".
+ @prop [contentScriptOptions] {object}
+ Read-only value exposed to content scripts under `self.options` property.
+
+ Any kind of jsonable value (object, array, string, etc.) can be used here.
+ Optional.
@prop [onMessage] {function}
Include this to listen to the panel's `message` event.
@@ -480,6 +485,14 @@ fires
</api>
+<api name="contentScriptOptions">
+@property {object}
+Read-only value exposed to content scripts under `self.options` property.
+
+Any kind of jsonable value (object, array, string, etc.) can be used here.
+Optional.
+</api>
+
<api name="destroy">
@method
Destroys the panel, unloading any content that was loaded in it. Once
@@ -527,22 +527,27 @@ Represents a widget object.
specified by the `contentScriptFile` property.
@prop [contentScriptWhen="end"] {string}
- When to load the content scripts. This may take one of the following
- values:
+ When to load the content scripts. This may take one of the following
+ values:
- * "start": load content scripts immediately after the document
- element for the widget is inserted into the DOM, but before the DOM content
- itself has been loaded
- * "ready": load content scripts once DOM content has been loaded,
- corresponding to the
- [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
- event
- * "end": load content scripts once all the content (DOM, JS, CSS,
- images) for the widget has been loaded, at the time the
- [window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
- fires
+ * "start": load content scripts immediately after the document
+ element for the widget is inserted into the DOM, but before the DOM content
+ itself has been loaded
+ * "ready": load content scripts once DOM content has been loaded,
+ corresponding to the
+ [DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
+ event
+ * "end": load content scripts once all the content (DOM, JS, CSS,
+ images) for the widget has been loaded, at the time the
+ [window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
+ fires
+
+ This property is optional and defaults to "end".
+ @prop [contentScriptOptions] {object}
+ Read-only value exposed to content scripts under `self.options` property.
- This property is optional and defaults to "end".
+ Any kind of jsonable value (object, array, string, etc.) can be used here.
+ Optional.
</api>
@@ -665,6 +670,14 @@ Represents a widget object.
</api>
+<api name="contentScriptOptions">
+@property {object}
+Read-only value exposed to content scripts under `self.options` property.
+
+Any kind of jsonable value (object, array, string, etc.) can be used here.
+Optional.
+</api>
+
<api name="port">
@property {EventEmitter}
[EventEmitter](packages/api-utils/events.html) object that allows you to:
@@ -858,6 +871,14 @@ In this example `WidgetView` is used to display different content for
</api>
+<api name="contentScriptOptions">
+@property {object}
+Read-only value exposed to content scripts under `self.options` property.
+
+Any kind of jsonable value (object, array, string, etc.) can be used here.
+Optional.
+</api>
+
<api name="port">
@property {EventEmitter}
[EventEmitter](packages/api-utils/events.html) object that allows you to:
@@ -101,6 +101,7 @@ const PageMod = Loader.compose(EventEmitter, {
contentScript: Loader.required,
contentScriptFile: Loader.required,
contentScriptWhen: Loader.required,
+ contentScriptOptions: Loader.required,
include: null,
constructor: function PageMod(options) {
this._onContent = this._onContent.bind(this);
@@ -112,6 +113,8 @@ const PageMod = Loader.compose(EventEmitter, {
this.contentScript = options.contentScript;
if ('contentScriptFile' in options)
this.contentScriptFile = options.contentScriptFile;
+ if ('contentScriptOptions' in options)
+ this.contentScriptOptions = options.contentScriptOptions;
if ('contentScriptWhen' in options)
this.contentScriptWhen = options.contentScriptWhen;
if ('onAttach' in options)
@@ -200,6 +203,7 @@ const PageMod = Loader.compose(EventEmitter, {
window: window,
contentScript: this.contentScript,
contentScriptFile: this.contentScriptFile,
+ contentScriptOptions: this.contentScriptOptions,
onError: this._onUncaughtError
});
this._emit('attach', worker);
@@ -316,4 +320,4 @@ const PageModManager = Registry.resolve({
delete RULES[topic];
}
});
-const pageModManager = PageModManager();
+const pageModManager = PageModManager();
@@ -37,6 +37,8 @@ const Page = Trait.compose(
this.contentScriptWhen = options.contentScriptWhen;
if ('contentScriptFile' in options)
this.contentScriptFile = options.contentScriptFile;
+ if ('contentScriptOptions' in options)
+ this.contentScriptOptions = options.contentScriptOptions;
if ('contentScript' in options)
this.contentScript = options.contentScript;
if ('allow' in options)
@@ -802,6 +802,7 @@ WidgetChrome.prototype.setContent = function WC_setContent() {
contentScriptFile: this._widget.contentScriptFile,
contentScript: this._widget.contentScript,
contentScriptWhen: this._widget.contentScriptWhen,
+ contentScriptOptions: this._widget.contentScriptOptions,
allow: this._widget.allow,
onMessage: function(message) {
setTimeout(function() {
@@ -59,7 +59,8 @@ exports.testPageMod = function testPageMod(test, testURL, pageModOptions,
tabBrowser.removeTab(newTab);
loader.unload();
test.done();
- });
+ }
+ );
}
b.addEventListener("load", onPageLoad, true);
@@ -419,6 +419,32 @@ exports.testAutomaticDestroy = function(test) {
}
+exports.testContentScriptOptionsOption = function(test) {
+ test.waitUntilDone();
+
+ let callbackDone = null;
+ testPageMod(test, "about:", [{
+ include: "about:*",
+ contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
+ contentScriptWhen: "end",
+ contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
+ onAttach: function(worker) {
+ worker.on('message', function(msg) {
+ test.assertEqual( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+ test.assertEqual( typeof msg[1], 'object', 'object as contentScriptOptions' );
+ test.assertEqual( msg[1].a, true, 'boolean in contentScriptOptions' );
+ test.assertEqual( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+ test.assertEqual( msg[1].c, 'string', 'string in contentScriptOptions' );
+ callbackDone();
+ });
+ }
+ }],
+ function(win, done) {
+ callbackDone = done;
+ }
+ );
+};
+
exports.testPageModCss = function(test) {
let [pageMod] = testPageMod(test,
'data:text/html,<div style="background: silver">css test</div>', [{
@@ -328,6 +328,23 @@ tests.testMultipleDestroys = function(test) {
test.pass("Multiple destroys should not cause an error");
};
+exports.testContentScriptOptionsOption = function(test) {
+ test.waitUntilDone();
+
+ let page = new Page({
+ contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
+ contentScriptWhen: "end",
+ contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
+ onMessage: function(msg) {
+ test.assertEqual( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+ test.assertEqual( typeof msg[1], 'object', 'object as contentScriptOptions' );
+ test.assertEqual( msg[1].a, true, 'boolean in contentScriptOptions' );
+ test.assertEqual( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+ test.assertEqual( msg[1].c, 'string', 'string in contentScriptOptions' );
+ test.done();
+ }
+ });
+};
function isDestroyed(page) {
try {
@@ -440,6 +440,25 @@ tests.testContentURLOption = function(test) {
"Panel throws an exception if contentURL is not a URL.");
};
+exports.testContentScriptOptionsOption = function(test) {
+ test.waitUntilDone();
+
+ let loader = Loader(module);
+ let panel = loader.require("panel").Panel({
+ contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
+ contentScriptWhen: "end",
+ contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
+ onMessage: function(msg) {
+ test.assertEqual( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+ test.assertEqual( typeof msg[1], 'object', 'object as contentScriptOptions' );
+ test.assertEqual( msg[1].a, true, 'boolean in contentScriptOptions' );
+ test.assertEqual( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+ test.assertEqual( msg[1].c, 'string', 'string in contentScriptOptions' );
+ test.done();
+ }
+ });
+};
+
let panelSupported = true;
try {
@@ -922,6 +922,28 @@ exports.testWidgetWithPound = function testWidgetWithPound(test) {
});
};
+exports.testContentScriptOptionsOption = function(test) {
+ test.waitUntilDone();
+
+ let widget = require("widget").Widget({
+ id: "fooz",
+ label: "fooz",
+ content: "fooz",
+ contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
+ contentScriptWhen: "end",
+ contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
+ onMessage: function(msg) {
+ test.assertEqual( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+ test.assertEqual( typeof msg[1], 'object', 'object as contentScriptOptions' );
+ test.assertEqual( msg[1].a, true, 'boolean in contentScriptOptions' );
+ test.assertEqual( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+ test.assertEqual( msg[1].c, 'string', 'string in contentScriptOptions' );
+ widget.destroy();
+ test.done();
+ }
+ });
+};
+
exports.testNavigationBarWidgets = function testNavigationBarWidgets(test) {
test.waitUntilDone();
@@ -181,13 +181,13 @@ const ContentWorker = Object.freeze({
ContentWorker.createEventEmitter(pipe.emit.bind(null, "event"));
pipe.on("event", portEmit);
- let self = Object.freeze({
+ let self = {
port: port,
postMessage: pipe.emit.bind(null, "message"),
on: pipe.on.bind(null),
once: pipe.once.bind(null),
removeListener: pipe.removeListener.bind(null),
- });
+ };
Object.defineProperty(exports, "self", {
value: self
});
@@ -233,12 +233,23 @@ const ContentWorker = Object.freeze({
});
},
- inject: function (exports, chromeAPI, emitToChrome) {
+ injectOptions: function (exports, options) {
+ Object.defineProperty( exports.self, "options", { value: JSON.parse( options ) });
+ },
+
+ inject: function (exports, chromeAPI, emitToChrome, options) {
let { pipe, onChromeEvent, hasListenerFor } =
ContentWorker.createPipe(emitToChrome);
+
ContentWorker.injectConsole(exports, pipe);
ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console);
ContentWorker.injectMessageAPI(exports, pipe);
+ if ( options !== undefined ) {
+ ContentWorker.injectOptions(exports, options);
+ }
+
+ Object.freeze( exports.self );
+
return {
emitToContent: onChromeEvent,
hasListenerFor: hasListenerFor
@@ -77,6 +77,14 @@ fires
</api>
+<api name="contentScriptOptions">
+@property {object}
+Read-only value exposed to content scripts under `self.options` property.
+
+Any kind of jsonable value (object, array, string, etc.) can be used here.
+Optional.
+</api>
+
<api name="contentURL">
@property {string}
The URL of the content loaded.
Oops, something went wrong.

0 comments on commit 0006f26

Please sign in to comment.