Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Convert Instant Preview to a Jetpack #617

Open
wants to merge 3 commits into from

3 participants

@erikvold

No description provided.

@scrapmac

will this help anyhow ?

@erikvold

It'll mean we don't have to copy scripts into each addon folder, albeit that could be achieved with a little effort anyhow.

It'll mean we can utilize a good test framework and documentation system.

It'll mean others can use the code far more easily.

Requiring modules is a way of revealing the features a add-on uses, which will be info made available to users at some point afaik.

Having the Prospector team use the Add-on SDK will improve the SDK.

@Mardak
Owner

How do you build a xpi with this structure? I see that you put the common libs in the parent directory with its own package.json.

But if I run "cfx xpi" from instantPreview/ directory:

cuddlefish.packaging.PackageNotFoundError: prospector (required by 'prospector-instant-preview')

@erikvold

There may be a better way to link the package, or to organize this project, I don't claim to think this is optimal, but I did want to reuse modules, so this is why I did what I did.

To link the prospector package with your addon sdk you have a number of options, but what I do is make a soft link from the addon-sdk/packages dir called prospector which points to my checkout of this repo.

$ ln -s ~/github/prospector/ ~/github/addon-sdk/packages/prospector
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
27 instantPreview/install.rdf
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<r:RDF xmlns="http://www.mozilla.org/2004/em-rdf#"
- xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <r:Description about="urn:mozilla:install-manifest">
- <creator>Mozilla Labs</creator>
- <description>Start loading pages as you select them from the location bar suggestions</description>
- <homepageURL>https://mozillalabs.com/prospector</homepageURL>
- <iconURL>http://mozillalabs.com/wp-content/themes/labs_project/img/prospector-header.png</iconURL>
- <id>instant.preview@prospector.labs.mozilla</id>
- <name>Mozilla Labs: Prospector - Instant Preview</name>
- <version>4</version>
-
- <bootstrap>true</bootstrap>
- <type>2</type>
-
- <contributor>Edward Lee</contributor>
- <contributor>Erik Vold</contributor>
-
- <targetApplication>
- <r:Description>
- <id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</id>
- <minVersion>4.0</minVersion>
- <maxVersion>7.0a1</maxVersion>
- </r:Description>
- </targetApplication>
- </r:Description>
-</r:RDF>
View
34 instantPreview/bootstrap.js → instantPreview/lib/main.js
@@ -36,10 +36,9 @@
*
* ***** END LICENSE BLOCK ***** */
-const {interfaces: Ci, utils: Cu} = Components;
-const global = this;
-Cu.import("resource://gre/modules/AddonManager.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
+const {Ci, Cu} = require("chrome");
+var {listen} = require("listen");
+var {unload} = require("unload+");
/**
* Start showing a preview of the selected location bar suggestion
@@ -150,7 +149,8 @@ function addPreviews(window) {
stop = false;
return;
}
- Utils.delay(arguments.callee, 100);
+
+ require("timers").setTimeout(arguments.callee, 100);
// Short circuit if there's no suggestions but don't remove the preview
if (!urlBar.popupOpen)
@@ -158,6 +158,7 @@ function addPreviews(window) {
// Make sure we have something selected to show
let result = richBox.selectedItem;
+
if (result == null) {
removePreview();
return;
@@ -236,25 +237,6 @@ function addPreviews(window) {
/**
* Handle the add-on being activated on install/enable
*/
-function startup(data, reason) AddonManager.getAddonByID(data.id, function(addon) {
- // Load various javascript includes for helper functions
- ["utils"].forEach(function(fileName) {
- let fileURI = addon.getResourceURI("scripts/" + fileName + ".js");
- Services.scriptloader.loadSubScript(fileURI.spec, global);
- });
-
- Cu.import("resource://services-sync/util.js");
- watchWindows(addPreviews);
-});
-
-/**
- * Handle the add-on being deactivated on uninstall/disable
- */
-function shutdown(data, reason) {
- // Clean up with unloaders when we're deactivating
- if (reason != APP_SHUTDOWN)
- unload();
+exports.main = function main() {
+ require("window-watcher").watchWindows(addPreviews);
}
-
-function install() {}
-function uninstall() {}
View
15 instantPreview/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "prospector-instant-preview",
+ "fullName": "Mozilla Labs: Prospector - Instant Preview",
+ "author": "Mozilla Labs",
+ "contributors": [
+ "Edward Lee <edilee@mozilla.com>",
+ "Erik Vold <erikvvold@gmail.com>",
+ "Greg Parris <greg.parris@gmail.com>"
+ ],
+ "id": "instant.preview@prospector.labs.mozilla",
+ "version": "5",
+ "homepage": "https://mozillalabs.com/prospector",
+ "description": "Start loading pages as you select them from the location bar suggestions",
+ "dependencies": ["addon-kit", "api-utils", "prospector"]
+}
View
420 instantPreview/scripts/utils.js
@@ -1,420 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Home Dash Utility.
- *
- * The Initial Developer of the Original Code is The Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Edward Lee <edilee@mozilla.com>
- * Erik Vold <erikvvold@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-"use strict";
-
-/**
- * Get a localized string with string replacement arguments filled in and
- * correct plural form picked if necessary.
- *
- * @note: Initialize the strings to use with getString.init(addon).
- *
- * @usage getString(name): Get the localized string for the given name.
- * @param [string] name: Corresponding string name in the properties file.
- * @return [string]: Localized string for the string name.
- *
- * @usage getString(name, arg): Replace %S references in the localized string.
- * @param [string] name: Corresponding string name in the properties file.
- * @param [any] arg: Value to insert for instances of %S.
- * @return [string]: Localized string with %S references replaced.
- *
- * @usage getString(name, args): Replace %1$S references in localized string.
- * @param [string] name: Corresponding string name in the properties file.
- * @param [array of any] args: Array of values to replace references like %1$S.
- * @return [string]: Localized string with %N$S references replaced.
- *
- * @usage getString(name, args, plural): Pick the correct plural form.
- * @param [string] name: Corresponding string name in the properties file.
- * @param [array of any] args: Array of values to replace references like %1$S.
- * @param [number] plural: Number to decide what plural form to use.
- * @return [string]: Localized string of the correct plural form.
- */
-function getString(name, args, plural) {
- // Use the cached bundle to retrieve the string
- let str;
- try {
- str = getString.bundle.GetStringFromName(name);
- }
- // Use the fallback in-case the string isn't localized
- catch(ex) {
- str = getString.fallback.GetStringFromName(name);
- }
-
- // Pick out the correct plural form if necessary
- if (plural != null)
- str = getString.plural(plural, str);
-
- // Fill in the arguments if necessary
- if (args != null) {
- // Convert a string or something not array-like to an array
- if (typeof args == "string" || args.length == null)
- args = [args];
-
- // Assume %S refers to the first argument
- str = str.replace(/%s/gi, args[0]);
-
- // Replace instances of %N$S where N is a 1-based number
- Array.forEach(args, function(replacement, index) {
- str = str.replace(RegExp("%" + (index + 1) + "\\$S", "gi"), replacement);
- });
- }
-
- return str;
-}
-
-/**
- * Initialize getString() for the provided add-on.
- *
- * @usage getString.init(addon): Load properties file for the add-on.
- * @param [object] addon: Add-on object from AddonManager
- *
- * @usage getString.init(addon, getAlternate): Load properties with alternate.
- * @param [object] addon: Add-on object from AddonManager
- * @param [function] getAlternate: Convert a locale to an alternate locale
- */
-getString.init = function(addon, getAlternate) {
- // Set a default get alternate function if it doesn't exist
- if (typeof getAlternate != "function")
- getAlternate = function() "en-US";
-
- // Get the bundled properties file for the app's locale
- function getBundle(locale) {
- let propertyPath = "locales/" + locale + ".properties";
- let propertyFile = addon.getResourceURI(propertyPath);
-
- // Get a bundle and test if it's able to do simple things
- try {
- let bundle = Services.strings.createBundle(propertyFile.spec);
- bundle.getSimpleEnumeration();
- return bundle;
- }
- catch(ex) {}
-
- // The locale must not exist, so give nothing
- return null;
- }
-
- // Use the current locale or the alternate as the primary bundle
- let locale = Cc["@mozilla.org/chrome/chrome-registry;1"].
- getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global");
- getString.bundle = getBundle(locale) || getBundle(getAlternate(locale));
-
- // Create a fallback in-case a string is missing
- getString.fallback = getBundle("en-US");
-
- // Get the appropriate plural form getter
- Cu.import("resource://gre/modules/PluralForm.jsm");
- let rule = getString("pluralRule");
- [getString.plural] = PluralForm.makeGetter(rule);
-
- // Clear out the strings cache when cleaning up so new ones load
- unload(function() Services.strings.flushBundles());
-}
-
-/**
- * Helper that adds event listeners and remembers to remove on unload
- */
-function listen(window, node, event, func, capture) {
- // Default to use capture
- if (capture == null)
- capture = true;
-
- node.addEventListener(event, func, capture);
- function undoListen() {
- node.removeEventListener(event, func, capture);
- }
-
- // Undo the listener on unload and provide a way to undo everything
- let undoUnload = unload(undoListen, window);
- return function() {
- undoListen();
- undoUnload();
- };
-}
-
-/**
- * Load various packaged styles for the add-on and undo on unload
- *
- * @usage loadStyles(addon, styles): Load specified styles
- * @param [object] addon: Add-on object from AddonManager
- * @param [array of strings] styles: Style files to load
- */
-function loadStyles(addon, styles) {
- let sss = Cc["@mozilla.org/content/style-sheet-service;1"].
- getService(Ci.nsIStyleSheetService);
- styles.forEach(function(fileName) {
- let fileURI = addon.getResourceURI("styles/" + fileName + ".css");
- sss.loadAndRegisterSheet(fileURI, sss.USER_SHEET);
- unload(function() sss.unregisterSheet(fileURI, sss.USER_SHEET));
- });
-}
-
-/**
- * Create a trigger that allows adding callbacks by default then triggering all
- * of them.
- */
-function makeTrigger() {
- let callbacks = [];
-
- // Provide the main function to add callbacks that can be removed
- function addCallback(callback) {
- callbacks.push(callback);
- return function() {
- let index = callbacks.indexOf(callback);
- if (index != -1)
- callbacks.splice(index, 1);
- };
- }
-
- // Provide a way to clear out all the callbacks
- addCallback.reset = function() {
- callbacks.length = 0;
- };
-
- // Run each callback in order ignoring failures
- addCallback.trigger = function(reason) {
- callbacks.slice().forEach(function(callback) {
- try {
- callback(reason);
- }
- catch(ex) {}
- });
- };
-
- return addCallback;
-}
-
-/**
- * Synchronously query with an async statement fetching results by name
- */
-function spinQuery(connection, {names, params, query}) {
- // Initialize the observer to watch for application quits during a query
- if (spinQuery.checkReady == null) {
- // In the common case, return true to continue execution
- spinQuery.checkReady = function() true;
-
- // Change the checkReady function to throw to abort
- let abort = function(reason) {
- spinQuery.checkReady = function() {
- throw reason;
- };
- };
-
- // Add the observer and make sure to clean up
- let onQuit = function() abort("Application Quitting");
- Services.obs.addObserver(onQuit, "quit-application", false);
- unload(function() Services.obs.removeObserver(onQuit, "quit-application"));
-
- // Also watch for unloads to stop queries
- unload(function() abort("Add-on Unloading"));
- }
-
- // Remember the results from processing the query
- let allResults = [];
- let status;
-
- // Nothing to do with no query
- if (query == null)
- return allResults;
-
- // Create the statement and add parameters if necessary
- let statement = connection.createAsyncStatement(query);
- if (params != null) {
- Object.keys(params).forEach(function(key) {
- statement.params[key] = params[key];
- });
- }
-
- // Start the query and prepare to cancel if necessary
- let pending = statement.executeAsync({
- // Remember that we finished successfully
- handleCompletion: function handleCompletion(reason) {
- if (reason != Ci.mozIStorageStatementCallback.REASON_ERROR)
- status = allResults;
- },
-
- // Remember that we finished with an error
- handleError: function handleError(error) {
- status = error;
- },
-
- // Process the batch of results and save them for later
- handleResult: function handleResult(results) {
- let row;
- while ((row = results.getNextRow()) != null) {
- let item = {};
- names.forEach(function(name) {
- item[name] = row.getResultByName(name);
- });
- allResults.push(item);
- }
- },
- });
-
- // Grab the current thread so we can make it give up priority
- let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
-
- // Keep waiting until the query finished unless aborting
- try {
- while (spinQuery.checkReady() && status == null)
- thread.processNextEvent(true);
- }
- // Must be aborting, so cancel the query
- catch(ex) {
- pending.cancel();
- status = ex;
- }
-
- // Must have completed with an error so expose it
- if (status != allResults)
- throw status;
-
- return allResults;
-}
-
-/**
- * Save callbacks to run when unloading. Optionally scope the callback to a
- * container, e.g., window. Provide a way to run all the callbacks.
- *
- * @usage unload(): Run all callbacks and release them.
- *
- * @usage unload(callback): Add a callback to run on unload.
- * @param [function] callback: 0-parameter function to call on unload.
- * @return [function]: A 0-parameter function that undoes adding the callback.
- *
- * @usage unload(callback, container) Add a scoped callback to run on unload.
- * @param [function] callback: 0-parameter function to call on unload.
- * @param [node] container: Remove the callback when this container unloads.
- * @return [function]: A 0-parameter function that undoes adding the callback.
- */
-function unload(callback, container) {
- // Initialize the array of unloaders on the first usage
- let unloaders = unload.unloaders;
- if (unloaders == null)
- unloaders = unload.unloaders = [];
-
- // Calling with no arguments runs all the unloader callbacks
- if (callback == null) {
- unloaders.slice().forEach(function(unloader) unloader());
- unloaders.length = 0;
- return;
- }
-
- // The callback is bound to the lifetime of the container if we have one
- if (container != null) {
- // Remove the unloader when the container unloads
- container.addEventListener("unload", removeUnloader, false);
-
- // Wrap the callback to additionally remove the unload listener
- let origCallback = callback;
- callback = function() {
- container.removeEventListener("unload", removeUnloader, false);
- origCallback();
- }
- }
-
- // Wrap the callback in a function that ignores failures
- function unloader() {
- try {
- callback();
- }
- catch(ex) {}
- }
- unloaders.push(unloader);
-
- // Provide a way to remove the unloader
- function removeUnloader() {
- let index = unloaders.indexOf(unloader);
- if (index != -1)
- unloaders.splice(index, 1);
- }
- return removeUnloader;
-}
-
-/**
- * Apply a callback to each open and new browser windows.
- *
- * @usage watchWindows(callback): Apply a callback to each browser window.
- * @param [function] callback: 1-parameter function that gets a browser window.
- */
-function watchWindows(callback) {
- var unloaded = false;
- unload(function() unloaded = true);
-
- // Wrap the callback in a function that ignores failures
- function watcher(window) {
- try {
- // Now that the window has loaded, only handle browser windows
- let {documentElement} = window.document;
- if (documentElement.getAttribute("windowtype") == "navigator:browser")
- callback(window);
- }
- catch(ex) {}
- }
-
- // Wait for the window to finish loading before running the callback
- function runOnLoad(window) {
- // Listen for one load event before checking the window type
- window.addEventListener("load", function runOnce() {
- window.removeEventListener("load", runOnce, false);
- if (unloaded) return; // the extension has shutdown
- watcher(window);
- }, false);
- }
-
- // Add functionality to existing windows
- let windows = Services.wm.getEnumerator(null);
- while (windows.hasMoreElements()) {
- // Only run the watcher immediately if the window is completely loaded
- let window = windows.getNext();
- if (window.document.readyState == "complete")
- watcher(window);
- // Wait for the window to load before continuing
- else
- runOnLoad(window);
- }
-
- // Watch for new browser windows opening then wait for it to load
- function windowWatcher(subject, topic) {
- if (topic == "domwindowopened")
- runOnLoad(subject);
- }
- Services.ww.registerNotification(windowWatcher);
-
- // Make sure to stop watching for windows if we're unloading
- unload(function() Services.ww.unregisterNotification(windowWatcher));
-}
View
61 lib/listen.js
@@ -0,0 +1,61 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Home Dash Utility.
+ *
+ * The Initial Developer of the Original Code is The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Edward Lee <edilee@mozilla.com>
+ * Erik Vold <erikvvold@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+var {unload} = require("unload+");
+
+/**
+ * Helper that adds event listeners and remembers to remove on unload
+ */
+exports.listen = function listen(window, node, event, func, capture) {
+ // Default to use capture
+ if (capture == null)
+ capture = true;
+
+ node.addEventListener(event, func, capture);
+ function undoListen() {
+ node.removeEventListener(event, func, capture);
+ }
+
+ // Undo the listener on unload and provide a way to undo everything
+ let undoUnload = unload(undoListen, window);
+ return function() {
+ undoListen();
+ undoUnload();
+ };
+}
View
102 lib/unload+.js
@@ -0,0 +1,102 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Speak Words.
+ *
+ * The Initial Developer of the Original Code is The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Edward Lee <edilee@mozilla.com>
+ * Erik Vold <erikvvold@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+var unloaders = [];
+
+function unloadersUnlaod() {
+ unloaders.slice().forEach(function(unloader) unloader());
+ unloaders.length = 0;
+}
+require("unload").when(unloadersUnlaod);
+
+/**
+ * Save callbacks to run when unloading. Optionally scope the callback to a
+ * container, e.g., window. Provide a way to run all the callbacks.
+ *
+ * @usage unload(): Run all callbacks and release them.
+ *
+ * @usage unload(callback): Add a callback to run on unload.
+ * @param [function] callback: 0-parameter function to call on unload.
+ * @return [function]: A 0-parameter function that undoes adding the callback.
+ *
+ * @usage unload(callback, container) Add a scoped callback to run on unload.
+ * @param [function] callback: 0-parameter function to call on unload.
+ * @param [node] container: Remove the callback when this container unloads.
+ * @return [function]: A 0-parameter function that undoes adding the callback.
+ */
+exports.unload = function unload(callback, container) {
+ // Calling with no arguments runs all the unloader callbacks
+ if (callback == null) {
+ unloadersUnlaod();
+ return;
+ }
+
+ var remover = removeUnloader.bind(null, unloader);
+
+ // The callback is bound to the lifetime of the container if we have one
+ if (container != null) {
+ // Remove the unloader when the container unloads
+ container.addEventListener("unload", remover, false);
+
+ // Wrap the callback to additionally remove the unload listener
+ let origCallback = callback;
+ callback = function() {
+ container.removeEventListener("unload", remover, false);
+ origCallback();
+ }
+ }
+
+ // Wrap the callback in a function that ignores failures
+ function unloader() {
+ try {
+ callback();
+ }
+ catch(ex) {}
+ }
+ unloaders.push(unloader);
+
+ // Provide a way to remove the unloader
+ return remover;
+}
+
+function removeUnloader(unloader) {
+ let index = unloaders.indexOf(unloader);
+ if (index != -1)
+ unloaders.splice(index, 1);
+}
View
95 lib/window-watcher.js
@@ -0,0 +1,95 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Twitter Address Bar Search.
+ *
+ * The Initial Developer of the Original Code is The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Edward Lee <edilee@mozilla.com>
+ * Erik Vold <erikvvold@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const {Cu} = require("chrome");
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+var {unload} = require("unload+");
+
+/**
+ * Apply a callback to each open and new browser windows.
+ *
+ * @usage watchWindows(callback): Apply a callback to each browser window.
+ * @param [function] callback: 1-parameter function that gets a browser window.
+ */
+exports.watchWindows = function watchWindows(callback) {
+ var unloaded = false;
+ unload(function() unloaded = true);
+
+ // Wrap the callback in a function that ignores failures
+ function watcher(window) {
+ try {
+ // Now that the window has loaded, only handle browser windows
+ let {documentElement} = window.document;
+ if (documentElement.getAttribute("windowtype") == "navigator:browser")
+ callback(window);
+ }
+ catch(ex) {}
+ }
+
+ // Wait for the window to finish loading before running the callback
+ function runOnLoad(window) {
+ // Listen for one load event before checking the window type
+ window.addEventListener("load", function runOnce() {
+ window.removeEventListener("load", runOnce, false);
+ if (unloaded) return; // the extension has shutdown
+ watcher(window);
+ }, false);
+ }
+
+ // Add functionality to existing windows
+ let windows = Services.wm.getEnumerator(null);
+ while (windows.hasMoreElements()) {
+ // Only run the watcher immediately if the window is completely loaded
+ let window = windows.getNext();
+ if (window.document.readyState == "complete")
+ watcher(window);
+ // Wait for the window to load before continuing
+ else
+ runOnLoad(window);
+ }
+
+ // Watch for new browser windows opening then wait for it to load
+ function windowWatcher(subject, topic) {
+ if (topic == "domwindowopened")
+ runOnLoad(subject);
+ }
+ Services.ww.registerNotification(windowWatcher);
+
+ // Make sure to stop watching for windows if we're unloading
+ unload(function() Services.ww.unregisterNotification(windowWatcher));
+};
View
15 package.json
@@ -0,0 +1,15 @@
+{
+ "name": "prospector",
+ "fullName": "Prospector Modules",
+ "description": "A collection of modules used for Prospector add-ons.",
+ "keywords": ["prospector"],
+ "author": "Mozilla Labs",
+ "homepage": "https://github.com/mozilla/prospector",
+ "contributors": [
+ "Edward Lee <edilee@mozilla.com>",
+ "Erik Vold <erikvvold@gmail.com>"
+ ],
+ "version": "0.0.1a1pre",
+ "license": "MPL 1.1/GPL 2.0/LGPL 2.1",
+ "dependencies": ["addon-kit", "api-utils"]
+}
Something went wrong with that request. Please try again.