Bug 1229874: Part 3 - Enable ESLint in WebExtension code. r=billm

The base .eslintrc is essentially a merge of the root Toolkit .eslintrc and
the devtools .eslintrc, with some minor changes to match our prevalent style.

For the most enforces the coding styles that we've been using most
consistently. There are a couple of significant differences, though:

 * The rule for opening brace alignment can only be applied globally, and
   doesn't make exceptions for top-level functions. I chose to turn it on, and
   change the brace style of existing top-level functions that violated it,
   since the rule seemed worth using, and that's the direction most Toolkit JS
   code has been headed anyway.

 * The rule for switch/case statements requires an added indentation level for
   case statements. Most of our switch statements did not use an extra level
   of indentation, and I initially wrote the rule to enforce that style, until
   I came across case statements that used blocks, and required the extra
   indentation level for sanity.
This commit is contained in:
Kris Maglione 2015-12-02 16:58:53 -08:00
parent cfefdf4ef5
commit 1fb8f15cd8
65 changed files with 1312 additions and 615 deletions

View File

@ -197,7 +197,7 @@ toolkit/components/jsdownloads/src/DownloadIntegration.jsm
toolkit/components/search/nsSearchService.js
toolkit/components/url-classifier/**
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/identity/FirefoxAccounts.jsm
toolkit/identity/FirefoxAccounts.jsm
toolkit/modules/AppConstants.jsm
toolkit/modules/SessionRecorder.jsm
toolkit/mozapps/downloads/nsHelperAppDlg.js

View File

@ -0,0 +1,16 @@
{
"extends": "../../../toolkit/components/extensions/.eslintrc",
"globals": {
"currentWindow": true,
"EventEmitter": true,
"IconDetails": true,
"openPanel": true,
"makeWidgetId": true,
"TabContext": true,
"AllWindowEvents": true,
"WindowEventManager": true,
"WindowListManager": true,
"WindowManager": true,
},
}

View File

@ -1,3 +1,7 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/PlacesUtils.jsm");
@ -5,7 +9,6 @@ var Bookmarks = PlacesUtils.bookmarks;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
EventManager,
runSafe,
} = ExtensionUtils;
@ -40,11 +43,12 @@ function getTree(rootGuid, onlyChildren) {
}
return PlacesUtils.promiseBookmarksTree(rootGuid, {
excludeItemsCallback: aItem => {
if (aItem.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR)
excludeItemsCallback: item => {
if (item.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
return true;
return aItem.annos &&
aItem.annos.find(a => a.name == PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
}
return item.annos &&
item.annos.find(a => a.name == PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
},
}).then(root => {
if (onlyChildren) {
@ -168,14 +172,14 @@ extensions.registerPrivilegedAPI("bookmarks", (extension, context) => {
runSafe(context, callback, convert(result));
}
}, failure);
} catch(e) {
} catch (e) {
failure(e);
}
},
move: function(id, destination, callback) {
let info = {
guid: id
guid: id,
};
if ("parentId" in destination) {
@ -204,7 +208,7 @@ extensions.registerPrivilegedAPI("bookmarks", (extension, context) => {
update: function(id, changes, callback) {
let info = {
guid: id
guid: id,
};
if ("title" in changes) {
@ -233,7 +237,7 @@ extensions.registerPrivilegedAPI("bookmarks", (extension, context) => {
remove: function(id, callback) {
let info = {
guid: id
guid: id,
};
let failure = reason => {
@ -252,8 +256,8 @@ extensions.registerPrivilegedAPI("bookmarks", (extension, context) => {
} catch (e) {
failure(e);
}
}
}
},
},
};
});

View File

@ -10,24 +10,19 @@ Cu.import("resource://devtools/shared/event-emitter.js");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
EventManager,
DefaultWeakMap,
runSafe,
} = ExtensionUtils;
// WeakMap[Extension -> BrowserAction]
var browserActionMap = new WeakMap();
function browserActionOf(extension)
{
function browserActionOf(extension) {
return browserActionMap.get(extension);
}
var nextActionId = 0;
// Responsible for the browser_action section of the manifest as well
// as the associated popup.
function BrowserAction(options, extension)
{
function BrowserAction(options, extension) {
this.extension = extension;
this.id = makeWidgetId(extension.id) + "-browser-action";
this.widget = null;
@ -73,7 +68,7 @@ BrowserAction.prototype = {
let tabbrowser = document.defaultView.gBrowser;
node.addEventListener("command", event => {
node.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
let tab = tabbrowser.selectedTab;
let popup = this.getProperty(tab, "popup");
this.tabManager.addActiveTabPermission(tab);
@ -88,8 +83,8 @@ BrowserAction.prototype = {
},
});
this.tabContext.on("tab-select",
(evt, tab) => { this.updateWindow(tab.ownerDocument.defaultView); })
this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
(evt, tab) => { this.updateWindow(tab.ownerDocument.defaultView); });
this.widget = widget;
},
@ -124,7 +119,7 @@ BrowserAction.prototype = {
}
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
'class', 'toolbarbutton-badge');
"class", "toolbarbutton-badge");
if (badgeNode) {
let color = tabData.badgeBackgroundColor;
if (Array.isArray(color)) {
@ -190,6 +185,7 @@ BrowserAction.prototype = {
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_browser_action", (type, directive, extension, manifest) => {
let browserAction = new BrowserAction(manifest.browser_action, extension);
browserAction.build();
@ -202,6 +198,7 @@ extensions.on("shutdown", (type, extension) => {
browserActionMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerAPI((extension, context) => {
return {
@ -273,7 +270,6 @@ extensions.registerAPI((extension, context) => {
},
setBadgeBackgroundColor: function(details) {
let color = details.color;
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
browserActionOf(extension).setProperty(tab, "badgeBackgroundColor", details.color);
},
@ -283,6 +279,6 @@ extensions.registerAPI((extension, context) => {
let color = browserActionOf(extension).getProperty(tab, "badgeBackgroundColor");
runSafe(context, callback, color);
},
}
},
};
});

View File

@ -1,3 +1,7 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.import("resource://gre/modules/MatchPattern.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -5,8 +9,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var {
EventManager,
contextMenuItems,
runSafe
runSafe,
} = ExtensionUtils;
@ -21,11 +24,6 @@ var onClickedCallbacksMap = new WeakMap();
// If id is not specified for an item we use an integer.
var nextID = 0;
function contextMenuObserver(subject, topic, data) {
subject = subject.wrappedJSObject;
menuBuilder.build(subject);
}
// When a new contextMenu is opened, this function is called and
// we populate the |xulMenu| with all the items from extensions
// to be displayed. We always clear all the items again when
@ -82,7 +80,7 @@ var menuBuilder = {
topLevelItems.add(element);
}
element.addEventListener("command", event => {
element.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
item.tabManager.addActiveTabPermission();
if (item.onclick) {
let clickData = item.getClickData(contextData, event);
@ -124,6 +122,11 @@ var menuBuilder = {
},
_itemsToCleanUp: new Set(),
};
function contextMenuObserver(subject, topic, data) {
subject = subject.wrappedJSObject;
menuBuilder.build(subject);
}
function getContexts(contextData) {
@ -132,38 +135,37 @@ function getContexts(contextData) {
contexts.add("page");
if (contextData.inFrame) {
contexts.add("frame")
contexts.add("frame");
}
if (contextData.isTextSelected) {
contexts.add("selection")
contexts.add("selection");
}
if (contextData.onLink) {
contexts.add("link")
contexts.add("link");
}
if (contextData.onEditableArea) {
contexts.add("editable")
contexts.add("editable");
}
if (contextData.onImage) {
contexts.add("image")
contexts.add("image");
}
if (contextData.onVideo) {
contexts.add("video")
contexts.add("video");
}
if (contextData.onAudio) {
contexts.add("audio")
contexts.add("audio");
}
return contexts;
}
function MenuItem(extension, extContext, createProperties)
{
function MenuItem(extension, extContext, createProperties) {
this.extension = extension;
this.extContext = extContext;
@ -175,7 +177,7 @@ function MenuItem(extension, extContext, createProperties)
MenuItem.prototype = {
// init is called from the MenuItem ctor and from update. The |update|
// flag is for the later case.
init(createProperties, update=false) {
init(createProperties, update = false) {
let item = this;
// return true if the prop was set on |this|
function parseProp(propName, defaultValue = null) {
@ -214,8 +216,8 @@ MenuItem.prototype = {
let found = false;
let menuMap = contextMenuMap.get(this.extension);
if (menuMap.has(this.parentId)) {
found = true;
menuMap.get(this.parentId).isMenu = true;
found = true;
menuMap.get(this.parentId).isMenu = true;
}
if (!found) {
throw new Error("MenuItem with this parentId not found");
@ -265,7 +267,7 @@ MenuItem.prototype = {
let toRemove = new Set();
toRemove.add(this.id);
for (let [id, item] of menuMap) {
for (let [, item] of menuMap) {
if (hasAncestorWithId(item, this.id)) {
toRemove.add(item.id);
}
@ -288,7 +290,7 @@ MenuItem.prototype = {
}
let clickData = {
menuItemId: this.id
menuItemId: this.id,
};
function setIfDefined(argName, value) {
@ -318,7 +320,7 @@ MenuItem.prototype = {
for (let c of this.contexts) {
if (contexts.has(c)) {
enabled = true;
break
break;
}
}
if (!enabled) {
@ -342,6 +344,7 @@ MenuItem.prototype = {
};
var extCount = 0;
/* eslint-disable mozilla/balanced-listeners */
extensions.on("startup", (type, extension) => {
contextMenuMap.set(extension, new Map());
if (++extCount == 1) {
@ -358,6 +361,7 @@ extensions.on("shutdown", (type, extension) => {
"on-build-contextmenu");
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerPrivilegedAPI("contextMenus", (extension, context) => {
return {
@ -392,7 +396,7 @@ extensions.registerPrivilegedAPI("contextMenus", (extension, context) => {
},
removeAll: function(callback) {
for (let [id, menuItem] of contextMenuMap.get(extension)) {
for (let [, menuItem] of contextMenuMap.get(extension)) {
menuItem.remove();
}
if (callback) {

View File

@ -5,7 +5,6 @@
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
EventManager,
DefaultWeakMap,
runSafe,
} = ExtensionUtils;
@ -15,8 +14,7 @@ var pageActionMap = new WeakMap();
// Handles URL bar icons, including the |page_action| manifest entry
// and associated API.
function PageAction(options, extension)
{
function PageAction(options, extension) {
this.extension = extension;
this.id = makeWidgetId(extension.id) + "-page-action";
@ -39,7 +37,7 @@ function PageAction(options, extension)
this.tabContext = new TabContext(tab => Object.create(this.defaults),
extension);
this.tabContext.on("location-change", this.handleLocationChange.bind(this));
this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
// WeakMap[ChromeWindow -> <xul:image>]
this.buttons = new WeakMap();
@ -110,7 +108,7 @@ PageAction.prototype = {
button.id = this.id;
button.setAttribute("class", "urlbar-icon");
button.addEventListener("click", event => {
button.addEventListener("click", event => { // eslint-disable-line mozilla/balanced-listeners
if (event.button == 0) {
this.handleClick(window);
}
@ -173,6 +171,7 @@ PageAction.for = extension => {
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_page_action", (type, directive, extension, manifest) => {
let pageAction = new PageAction(manifest.page_action, extension);
pageActionMap.set(extension, pageAction);
@ -184,6 +183,7 @@ extensions.on("shutdown", (type, extension) => {
pageActionMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerAPI((extension, context) => {
@ -244,6 +244,6 @@ extensions.registerAPI((extension, context) => {
let popup = PageAction.for(extension).getProperty(tab, "popup");
runSafe(context, callback, popup);
},
}
},
};
});

View File

@ -1,3 +1,5 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
@ -10,6 +12,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
/* globals aboutNewTabService */
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
@ -20,8 +24,7 @@ var {
// This function is pretty tightly tied to Extension.jsm.
// Its job is to fill in the |tab| property of the sender.
function getSender(context, target, sender)
{
function getSender(context, target, sender) {
// The message was sent from a content script to a <browser> element.
// We can just get the |tab| from |target|.
if (target instanceof Ci.nsIDOMXULElement) {
@ -33,20 +36,19 @@ function getSender(context, target, sender)
let tab = tabbrowser.getTabForBrowser(target);
sender.tab = TabManager.convert(context.extension, tab);
} else {
} else if ("tabId" in sender) {
// The message came from an ExtensionPage. In that case, it should
// include a tabId property (which is filled in by the page-open
// listener below).
if ("tabId" in sender) {
sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId));
delete sender.tabId;
}
sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId));
delete sender.tabId;
}
}
// WeakMap[ExtensionPage -> {tab, parentWindow}]
var pageDataMap = new WeakMap();
/* eslint-disable mozilla/balanced-listeners */
// This listener fires whenever an extension page opens in a tab
// (either initiated by the extension or the user). Its job is to fill
// in some tab-specific details and keep data around about the
@ -95,15 +97,15 @@ extensions.on("fill-browser-data", (type, browser, data, result) => {
data.tabId = tabId;
});
/* eslint-enable mozilla/balanced-listeners */
global.currentWindow = function(context)
{
global.currentWindow = function(context) {
let pageData = pageDataMap.get(context);
if (pageData) {
return pageData.parentWindow;
}
return WindowManager.topWindow;
}
};
// TODO: activeTab permission
@ -211,7 +213,7 @@ extensions.registerAPI((extension, context) => {
let tabId = TabManager.getId(tab);
let [needed, changeInfo] = sanitize(extension, {
status: webProgress.isLoadingDocument ? "loading" : "complete",
url: locationURI.spec
url: locationURI.spec,
});
if (needed) {
fire(tabId, changeInfo, TabManager.convert(extension, tab));
@ -389,7 +391,7 @@ extensions.registerAPI((extension, context) => {
getAllInWindow: function(...args) {
let window, callback;
if (args.length == 1) {
callbacks = args[0];
callback = args[0];
} else {
window = WindowManager.getWindow(args[0]);
callback = args[1];
@ -435,10 +437,8 @@ extensions.registerAPI((extension, context) => {
if (currentWindow(context) != window) {
return false;
}
} else {
if (queryInfo.windowId != tab.windowId) {
return false;
}
} else if (queryInfo.windowId != tab.windowId) {
return false;
}
}
@ -495,7 +495,7 @@ extensions.registerAPI((extension, context) => {
}
if (details.code) {
options[kind + 'Code'] = details.code;
options[kind + "Code"] = details.code;
}
if (details.file) {
let url = context.uri.resolve(details.file);
@ -522,17 +522,17 @@ extensions.registerAPI((extension, context) => {
executeScript: function(...args) {
if (args.length == 1) {
self.tabs._execute(undefined, args[0], 'js', undefined);
self.tabs._execute(undefined, args[0], "js", undefined);
} else {
self.tabs._execute(args[0], args[1], 'js', args[2]);
self.tabs._execute(args[0], args[1], "js", args[2]);
}
},
insertCss: function(tabId, details, callback) {
insertCss: function(...args) {
if (args.length == 1) {
self.tabs._execute(undefined, args[0], 'css', undefined);
self.tabs._execute(undefined, args[0], "css", undefined);
} else {
self.tabs._execute(args[0], args[1], 'css', args[2]);
self.tabs._execute(args[0], args[1], "css", args[2]);
}
},

View File

@ -31,7 +31,7 @@ global.IconDetails = {
//
// If no context is specified, instead of throwing an error, this
// function simply logs a warning message.
normalize(details, extension, context=null, localize=false) {
normalize(details, extension, context = null, localize = false) {
let result = {};
try {
@ -112,14 +112,14 @@ global.IconDetails = {
canvas.getContext("2d").putImageData(imageData, 0, 0);
return canvas.toDataURL("image/png");
}
},
};
global.makeWidgetId = id => {
id = id.toLowerCase();
// FIXME: This allows for collisions.
return id.replace(/[^a-z0-9_-]/g, "_");
}
};
// Open a panel anchored to the given node, containing a browser opened
// to the given URL, owned by the given extension. If |popupURL| is not
@ -161,14 +161,16 @@ global.openPanel = (node, popupURL, extension) => {
let titleChangedListener = () => {
panel.setAttribute("aria-label", browser.contentTitle);
}
};
let context;
panel.addEventListener("popuphidden", () => {
let popuphidden = () => {
panel.removeEventListener("popuphidden", popuphidden);
browser.removeEventListener("DOMTitleChanged", titleChangedListener, true);
context.unload();
panel.remove();
});
};
panel.addEventListener("popuphidden", popuphidden);
let loadListener = () => {
panel.removeEventListener("load", loadListener);
@ -216,7 +218,7 @@ global.openPanel = (node, popupURL, extension) => {
panel.addEventListener("load", loadListener);
return panel;
}
};
// Manages tab-specific context data, and dispatching tab select events
// across all windows.
@ -230,7 +232,7 @@ global.TabContext = function TabContext(getDefaults, extension) {
AllWindowEvents.addListener("TabSelect", this);
EventEmitter.decorate(this);
}
};
TabContext.prototype = {
get(tab) {
@ -311,7 +313,6 @@ ExtensionTabManager.prototype = {
convert(tab) {
let window = tab.ownerDocument.defaultView;
let windowActive = window == WindowManager.topWindow;
let result = {
id: TabManager.getId(tab),
@ -411,16 +412,18 @@ let tabManagers = new WeakMap();
// Returns the extension-specific tab manager for the given extension, or
// creates one if it doesn't already exist.
TabManager.for = function (extension) {
TabManager.for = function(extension) {
if (!tabManagers.has(extension)) {
tabManagers.set(extension, new ExtensionTabManager(extension));
}
return tabManagers.get(extension);
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("shutdown", (type, extension) => {
tabManagers.delete(extension);
});
/* eslint-enable mozilla/balanced-listeners */
// Manages mapping between XUL windows and extension window IDs.
global.WindowManager = {
@ -489,7 +492,7 @@ global.WindowListManager = {
// Returns an iterator for all browser windows. Unless |includeIncomplete| is
// true, only fully-loaded windows are returned.
*browserWindows(includeIncomplete = false) {
* browserWindows(includeIncomplete = false) {
// The window type parameter is only available once the window's document
// element has been created. This means that, when looking for incomplete
// browser windows, we need to ignore the type entirely for windows which
@ -654,15 +657,14 @@ AllWindowEvents.openListener = AllWindowEvents.openListener.bind(AllWindowEvents
// Subclass of EventManager where we just need to call
// add/removeEventListener on each XUL window.
global.WindowEventManager = function(context, name, event, listener)
{
global.WindowEventManager = function(context, name, event, listener) {
EventManager.call(this, context, name, fire => {
let listener2 = (...args) => listener(fire, ...args);
AllWindowEvents.addListener(event, listener2);
return () => {
AllWindowEvents.removeListener(event, listener2);
}
};
});
}
};
WindowEventManager.prototype = Object.create(EventManager.prototype);

View File

@ -1,7 +1,13 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
/* globals aboutNewTabService */
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
EventManager,

View File

@ -0,0 +1,43 @@
{
"extends": "../../.eslintrc",
"globals": {
// DOM window globals
"CustomEvent": false,
"document": false,
"ImageData": false,
"MouseEvent": false,
"window": false,
"XMLHttpRequest": false,
"gBrowser": false,
"sendAsyncMessage": false,
"NetUtil": true,
"XPCOMUtils": true,
"Task": true,
"browser": false,
// Test harness globals
"add_task": false,
"BrowserTestUtils": false,
"ContentTask": false,
"EventUtils": false,
"ExtensionTestUtils": false,
"info": false,
"is": false,
"ok": false,
"registerCleanupFunction": false,
"SimpleTest": false,
"SpecialPowers": false,
"waitForFocus": false,
"clickBrowserAction": true,
"clickPageAction": true,
"CustomizableUI": true,
"focusWindow": true,
"makeWidgetId": true,
}
}

View File

@ -3,7 +3,6 @@
"use strict";
add_task(function* testTabSwitchContext() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {
@ -14,8 +13,8 @@ add_task(function* testTabSwitchContext() {
"permissions": ["tabs"],
},
background: function () {
var details = [
background: function() {
let details = [
{ "icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
"title": "Default Title",
@ -51,9 +50,10 @@ add_task(function* testTabSwitchContext() {
"badgeBackgroundColor": [0, 0xff, 0] },
];
var tabs = [];
let tabs = [];
var tests = [
let expectDefaults;
let tests = [
expect => {
browser.test.log("Initial state, expect default properties.");
expectDefaults(details[0]).then(() => {
@ -78,7 +78,7 @@ add_task(function* testTabSwitchContext() {
},
expect => {
browser.test.log("Change properties. Expect new properties.");
var tabId = tabs[1];
let tabId = tabs[1];
browser.browserAction.setIcon({ tabId, path: "2.png" });
browser.browserAction.setPopup({ tabId, popup: "2.html" });
browser.browserAction.setTitle({ tabId, title: "Title 2" });
@ -191,14 +191,14 @@ add_task(function* testTabSwitchContext() {
});
}
function expectDefaults(expecting) {
expectDefaults = expecting => {
return checkDetails(expecting);
}
};
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
var test = tests.shift();
let test = tests.shift();
test(expecting => {
// Check that the API returns the expected values, and then
@ -247,11 +247,11 @@ add_task(function* testTabSwitchContext() {
if (details.badge && details.badgeBackgroundColor) {
let badge = button.ownerDocument.getAnonymousElementByAttribute(
button, 'class', 'toolbarbutton-badge');
button, "class", "toolbarbutton-badge");
let badgeColor = window.getComputedStyle(badge).backgroundColor;
let color = details.badgeBackgroundColor;
let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`
let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
is(badgeColor, expectedColor, "badge color is correct");
}
@ -265,7 +265,7 @@ add_task(function* testTabSwitchContext() {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest")
extension.sendMessage("runNextTest");
} else {
resolve();
}

View File

@ -3,14 +3,13 @@
"use strict";
add_task(function* testDisabled() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {}
"browser_action": {},
},
background: function () {
var clicked = false;
background: function() {
let clicked = false;
browser.browserAction.onClicked.addListener(() => {
browser.test.log("Got click event");

View File

@ -8,10 +8,10 @@
add_task(function* testDetailsObjects() {
function background() {
function getImageData(color) {
var canvas = document.createElement("canvas");
let canvas = document.createElement("canvas");
canvas.width = 2;
canvas.height = 2;
var canvasContext = canvas.getContext("2d");
let canvasContext = canvas.getContext("2d");
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
canvasContext.fillStyle = color;
@ -23,12 +23,13 @@ add_task(function* testDetailsObjects() {
};
}
var imageData = {
let imageData = {
red: getImageData("red"),
green: getImageData("green"),
};
var iconDetails = [
/* eslint-disable comma-dangle, indent */
let iconDetails = [
// Only paths.
{ details: { "path": "a.png" },
resolutions: {
@ -129,18 +130,18 @@ add_task(function* testDetailsObjects() {
// Allow serializing ImageData objects for logging.
ImageData.prototype.toJSON = () => "<ImageData>";
var tabId;
let tabId;
browser.test.onMessage.addListener((msg, test) => {
if (msg != "setIcon") {
browser.test.fail("expecting 'setIcon' message");
}
var details = iconDetails[test.index];
var expectedURL = details.resolutions[test.resolution];
let details = iconDetails[test.index];
let expectedURL = details.resolutions[test.resolution];
var detailString = JSON.stringify(details);
browser.test.log(`Setting browerAction/pageAction to ${detailString} expecting URL ${expectedURL}`)
let detailString = JSON.stringify(details);
browser.test.log(`Setting browerAction/pageAction to ${detailString} expecting URL ${expectedURL}`);
browser.browserAction.setIcon(Object.assign({tabId}, details.details));
browser.pageAction.setIcon(Object.assign({tabId}, details.details));
@ -158,9 +159,9 @@ add_task(function* testDetailsObjects() {
// objects without issue. Unfortunately, |cloneInto| implements a slightly
// different algorithm than we use in web APIs, and does not handle them
// correctly.
var tests = [];
for (var [idx, icon] of iconDetails.entries()) {
for (var res of Object.keys(icon.resolutions)) {
let tests = [];
for (let [idx, icon] of iconDetails.entries()) {
for (let res of Object.keys(icon.resolutions)) {
tests.push({ index: idx, resolution: Number(res) });
}
}
@ -221,19 +222,18 @@ add_task(function* testDetailsObjects() {
});
// Test that an error is thrown when providing invalid icon sizes
add_task(function *testInvalidIconSizes() {
add_task(function* testInvalidIconSizes() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {},
"page_action": {},
},
background: function () {
background: function() {
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
var tabId = tabs[0].id;
let tabId = tabs[0].id;
for (var api of ["pageAction", "browserAction"]) {
for (let api of ["pageAction", "browserAction"]) {
// helper function to run setIcon and check if it fails
let assertSetIconThrows = function(detail, error, message) {
try {
@ -246,10 +246,10 @@ add_task(function *testInvalidIconSizes() {
} catch (e) {
browser.test.succeed("setIcon with invalid icon size");
}
}
};
// test invalid icon size inputs
for (var type of ["path", "imageData"]) {
for (let type of ["path", "imageData"]) {
assertSetIconThrows({ [type]: { "abcdef": "test.png" } });
assertSetIconThrows({ [type]: { "48px": "test.png" } });
assertSetIconThrows({ [type]: { "20.5": "test.png" } });
@ -278,7 +278,7 @@ add_task(function *testInvalidIconSizes() {
// Test that default icon details in the manifest.json file are handled
// correctly.
add_task(function *testDefaultDetails() {
add_task(function* testDefaultDetails() {
// TODO: Test localized variants.
let icons = [
"foo/bar.png",
@ -297,9 +297,9 @@ add_task(function *testDefaultDetails() {
"page_action": { "default_icon": icon },
},
background: function () {
background: function() {
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
var tabId = tabs[0].id;
let tabId = tabs[0].id;
browser.pageAction.show(tabId);
browser.test.sendMessage("ready");
@ -332,7 +332,6 @@ add_task(function *testDefaultDetails() {
// Check that attempts to load a privileged URL as an icon image fail.
add_task(function* testSecureURLsDenied() {
// Test URLs passed to setIcon.
let extension = ExtensionTestUtils.loadExtension({
@ -341,15 +340,15 @@ add_task(function* testSecureURLsDenied() {
"page_action": {},
},
background: function () {
background: function() {
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
var tabId = tabs[0].id;
let tabId = tabs[0].id;
var urls = ["chrome://browser/content/browser.xul",
let urls = ["chrome://browser/content/browser.xul",
"javascript:true"];
for (var url of urls) {
for (var api of ["pageAction", "browserAction"]) {
for (let url of urls) {
for (let api of ["pageAction", "browserAction"]) {
try {
browser[api].setIcon({tabId, path: url});

View File

@ -20,11 +20,11 @@ add_task(function* testPageActionPopup() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
"page": "data/background.html"
"page": "data/background.html",
},
"browser_action": {
"default_popup": "popup-a.html"
}
"default_popup": "popup-a.html",
},
},
files: {
@ -41,7 +41,8 @@ add_task(function* testPageActionPopup() {
"data/background.html": `<script src="background.js"></script>`,
"data/background.js": function() {
var tests = [
let sendClick;
let tests = [
() => {
sendClick({ expectEvent: false, expectPopup: "a" });
},
@ -68,11 +69,11 @@ add_task(function* testPageActionPopup() {
},
];
var expect = {};
function sendClick({ expectEvent, expectPopup }) {
let expect = {};
sendClick = ({ expectEvent, expectPopup }) => {
expect = { event: expectEvent, popup: expectPopup };
browser.test.sendMessage("send-click");
}
};
browser.runtime.onMessage.addListener(msg => {
if (expect.popup) {
@ -103,7 +104,7 @@ add_task(function* testPageActionPopup() {
}
if (tests.length) {
var test = tests.shift();
let test = tests.shift();
test();
} else {
browser.test.notifyPass("browseraction-tests-done");

View File

@ -1,9 +1,13 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {
"default_popup": "popup.html"
}
"default_popup": "popup.html",
},
},
files: {
@ -16,7 +20,7 @@ add_task(function* () {
"popup.js": function() {
browser.runtime.sendMessage("from-popup");
}
},
},
background: function() {
@ -36,7 +40,7 @@ add_task(function* () {
for (let i = 0; i < 3; i++) {
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true
cancelable: true,
});
node.dispatchEvent(evt);

View File

@ -1,14 +1,18 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["http://mochi.test/"]
"permissions": ["http://mochi.test/"],
},
background: function() {
var ports_received = 0;
var port_messages_received = 0;
let ports_received = 0;
let port_messages_received = 0;
browser.runtime.onConnect.addListener((port) => {
browser.test.assertTrue(!!port, "port1 received");
@ -19,9 +23,9 @@ add_task(function* () {
port.onMessage.addListener((msg, sender) => {
browser.test.assertEq("port message", msg, "listener1 port message received");
port_messages_received++
port_messages_received++;
browser.test.assertEq(1, port_messages_received, "1 port message received");
})
});
});
browser.runtime.onConnect.addListener((port) => {
browser.test.assertTrue(!!port, "port2 received");
@ -32,7 +36,7 @@ add_task(function* () {
port.onMessage.addListener((msg, sender) => {
browser.test.assertEq("port message", msg, "listener2 port message received");
port_messages_received++
port_messages_received++;
browser.test.assertEq(2, port_messages_received, "2 port messages received");
browser.test.notifyPass("contentscript_connect.pass");
@ -44,10 +48,10 @@ add_task(function* () {
files: {
"script.js": function() {
var port = browser.runtime.connect();
let port = browser.runtime.connect();
port.postMessage("port message");
}
}
},
},
});
yield extension.startup();

View File

@ -1,3 +1,10 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* globals content */
/* eslint-disable mozilla/no-cpows-in-tests */
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
@ -6,7 +13,7 @@ add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["contextMenus"]
"permissions": ["contextMenus"],
},
background: function() {
@ -17,27 +24,27 @@ add_task(function* () {
browser.contextMenus.create({ "contexts": ["all"], "type": "separator" });
var contexts = ["page", "selection", "image"];
for (var i = 0; i < contexts.length; i++) {
var context = contexts[i];
var title = context;
var id = browser.contextMenus.create({ "title": title, "contexts": [context], "id": "ext-" + context,
"onclick": genericOnClick });
let contexts = ["page", "selection", "image"];
for (let i = 0; i < contexts.length; i++) {
let context = contexts[i];
let title = context;
browser.contextMenus.create({ "title": title, "contexts": [context], "id": "ext-" + context,
"onclick": genericOnClick });
if (context == "selection") {
browser.contextMenus.update("ext-selection", { "title": "selection-edited" });
}
}
var parent = browser.contextMenus.create({ "title": "parent" });
var child1 = browser.contextMenus.create(
let parent = browser.contextMenus.create({ "title": "parent" });
browser.contextMenus.create(
{ "title": "child1", "parentId": parent, "onclick": genericOnClick });
var child2 = browser.contextMenus.create(
browser.contextMenus.create(
{ "title": "child2", "parentId": parent, "onclick": genericOnClick });
var parentToDel = browser.contextMenus.create({ "title": "parentToDel" });
var child1ToDel = browser.contextMenus.create(
let parentToDel = browser.contextMenus.create({ "title": "parentToDel" });
browser.contextMenus.create(
{ "title": "child1", "parentId": parentToDel, "onclick": genericOnClick });
var child2ToDel = browser.contextMenus.create(
browser.contextMenus.create(
{ "title": "child2", "parentId": parentToDel, "onclick": genericOnClick });
browser.contextMenus.remove(parentToDel);
@ -84,7 +91,7 @@ add_task(function* () {
items = top.getElementsByAttribute("label", "parent");
is(items.length, 1, "contextMenu item for parent was found (context=image)");
is(items.item(0).childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)")
is(items.item(0).childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)");
// Click on ext-image item and check the results
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");

View File

@ -1,7 +1,10 @@
function genericChecker()
{
var kind = "background";
var path = window.location.pathname;
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function genericChecker() {
let kind = "background";
let path = window.location.pathname;
if (path.indexOf("popup") != -1) {
kind = "popup";
} else if (path.indexOf("page") != -1) {
@ -11,13 +14,13 @@ function genericChecker()
browser.test.onMessage.addListener((msg, ...args) => {
if (msg == kind + "-check-current1") {
browser.tabs.query({
currentWindow: true
currentWindow: true,
}, function(tabs) {
browser.test.sendMessage("result", tabs[0].windowId);
});
} else if (msg == kind + "-check-current2") {
browser.tabs.query({
windowId: browser.windows.WINDOW_ID_CURRENT
windowId: browser.windows.WINDOW_ID_CURRENT,
}, function(tabs) {
browser.test.sendMessage("result", tabs[0].windowId);
});
@ -26,12 +29,12 @@ function genericChecker()
browser.test.sendMessage("result", window.id);
});
} else if (msg == kind + "-open-page") {
browser.tabs.create({windowId: args[0], url: chrome.runtime.getURL("page.html")});
browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("page.html")});
} else if (msg == kind + "-close-page") {
browser.tabs.query({
windowId: args[0],
}, tabs => {
var tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
let tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
browser.tabs.remove(tab.id, () => {
browser.test.sendMessage("closed");
});
@ -58,7 +61,7 @@ add_task(function* () {
"permissions": ["tabs"],
"browser_action": {
"default_popup": "popup.html"
"default_popup": "popup.html",
},
},
@ -87,7 +90,7 @@ add_task(function* () {
yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let winId1 = WindowManager.getId(win1);
let winId2 = WindowManager.getId(win2);
@ -112,7 +115,7 @@ add_task(function* () {
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true
cancelable: true,
});
node.dispatchEvent(evt);

View File

@ -1,7 +1,10 @@
function genericChecker()
{
var kind = "background";
var path = window.location.pathname;
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function genericChecker() {
let kind = "background";
let path = window.location.pathname;
if (path.indexOf("popup") != -1) {
kind = "popup";
} else if (path.indexOf("tab") != -1) {
@ -11,14 +14,14 @@ function genericChecker()
browser.test.onMessage.addListener((msg, ...args) => {
if (msg == kind + "-check-views") {
var views = browser.extension.getViews();
var counts = {
let views = browser.extension.getViews();
let counts = {
"background": 0,
"tab": 0,
"popup": 0
"popup": 0,
};
for (var i = 0; i < views.length; i++) {
var view = views[i];
for (let i = 0; i < views.length; i++) {
let view = views[i];
browser.test.assertTrue(view.kind in counts, "view type is valid");
counts[view.kind]++;
if (view.kind == "background") {
@ -28,12 +31,12 @@ function genericChecker()
}
browser.test.sendMessage("counts", counts);
} else if (msg == kind + "-open-tab") {
browser.tabs.create({windowId: args[0], url: chrome.runtime.getURL("tab.html")});
browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("tab.html")});
} else if (msg == kind + "-close-tab") {
browser.tabs.query({
windowId: args[0],
}, tabs => {
var tab = tabs.find(tab => tab.url.indexOf("tab.html") != -1);
let tab = tabs.find(tab => tab.url.indexOf("tab.html") != -1);
browser.tabs.remove(tab.id, () => {
browser.test.sendMessage("closed");
});
@ -52,7 +55,7 @@ add_task(function* () {
"permissions": ["tabs"],
"browser_action": {
"default_popup": "popup.html"
"default_popup": "popup.html",
},
},
@ -83,7 +86,7 @@ add_task(function* () {
info("started");
let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let winId1 = WindowManager.getId(win1);
let winId2 = WindowManager.getId(win2);
@ -118,7 +121,7 @@ add_task(function* () {
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true
cancelable: true,
});
node.dispatchEvent(evt);

View File

@ -3,7 +3,6 @@
"use strict";
add_task(function* testTabSwitchContext() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"page_action": {
@ -14,8 +13,8 @@ add_task(function* testTabSwitchContext() {
"permissions": ["tabs"],
},
background: function () {
var details = [
background: function() {
let details = [
{ "icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
"title": "Default Title" },
@ -27,9 +26,9 @@ add_task(function* testTabSwitchContext() {
"title": "Title 2" },
];
var tabs;
var tests;
var allTests = [
let tabs;
let tests;
let allTests = [
expect => {
browser.test.log("Initial state. No icon visible.");
expect(null);
@ -53,7 +52,7 @@ add_task(function* testTabSwitchContext() {
},
expect => {
browser.test.log("Change properties. Expect new properties.");
var tabId = tabs[1];
let tabId = tabs[1];
browser.pageAction.show(tabId);
browser.pageAction.setIcon({ tabId, path: "2.png" });
browser.pageAction.setPopup({ tabId, popup: "2.html" });
@ -112,10 +111,10 @@ add_task(function* testTabSwitchContext() {
return new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
var tabId = tabs[0].id;
let tabId = tabs[0].id;
return Promise.all([
new Promise(resolve => browser.pageAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))])
new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))]);
}).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1] });
@ -126,7 +125,7 @@ add_task(function* testTabSwitchContext() {
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
var test = tests.shift();
let test = tests.shift();
test(expecting => {
function finish() {
@ -153,16 +152,6 @@ add_task(function* testTabSwitchContext() {
});
}
browser.test.onMessage.addListener((msg) => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
function runTests() {
tabs = [];
tests = allTests.slice();
@ -174,6 +163,16 @@ add_task(function* testTabSwitchContext() {
});
}
browser.test.onMessage.addListener((msg) => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
runTests();
},
});
@ -203,7 +202,7 @@ add_task(function* testTabSwitchContext() {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest")
extension.sendMessage("runNextTest");
} else if (testNewWindows) {
testNewWindows--;
@ -212,7 +211,7 @@ add_task(function* testTabSwitchContext() {
currentWindow = window;
return focusWindow(window);
}).then(() => {
extension.sendMessage("runTests")
extension.sendMessage("runTests");
});
} else {
resolve();

View File

@ -20,11 +20,11 @@ add_task(function* testPageActionPopup() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
"page": "data/background.html"
"page": "data/background.html",
},
"page_action": {
"default_popup": "popup-a.html"
}
"default_popup": "popup-a.html",
},
},
files: {
@ -41,9 +41,10 @@ add_task(function* testPageActionPopup() {
"data/background.html": `<script src="background.js"></script>`,
"data/background.js": function() {
var tabId;
let tabId;
var tests = [
let sendClick;
let tests = [
() => {
sendClick({ expectEvent: false, expectPopup: "a" });
},
@ -70,11 +71,11 @@ add_task(function* testPageActionPopup() {
},
];
var expect = {};
function sendClick({ expectEvent, expectPopup }) {
let expect = {};
sendClick = ({ expectEvent, expectPopup }) => {
expect = { event: expectEvent, popup: expectPopup };
browser.test.sendMessage("send-click");
}
};
browser.runtime.onMessage.addListener(msg => {
if (expect.popup) {
@ -105,7 +106,7 @@ add_task(function* testPageActionPopup() {
}
if (tests.length) {
var test = tests.shift();
let test = tests.shift();
test();
} else {
browser.test.notifyPass("pageaction-tests-done");
@ -159,10 +160,6 @@ add_task(function* testPageActionPopup() {
add_task(function* testPageActionSecurity() {
const URL = "chrome://browser/content/browser.xul";
let matchURLForbidden = url => ({
message: new RegExp(`Loading extension.*Access to.*'${URL}' denied`),
});
let messages = [/Access to restricted URI denied/,
/Access to restricted URI denied/];
@ -180,9 +177,9 @@ add_task(function* testPageActionSecurity() {
"page_action": { "default_popup": URL },
},
background: function () {
background: function() {
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
var tabId = tabs[0].id;
let tabId = tabs[0].id;
browser.pageAction.show(tabId);
browser.test.sendMessage("ready");

View File

@ -26,7 +26,7 @@ add_task(function* testPageActionPopup() {
},
background: function() {
let tabId
let tabId;
browser.tabs.query({ active: true, currentWindow: true }, tabs => {
tabId = tabs[0].id;
browser.pageAction.show(tabId);
@ -53,7 +53,7 @@ add_task(function* testPageActionPopup() {
} else {
EventUtils.synthesizeMouseAtCenter(button, {}, window);
}
};
}
let promiseConsoleMessage = pattern => new Promise(resolve => {
Services.console.registerListener(function listener(msg) {

View File

@ -1,11 +1,15 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* test_simple() {
let extensionData = {
manifest: {
"name": "Simple extension test",
"version": "1.0",
"manifest_version": 2,
"description": ""
}
"description": "",
},
};
let extension = ExtensionTestUtils.loadExtension(extensionData);
@ -36,8 +40,8 @@ add_task(function* test_background() {
"name": "Simple extension test",
"version": "1.0",
"manifest_version": 2,
"description": ""
}
"description": "",
},
};
let extension = ExtensionTestUtils.loadExtension(extensionData);

View File

@ -1,19 +1,23 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
var messages_received = [];
let messages_received = [];
var tabId;
let tabId;
browser.runtime.onConnect.addListener((port) => {
browser.test.assertTrue(!!port, "tab to background port received");
browser.test.assertEq("tab-connection-name", port.name, "port name should be defined and equal to connectInfo.name")
browser.test.assertEq("tab-connection-name", port.name, "port name should be defined and equal to connectInfo.name");
browser.test.assertTrue(!!port.sender.tab, "port.sender.tab should be defined");
browser.test.assertEq(tabId, port.sender.tab.id, "port.sender.tab.id should be equal to the expected tabId");
@ -31,17 +35,16 @@ add_task(function* () {
browser.test.notifyPass("tabRuntimeConnect.pass");
}
})
});
});
browser.tabs.create({
url: "tab.html"
}, (tab) => { tabId = tab.id });
browser.tabs.create({ url: "tab.html" },
(tab) => { tabId = tab.id; });
},
files: {
"tab.js": function() {
var port = browser.runtime.connect({ name: "tab-connection-name"});
let port = browser.runtime.connect({ name: "tab-connection-name"});
port.postMessage("tab to background port message");
port.onMessage.addListener((msg) => {
port.postMessage({ tabReceived: msg });
@ -59,8 +62,8 @@ add_task(function* () {
<h1>test tab extension page</h1>
</body>
</html>
`
}
`,
},
});
yield extension.startup();

View File

@ -5,38 +5,40 @@
function* testHasNoPermission(params) {
let contentSetup = params.contentSetup || (() => Promise.resolve());
function background(contentSetup) {
browser.runtime.onMessage.addListener((msg, sender) => {
browser.test.assertEq(msg, "second script ran", "second script ran");
browser.test.notifyPass("executeScript");
});
browser.test.onMessage.addListener(msg => {
browser.test.assertEq(msg, "execute-script");
browser.tabs.query({ activeWindow: true }, tabs => {
browser.tabs.executeScript({
file: "script.js",
});
// Execute a script we know we have permissions for in the
// second tab, in the hopes that it will execute after the
// first one. This has intermittent failure written all over
// it, but it's just about the best we can do until we
// support callbacks for executeScript.
browser.tabs.executeScript(tabs[1].id, {
file: "second-script.js",
});
});
});
contentSetup().then(() => {
browser.test.sendMessage("ready");
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: params.manifest,
background: `(${function(contentSetup) {
browser.runtime.onMessage.addListener((msg, sender) => {
browser.test.assertEq(msg, "second script ran", "second script ran");
browser.test.notifyPass("executeScript");
});
browser.test.onMessage.addListener(msg => {
browser.test.assertEq(msg, "execute-script");
browser.tabs.query({ activeWindow: true }, tabs => {
browser.tabs.executeScript({
file: "script.js"
});
// Execute a script we know we have permissions for in the
// second tab, in the hopes that it will execute after the
// first one. This has intermittent failure written all over
// it, but it's just about the best we can do until we
// support callbacks for executeScript.
browser.tabs.executeScript(tabs[1].id, {
file: "second-script.js"
});
});
});
contentSetup().then(() => {
browser.test.sendMessage("ready");
});
}})(${contentSetup})`,
background: `(${background})(${contentSetup})`,
files: {
"script.js": function() {
@ -45,8 +47,8 @@ function* testHasNoPermission(params) {
"second-script.js": function() {
browser.runtime.sendMessage("second script ran");
}
}
},
},
});
yield extension.startup();
@ -68,12 +70,12 @@ add_task(function* testBadPermissions() {
info("Test no special permissions");
yield testHasNoPermission({
manifest: { "permissions": ["http://example.com/"] }
manifest: { "permissions": ["http://example.com/"] },
});
info("Test tabs permissions");
yield testHasNoPermission({
manifest: { "permissions": ["http://example.com/", "tabs"] }
manifest: { "permissions": ["http://example.com/", "tabs"] },
});
info("Test active tab, browser action, no click");
@ -97,7 +99,7 @@ add_task(function* testBadPermissions() {
resolve();
});
});
}
},
});
yield BrowserTestUtils.removeTab(tab2);

View File

@ -1,35 +1,39 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function* testHasPermission(params) {
let contentSetup = params.contentSetup || (() => Promise.resolve());
function background(contentSetup) {
browser.runtime.onMessage.addListener((msg, sender) => {
browser.test.assertEq(msg, "script ran", "script ran");
browser.test.notifyPass("executeScript");
});
browser.test.onMessage.addListener(msg => {
browser.test.assertEq(msg, "execute-script");
browser.tabs.executeScript({
file: "script.js",
});
});
contentSetup().then(() => {
browser.test.sendMessage("ready");
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: params.manifest,
background: `(${function(contentSetup) {
browser.runtime.onMessage.addListener((msg, sender) => {
browser.test.assertEq(msg, "script ran", "script ran");
browser.test.notifyPass("executeScript");
});
browser.test.onMessage.addListener(msg => {
browser.test.assertEq(msg, "execute-script");
browser.tabs.executeScript({
file: "script.js"
});
});
contentSetup().then(() => {
browser.test.sendMessage("ready");
});
}})(${contentSetup})`,
background: `(${background})(${contentSetup})`,
files: {
"script.js": function() {
browser.runtime.sendMessage("script ran");
}
}
},
},
});
yield extension.startup();
@ -50,17 +54,17 @@ add_task(function* testGoodPermissions() {
info("Test explicit host permission");
yield testHasPermission({
manifest: { "permissions": ["http://mochi.test/"] }
manifest: { "permissions": ["http://mochi.test/"] },
});
info("Test explicit host subdomain permission");
yield testHasPermission({
manifest: { "permissions": ["http://*.mochi.test/"] }
manifest: { "permissions": ["http://*.mochi.test/"] },
});
info("Test explicit <all_urls> permission");
yield testHasPermission({
manifest: { "permissions": ["<all_urls>"] }
manifest: { "permissions": ["<all_urls>"] },
});
info("Test activeTab permission with a browser action click");

View File

@ -1,3 +1,5 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {

View File

@ -1,3 +1,7 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let win1 = yield BrowserTestUtils.openNewBrowserWindow();
@ -9,49 +13,49 @@ add_task(function* () {
"content_scripts": [{
"matches": ["http://mochi.test/*/context_tabs_onUpdated_page.html"],
"js": ["content-script.js"],
"run_at": "document_start"
},],
"run_at": "document_start",
}],
},
background: function() {
var pageURL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
let pageURL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
var expectedSequence = [
let expectedSequence = [
{ status: "loading" },
{ status: "loading", url: pageURL },
{ status: "complete" }
{ status: "complete" },
];
var collectedSequence = [];
let collectedSequence = [];
browser.tabs.onUpdated.addListener(function (tabId, updatedInfo) {
browser.tabs.onUpdated.addListener(function(tabId, updatedInfo) {
collectedSequence.push(updatedInfo);
});
browser.runtime.onMessage.addListener(function () {
if (collectedSequence.length !== expectedSequence.length) {
browser.runtime.onMessage.addListener(function() {
if (collectedSequence.length !== expectedSequence.length) {
browser.test.assertEq(
JSON.stringify(expectedSequence),
JSON.stringify(collectedSequence),
"got unexpected number of updateInfo data"
);
} else {
for (let i = 0; i < expectedSequence.length; i++) {
browser.test.assertEq(
JSON.stringify(expectedSequence),
JSON.stringify(collectedSequence),
"got unexpected number of updateInfo data"
expectedSequence[i].status,
collectedSequence[i].status,
"check updatedInfo status"
);
} else {
for (var i = 0; i < expectedSequence.length; i++) {
if (expectedSequence[i].url || collectedSequence[i].url) {
browser.test.assertEq(
expectedSequence[i].status,
collectedSequence[i].status,
"check updatedInfo status"
expectedSequence[i].url,
collectedSequence[i].url,
"check updatedInfo url"
);
if (expectedSequence[i].url || collectedSequence[i].url) {
browser.test.assertEq(
expectedSequence[i].url,
collectedSequence[i].url,
"check updatedInfo url"
);
}
}
}
}
browser.test.notifyPass("tabs.onUpdated");
browser.test.notifyPass("tabs.onUpdated");
});
browser.tabs.create({ url: pageURL });
@ -64,12 +68,12 @@ add_task(function* () {
}
}, true);
`,
}
},
});
yield Promise.all([
extension.startup(),
extension.awaitFinish("tabs.onUpdated")
extension.awaitFinish("tabs.onUpdated"),
]);
yield extension.unload();
@ -84,7 +88,7 @@ function* do_test_update(background) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: background,
@ -92,7 +96,7 @@ function* do_test_update(background) {
yield Promise.all([
yield extension.startup(),
yield extension.awaitFinish("finish")
yield extension.awaitFinish("finish"),
]);
yield extension.unload();

View File

@ -1,3 +1,7 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
@ -6,16 +10,16 @@ add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
browser.tabs.query({
lastFocusedWindow: true
lastFocusedWindow: true,
}, function(tabs) {
browser.test.assertEq(tabs.length, 3, "should have three tabs");
tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
tabs.sort((tab1, tab2) => tab1.index - tab2.index);
browser.test.assertEq(tabs[0].url, "about:blank", "first tab blank");
tabs.shift();
@ -53,16 +57,16 @@ add_task(function* () {
// test simple queries
extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
browser.tabs.query({
url: "<all_urls>"
url: "<all_urls>",
}, function(tabs) {
browser.test.assertEq(tabs.length, 3, "should have three tabs");
tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
tabs.sort((tab1, tab2) => tab1.index - tab2.index);
browser.test.assertEq(tabs[0].url, "http://example.com/", "tab 0 url correct");
browser.test.assertEq(tabs[1].url, "http://example.net/", "tab 1 url correct");
@ -80,12 +84,12 @@ add_task(function* () {
// match pattern
extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
browser.tabs.query({
url: "http://*/MochiKit*"
url: "http://*/MochiKit*",
}, function(tabs) {
browser.test.assertEq(tabs.length, 1, "should have one tab");
@ -103,16 +107,16 @@ add_task(function* () {
// match array of patterns
extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
browser.tabs.query({
url: ["http://*/MochiKit*", "http://*.com/*"]
url: ["http://*/MochiKit*", "http://*.com/*"],
}, function(tabs) {
browser.test.assertEq(tabs.length, 2, "should have two tabs");
tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
tabs.sort((tab1, tab2) => tab1.index - tab2.index);
browser.test.assertEq(tabs[0].url, "http://example.com/", "tab 0 url correct");
browser.test.assertEq(tabs[1].url, "http://test1.example.org/MochiKit/", "tab 1 url correct");

View File

@ -1,30 +1,34 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
chrome.tabs.create({ url: "about:robots"}, function (tab) {
var exception;
browser.tabs.create({ url: "about:robots"}, tab => {
let exception;
try {
browser.tabs.sendMessage(tab.id, "message");
browser.tabs.sendMessage(tab.id + 100, "message");
} catch(e) {
} catch (e) {
exception = e;
}
browser.test.assertEq(undefined, exception, "no exception should be raised on tabs.sendMessage to unexistent tabs");
chrome.tabs.remove(tab.id, function() {
browser.tabs.remove(tab.id, function() {
browser.test.notifyPass("tabs.sendMessage");
})
})
});
});
},
});
yield Promise.all([
extension.startup(),
extension.awaitFinish("tabs.sendMessage")
extension.awaitFinish("tabs.sendMessage"),
]);
yield extension.unload();

View File

@ -1,3 +1,7 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
@ -6,7 +10,7 @@ add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["tabs"],
},
background: function() {
@ -15,7 +19,7 @@ add_task(function* () {
}, function(tabs) {
browser.test.assertEq(tabs.length, 3, "should have three tabs");
tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
tabs.sort((tab1, tab2) => tab1.index - tab2.index);
browser.test.assertEq(tabs[0].url, "about:blank", "first tab blank");
tabs.shift();

View File

@ -1,10 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
function promiseWaitForFocus(aWindow) {
return new Promise(function(aResolve, aReject) {
function promiseWaitForFocus(window) {
return new Promise(resolve => {
waitForFocus(function() {
ok(Services.focus.activeWindow === aWindow, "correct window focused");
aResolve();
}, aWindow);
ok(Services.focus.activeWindow === window, "correct window focused");
resolve();
}, window);
});
}
@ -16,7 +20,7 @@ add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["windows"]
"permissions": ["windows"],
},
background: function() {
@ -35,7 +39,6 @@ add_task(function* () {
browser.windows.update(wins[0].id, {focused: true}, function() {
browser.test.sendMessage("check");
});
});
},
});

View File

@ -1,13 +1,17 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported CustomizableUI makeWidgetId focusWindow clickBrowserAction clickPageAction */
var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
function makeWidgetId(id)
{
function makeWidgetId(id) {
id = id.toLowerCase();
return id.replace(/[^a-z0-9_-]/g, "_");
}
var focusWindow = Task.async(function* focusWindow(win)
{
var focusWindow = Task.async(function* focusWindow(win) {
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
if (fm.activeWindow == win) {
return;
@ -39,6 +43,7 @@ function clickPageAction(extension, win = window) {
//
// Unfortunately, that doesn't happen automatically in browser chrome
// tests.
/* globals SetPageProxyState */
SetPageProxyState("valid");
let pageActionId = makeWidgetId(extension.id) + "-page-action";

View File

@ -0,0 +1,499 @@
{
"extends": "../../.eslintrc",
"globals": {
"Cc": true,
"Ci": true,
"Components": true,
"Cr": true,
"Cu": true,
"dump": true,
"TextDecoder": false,
"TextEncoder": false,
// Specific to WebExtensions:
"extensions": true,
"global": true,
"Extension": true,
"ExtensionManagement": true,
"ExtensionPage": true,
"GlobalManager": true,
"runSafe": true,
"runSafeSync": true,
"runSafeSyncWithoutClone": true,
"Services": true,
"TabManager": true,
"XPCOMUtils": true,
},
"rules": {
// Rules from the mozilla plugin
"mozilla/balanced-listeners": 2,
"mozilla/components-imports": 1,
"mozilla/import-headjs-globals": 1,
"mozilla/mark-test-function-used": 1,
"mozilla/no-aArgs": 1,
"mozilla/no-cpows-in-tests": 1,
"mozilla/var-only-at-top-level": 1,
// Braces only needed for multi-line arrow function blocks
// "arrow-body-style": [2, "as-needed"],
// Require spacing around =>
"arrow-spacing": 2,
// Always require spacing around a single line block
"block-spacing": 1,
// Enforce one true brace style (opening brace on the same line) and avoid
// start and end braces on the same line.
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
// No space before always a space after a comma
"comma-spacing": [2, {"before": false, "after": true}],
// Commas at the end of the line not the start
"comma-style": 2,
// Don't require spaces around computed properties
"computed-property-spacing": [1, "never"],
// Functions are not required to consistently return something or nothing
"consistent-return": 0,
// Require braces around blocks that start a new line
"curly": [2, "multi-line"],
// Always require a trailing EOL
"eol-last": 2,
// Require function* name()
"generator-star-spacing": [2, {"before": false, "after": true}],
// Two space indent
"indent": [2, 2, { "SwitchCase": 1 }],
// Space after colon not before in property declarations
"key-spacing": [2, { "beforeColon": false, "afterColon": true, "mode": "minimum" }],
// Unix linebreaks
"linebreak-style": [2, "unix"],
// Always require parenthesis for new calls
"new-parens": 2,
// Use [] instead of Array()
"no-array-constructor": 2,
// No duplicate arguments in function declarations
"no-dupe-args": 2,
// No duplicate keys in object declarations
"no-dupe-keys": 2,
// No duplicate cases in switch statements
"no-duplicate-case": 2,
// No labels
// "no-labels": 2,
// If an if block ends with a return no need for an else block
// "no-else-return": 2,
// Disallow empty statements. This will report an error for:
// try { something(); } catch (e) {}
// but will not report it for:
// try { something(); } catch (e) { /* Silencing the error because ...*/ }
// which is a valid use case.
"no-empty": 2,
// No empty character classes in regex
"no-empty-character-class": 2,
// Disallow empty destructuring
"no-empty-pattern": 2,
// No assiging to exception variable
"no-ex-assign": 2,
// No using !! where casting to boolean is already happening
"no-extra-boolean-cast": 1,
// No double semicolon
"no-extra-semi": 2,
// No overwriting defined functions
"no-func-assign": 2,
// No invalid regular expresions
"no-invalid-regexp": 2,
// No odd whitespace characters
"no-irregular-whitespace": 2,
// No single if block inside an else block
"no-lonely-if": 1,
// No mixing spaces and tabs in indent
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
// Disallow use of multiple spaces (sometimes used to align const values,
// array or object items, etc.). It's hard to maintain and doesn't add that
// much benefit.
"no-multi-spaces": 1,
// No reassigning native JS objects
"no-native-reassign": 2,
// No (!foo in bar)
"no-negated-in-lhs": 2,
// Nested ternary statements are confusing
"no-nested-ternary": 2,
// Use {} instead of new Object()
"no-new-object": 2,
// No Math() or JSON()
"no-obj-calls": 2,
// No octal literals
"no-octal": 2,
// No redeclaring variables
"no-redeclare": 2,
// No unnecessary comparisons
"no-self-compare": 2,
// No declaring variables from an outer scope
"no-shadow": 1,
// No declaring variables that hide things like arguments
"no-shadow-restricted-names": 2,
// No spaces between function name and parentheses
"no-spaced-func": 1,
// No trailing whitespace
"no-trailing-spaces": 2,
// No using undeclared variables
"no-undef": 2,
// Error on newline where a semicolon is needed
"no-unexpected-multiline": 2,
// No unreachable statements
"no-unreachable": 2,
// No expressions where a statement is expected
"no-unused-expressions": 2,
// No declaring variables that are never used
"no-unused-vars": [2, { "args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$" }],
// No using variables before defined
"no-use-before-define": 2,
// No using with
"no-with": 2,
// Always require semicolon at end of statement
"semi": [2, "always"],
// Require space after keywords
"space-after-keywords": 2,
// Require space before blocks
"space-before-blocks": 2,
// Never use spaces before function parentheses
"space-before-function-paren": [2, { "anonymous": "never", "named": "never" }],
// Require spaces before finally, catch, etc.
"space-before-keywords": [2, "always"],
// No space padding in parentheses
"space-in-parens": [2, "never"],
// Require spaces around operators, except for a|0.
"space-infix-ops": [2, {"int32Hint": true}],
// Require spaces after return, throw and case
"space-return-throw-case": 2,
// ++ and -- should not need spacing
"space-unary-ops": [1, { "nonwords": false }],
// No comparisons to NaN
"use-isnan": 2,
// Only check typeof against valid results
"valid-typeof": 2,
// Disallow using variables outside the blocks they are defined (especially
// since only let and const are used, see "no-var").
"block-scoped-var": 2,
// Allow trailing commas for easy list extension. Having them does not
// impair readability, but also not required either.
"comma-dangle": [1, "always-multiline"],
// Warn about cyclomatic complexity in functions.
"complexity": 1,
// Don't warn for inconsistent naming when capturing this (not so important
// with auto-binding fat arrow functions).
// "consistent-this": [2, "self"],
// Don't require a default case in switch statements. Avoid being forced to
// add a bogus default when you know all possible cases are handled.
"default-case": 0,
// Enforce dots on the next line with property name.
"dot-location": [1, "property"],
// Encourage the use of dot notation whenever possible.
"dot-notation": 2,
// Allow using == instead of ===, in the interest of landing something since
// the devtools codebase is split on convention here.
"eqeqeq": 0,
// Don't require function expressions to have a name.
// This makes the code more verbose and hard to read. Our engine already
// does a fantastic job assigning a name to the function, which includes
// the enclosing function name, and worst case you have a line number that
// you can just look up.
"func-names": 0,
// Allow use of function declarations and expressions.
"func-style": 0,
// Don't enforce the maximum depth that blocks can be nested. The complexity
// rule is a better rule to check this.
"max-depth": 0,
// Maximum length of a line.
// Disabled because we exceed this in too many places.
"max-len": [0, 80],
// Maximum depth callbacks can be nested.
"max-nested-callbacks": [2, 4],
// Don't limit the number of parameters that can be used in a function.
"max-params": 0,
// Don't limit the maximum number of statement allowed in a function. We
// already have the complexity rule that's a better measurement.
"max-statements": 0,
// Don't require a capital letter for constructors, only check if all new
// operators are followed by a capital letter. Don't warn when capitalized
// functions are used without the new operator.
"new-cap": [0, {"capIsNew": false}],
// Allow use of bitwise operators.
"no-bitwise": 0,
// Disallow use of arguments.caller or arguments.callee.
"no-caller": 2,
// Disallow the catch clause parameter name being the same as a variable in
// the outer scope, to avoid confusion.
"no-catch-shadow": 0,
// Disallow assignment in conditional expressions.
"no-cond-assign": 2,
// Disallow using the console API.
"no-console": 2,
// Allow using constant expressions in conditions like while (true)
"no-constant-condition": 0,
// Allow use of the continue statement.
"no-continue": 0,
// Disallow control characters in regular expressions.
"no-control-regex": 2,
// Disallow use of debugger.
"no-debugger": 2,
// Disallow deletion of variables (deleting properties is fine).
"no-delete-var": 2,
// Allow division operators explicitly at beginning of regular expression.
"no-div-regex": 0,
// Disallow use of labels for anything other then loops and switches.
"no-empty-label": 2,
// Disallow use of eval(). We have other APIs to evaluate code in content.
"no-eval": 2,
// Disallow adding to native types
"no-extend-native": 2,
// Disallow unnecessary function binding.
"no-extra-bind": 2,
// Allow unnecessary parentheses, as they may make the code more readable.
"no-extra-parens": 0,
// Disallow fallthrough of case statements, except if there is a comment.
"no-fallthrough": 2,
// Allow the use of leading or trailing decimal points in numeric literals.
"no-floating-decimal": 0,
// Allow comments inline after code.
"no-inline-comments": 0,
// Disallow use of multiline strings (use template strings instead).
"no-multi-str": 1,
// Disallow multiple empty lines.
"no-multiple-empty-lines": [1, {"max": 2}],
// Allow reassignment of function parameters.
"no-param-reassign": 0,
// Allow string concatenation with __dirname and __filename (not a node env).
"no-path-concat": 0,
// Allow use of unary operators, ++ and --.
"no-plusplus": 0,
// Allow using process.env (not a node environment).
"no-process-env": 0,
// Allow using process.exit (not a node environment).
"no-process-exit": 0,
// Disallow usage of __proto__ property.
"no-proto": 2,
// Disallow multiple spaces in a regular expression literal.
"no-regex-spaces": 2,
// Allow reserved words being used as object literal keys.
"no-reserved-keys": 0,
// Don't restrict usage of specified node modules (not a node environment).
"no-restricted-modules": 0,
// Disallow use of assignment in return statement. It is preferable for a
// single line of code to have only one easily predictable effect.
"no-return-assign": 2,
// Don't warn about declaration of variables already declared in the outer scope.
"no-shadow": 0,
// Disallow shadowing of names such as arguments.
"no-shadow-restricted-names": 2,
// Allow use of synchronous methods (not a node environment).
"no-sync": 0,
// Allow the use of ternary operators.
"no-ternary": 0,
// Disallow throwing literals (eg. throw "error" instead of
// throw new Error("error")).
"no-throw-literal": 2,
// Disallow use of undeclared variables unless mentioned in a /* global */
// block. Note that globals from head.js are automatically imported in tests
// by the import-headjs-globals rule form the mozilla eslint plugin.
"no-undef": 2,
// Allow dangling underscores in identifiers (for privates).
"no-underscore-dangle": 0,
// Allow use of undefined variable.
"no-undefined": 0,
// Disallow the use of Boolean literals in conditional expressions.
"no-unneeded-ternary": 2,
// We use var-only-at-top-level instead of no-var as we allow top level
// vars.
"no-var": 0,
// Allow using TODO/FIXME comments.
"no-warning-comments": 0,
// Don't require method and property shorthand syntax for object literals.
// We use this in the code a lot, but not consistently, and this seems more
// like something to check at code review time.
"object-shorthand": 0,
// Allow more than one variable declaration per function.
"one-var": 0,
// Disallow padding within blocks.
"padded-blocks": [1, "never"],
// Don't require quotes around object literal property names.
"quote-props": 0,
// Double quotes should be used.
"quotes": [1, "double", "avoid-escape"],
// Require use of the second argument for parseInt().
"radix": 2,
// Enforce spacing after semicolons.
"semi-spacing": [2, {"before": false, "after": true}],
// Don't require to sort variables within the same declaration block.
// Anyway, one-var is disabled.
"sort-vars": 0,
// Require a space after keywords.
"space-after-keywords": [1, "always"],
// Require a space immediately following the // in a line comment.
"spaced-comment": [2, "always"],
// Require "use strict" to be defined globally in the script.
"strict": [2, "global"],
// Warn about invalid JSDoc comments.
"valid-jsdoc": 0,
// Allow vars to be declared anywhere in the scope.
"vars-on-top": 0,
// Don't require immediate function invocation to be wrapped in parentheses.
"wrap-iife": 0,
// Don't require regex literals to be wrapped in parentheses (which
// supposedly prevent them from being mistaken for division operators).
"wrap-regex": 0,
// Disallow Yoda conditions (where literal value comes first).
"yoda": 2,
// disallow use of eval()-like methods
"no-implied-eval": 2,
// Disallow function or variable declarations in nested blocks
"no-inner-declarations": 2,
// Disallow usage of __iterator__ property
"no-iterator": 2,
// Disallow labels that share a name with a variable
"no-label-var": 2,
// Disallow negation of the left operand of an in expression
"no-negated-in-lhs": 2,
// Disallow creating new instances of String, Number, and Boolean
"no-new-wrappers": 2,
}
}

View File

@ -6,6 +6,8 @@
this.EXPORTED_SYMBOLS = ["Extension", "ExtensionData"];
/* globals Extension ExtensionData */
/*
* This file is the main entry point for extensions. When an extension
* loads, its bootstrap.js file creates a Extension instance
@ -21,8 +23,9 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyModuleGetter(this, "Locale",
"resource://gre/modules/Locale.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log",
@ -67,13 +70,14 @@ var {
injectAPI,
extend,
flushJarCache,
instanceOf,
} = ExtensionUtils;
const LOGGER_ID_BASE = "addons.webextension.";
var scriptScope = this;
var ExtensionPage, GlobalManager;
// This object loads the ext-*.js scripts that define the extension API.
var Management = {
initialized: false,
@ -165,7 +169,7 @@ var Management = {
off(hook, callback) {
this.emitter.off(hook, callback);
}
},
};
// A MessageBroker that's used to send and receive messages for
@ -183,9 +187,8 @@ var globalBroker = new MessageBroker([Services.mm, Services.ppmm]);
// |uri| is the URI of the content (optional).
// |docShell| is the docshell the content runs in (optional).
// |incognito| is the content running in a private context (default: false).
function ExtensionPage(extension, params)
{
let {type, contentWindow, uri, docShell} = params;
ExtensionPage = function(extension, params) {
let {type, contentWindow, uri} = params;
this.extension = extension;
this.type = type;
this.contentWindow = contentWindow || null;
@ -206,7 +209,7 @@ function ExtensionPage(extension, params)
this.messenger = new Messenger(this, globalBroker, sender, filter, delegate);
this.extension.views.add(this);
}
};
ExtensionPage.prototype = {
get cloneScope() {
@ -264,7 +267,7 @@ ExtensionPage.prototype = {
};
// Responsible for loading extension APIs into the right globals.
var GlobalManager = {
GlobalManager = {
// Number of extensions currently enabled.
count: 0,
@ -365,8 +368,7 @@ var GlobalManager = {
//
// No functionality of this class is guaranteed to work before
// |readManifest| has been called, and completed.
this.ExtensionData = function(rootURI)
{
this.ExtensionData = function(rootURI) {
this.rootURI = rootURI;
this.manifest = null;
@ -375,7 +377,7 @@ this.ExtensionData = function(rootURI)
this._promiseLocales = null;
this.errors = [];
}
};
ExtensionData.prototype = {
get logger() {
@ -406,11 +408,12 @@ ExtensionData.prototype = {
yield iter.forEach(entry => {
results.push(entry);
});
} catch (e) {}
} catch (e) {
// Always return a list, even if the directory does not exist (or is
// not a directory) for symmetry with the ZipReader behavior.
}
iter.close();
// Always return a list, even if the directory does not exist (or is
// not a directory) for symmetry with the ZipReader behavior.
return results;
}
@ -488,7 +491,9 @@ ExtensionData.prototype = {
try {
this.id = this.manifest.applications.gecko.id;
} catch (e) {}
} catch (e) {
// Errors are handled by the type check below.
}
if (typeof this.id != "string") {
this.manifestError("Missing required `applications.gecko.id` property");
@ -626,8 +631,7 @@ ExtensionData.prototype = {
// installed (by trying to load a moz-extension URI referring to a
// web_accessible_resource from the extension). getExtensionUUID
// returns the UUID for a given add-on ID.
function getExtensionUUID(id)
{
function getExtensionUUID(id) {
const PREF_NAME = "extensions.webextensions.uuids";
let pref = Preferences.get(PREF_NAME, "{}");
@ -653,8 +657,7 @@ function getExtensionUUID(id)
// We create one instance of this class per extension. |addonData|
// comes directly from bootstrap.js when initializing.
this.Extension = function(addonData)
{
this.Extension = function(addonData) {
ExtensionData.call(this, addonData.resourceURI);
this.uuid = getExtensionUUID(addonData.id);
@ -683,7 +686,7 @@ this.Extension = function(addonData)
this.webAccessibleResources = new Set();
this.emitter = new EventEmitter();
}
};
/**
* This code is designed to make it easy to test a WebExtension
@ -710,8 +713,7 @@ this.Extension = function(addonData)
* The generated extension is stored in the system temporary directory,
* and an nsIFile object pointing to it is returned.
*/
this.Extension.generateXPI = function(id, data)
{
this.Extension.generateXPI = function(id, data) {
let manifest = data.manifest;
if (!manifest) {
manifest = {};
@ -799,8 +801,7 @@ this.Extension.generateXPI = function(id, data)
* Generates a new extension using |Extension.generateXPI|, and initializes a
* new |Extension| instance which will execute it.
*/
this.Extension.generate = function(id, data)
{
this.Extension.generate = function(id, data) {
let file = this.generateXPI(id, data);
flushJarCache(file);
@ -812,7 +813,7 @@ this.Extension.generate = function(id, data)
return new Extension({
id,
resourceURI: jarURI,
cleanupFile: file
cleanupFile: file,
});
};
@ -856,7 +857,7 @@ Extension.prototype = extend(Object.create(ExtensionData.prototype), {
manifest: this.manifest,
resourceURL: this.addonData.resourceURI.spec,
baseURL: this.baseURI.spec,
content_scripts: this.manifest.content_scripts || [],
content_scripts: this.manifest.content_scripts || [], // eslint-disable-line camelcase
webAccessibleResources: this.webAccessibleResources,
whiteListedHosts: this.whiteListedHosts.serialize(),
localeData: this.localeData.serialize(),

View File

@ -6,6 +6,8 @@
this.EXPORTED_SYMBOLS = ["ExtensionContent"];
/* globals ExtensionContent */
/*
* This file handles the content process side of extensions. It mainly
* takes care of content script injection, content script APIs, and
@ -38,8 +40,7 @@ var {
flushJarCache,
} = ExtensionUtils;
function isWhenBeforeOrSame(when1, when2)
{
function isWhenBeforeOrSame(when1, when2) {
let table = {"document_start": 0,
"document_end": 1,
"document_idle": 2};
@ -48,63 +49,65 @@ function isWhenBeforeOrSame(when1, when2)
// This is the fairly simple API that we inject into content
// scripts.
var api = context => { return {
runtime: {
connect: function(extensionId, connectInfo) {
if (!connectInfo) {
connectInfo = extensionId;
extensionId = null;
}
let name = connectInfo && connectInfo.name || "";
let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
return context.messenger.connect(context.messageManager, name, recipient);
var api = context => {
return {
runtime: {
connect: function(extensionId, connectInfo) {
if (!connectInfo) {
connectInfo = extensionId;
extensionId = null;
}
let name = connectInfo && connectInfo.name || "";
let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
return context.messenger.connect(context.messageManager, name, recipient);
},
getManifest: function() {
return Cu.cloneInto(context.extension.manifest, context.cloneScope);
},
getURL: function(url) {
return context.extension.baseURI.resolve(url);
},
onConnect: context.messenger.onConnect("runtime.onConnect"),
onMessage: context.messenger.onMessage("runtime.onMessage"),
sendMessage: function(...args) {
let options; // eslint-disable-line no-unused-vars
let extensionId, message, responseCallback;
if (args.length == 1) {
message = args[0];
} else if (args.length == 2) {
[message, responseCallback] = args;
} else {
[extensionId, message, options, responseCallback] = args;
}
let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
},
},
getManifest: function() {
return Cu.cloneInto(context.extension.manifest, context.cloneScope);
extension: {
getURL: function(url) {
return context.extension.baseURI.resolve(url);
},
inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow),
},
getURL: function(url) {
return context.extension.baseURI.resolve(url);
i18n: {
getMessage: function(messageName, substitutions) {
return context.extension.localizeMessage(messageName, substitutions);
},
},
onConnect: context.messenger.onConnect("runtime.onConnect"),
onMessage: context.messenger.onMessage("runtime.onMessage"),
sendMessage: function(...args) {
let extensionId, message, options, responseCallback;
if (args.length == 1) {
message = args[0];
} else if (args.length == 2) {
[message, responseCallback] = args;
} else {
[extensionId, message, options, responseCallback] = args;
}
let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
},
},
extension: {
getURL: function(url) {
return context.extension.baseURI.resolve(url);
},
inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow),
},
i18n: {
getMessage: function(messageName, substitutions) {
return context.extension.localizeMessage(messageName, substitutions);
},
},
}};
};
};
// Represents a content script.
function Script(options)
{
function Script(options) {
this.options = options;
this.run_at = this.options.run_at;
this.js = this.options.js || [];
@ -155,8 +158,8 @@ Script.prototype = {
}
if (shouldRun("document_start")) {
let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
for (let url of this.css) {
url = extension.baseURI.resolve(url);
@ -183,8 +186,8 @@ Script.prototype = {
let options = {
target: sandbox,
charset: "UTF-8",
async: AppConstants.platform == "gonk"
}
async: AppConstants.platform == "gonk",
};
runSafeSyncWithoutClone(Services.scriptloader.loadSubScriptWithOptions, url, options);
}
@ -195,8 +198,7 @@ Script.prototype = {
},
};
function getWindowMessageManager(contentWindow)
{
function getWindowMessageManager(contentWindow) {
let ir = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor);
@ -208,11 +210,12 @@ function getWindowMessageManager(contentWindow)
}
}
var ExtensionManager;
// Scope in which extension content script code can run. It uses
// Cu.Sandbox to run the code. There is a separate scope for each
// frame.
function ExtensionContext(extensionId, contentWindow)
{
function ExtensionContext(extensionId, contentWindow) {
this.extension = ExtensionManager.get(extensionId);
this.extensionId = extensionId;
this.contentWindow = contentWindow;
@ -239,7 +242,7 @@ function ExtensionContext(extensionId, contentWindow)
let delegate = {
getSender(context, target, sender) {
// Nothing to do here.
}
},
};
let url = contentWindow.location.href;
@ -280,8 +283,7 @@ ExtensionContext.prototype = {
},
};
function windowId(window)
{
function windowId(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
@ -332,8 +334,10 @@ var DocumentManager = {
}
this.trigger("document_start", window);
/* eslint-disable mozilla/balanced-listeners */
window.addEventListener("DOMContentLoaded", this, true);
window.addEventListener("load", this, true);
/* eslint-enable mozilla/balanced-listeners */
} else if (topic == "inner-window-destroyed") {
let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (!this.windows.has(id)) {
@ -341,7 +345,7 @@ var DocumentManager = {
}
let extensions = this.windows.get(id);
for (let [extensionId, context] of extensions) {
for (let [, context] of extensions) {
context.close();
}
@ -385,7 +389,7 @@ var DocumentManager = {
enumerateWindows: function*(docShell) {
let window = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.getInterface(Ci.nsIDOMWindow);
yield [window, this.getWindowState(window)];
for (let i = 0; i < docShell.childCount; i++) {
@ -430,7 +434,7 @@ var DocumentManager = {
},
shutdownExtension(extensionId) {
for (let [windowId, extensions] of this.windows) {
for (let [, extensions] of this.windows) {
let context = extensions.get(extensionId);
if (context) {
context.close();
@ -458,8 +462,7 @@ var DocumentManager = {
};
// Represents a browser extension in the content process.
function BrowserExtensionContent(data)
{
function BrowserExtensionContent(data) {
this.id = data.id;
this.uuid = data.uuid;
this.data = data;
@ -478,7 +481,7 @@ function BrowserExtensionContent(data)
// Extension.jsm takes care of this in the parent.
ExtensionManagement.startupExtension(this.uuid, uri, this);
}
};
}
BrowserExtensionContent.prototype = {
shutdown() {
@ -496,7 +499,7 @@ BrowserExtensionContent.prototype = {
},
};
var ExtensionManager = {
ExtensionManager = {
// Map[extensionId, BrowserExtensionContent]
extensions: new Map(),
@ -546,7 +549,7 @@ var ExtensionManager = {
break;
}
}
}
},
};
this.ExtensionContent = {
@ -581,11 +584,11 @@ this.ExtensionContent = {
receiveMessage({target, name, data}) {
switch (name) {
case "Extension:Execute":
let script = new Script(data.options);
let {extensionId} = data;
DocumentManager.executeScript(target, extensionId, script);
break;
case "Extension:Execute":
let script = new Script(data.options);
let {extensionId} = data;
DocumentManager.executeScript(target, extensionId, script);
break;
}
},
};

View File

@ -70,15 +70,15 @@ var Frames = {
receiveMessage({name, data}) {
switch (name) {
case "Extension:TopWindowID":
// FIXME: Need to handle the case where the content process
// crashes. Right now we leak its top window IDs.
this.topWindowIds.add(data.windowId);
break;
case "Extension:TopWindowID":
// FIXME: Need to handle the case where the content process
// crashes. Right now we leak its top window IDs.
this.topWindowIds.add(data.windowId);
break;
case "Extension:RemoveTopWindowID":
this.topWindowIds.delete(data.windowId);
break;
case "Extension:RemoveTopWindowID":
this.topWindowIds.delete(data.windowId);
break;
}
},
};
@ -161,7 +161,7 @@ var Service = {
}
let path = uri.path;
if (path.length > 0 && path[0] == '/') {
if (path.length > 0 && path[0] == "/") {
path = path.substr(1);
}
return extension.webAccessibleResources.has(path);

View File

@ -13,9 +13,11 @@ const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/osfile.jsm")
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
/* globals OS ExtensionStorage */
var Path = OS.Path;
var profileDir = OS.Constants.Path.profileDir;

View File

@ -18,8 +18,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Locale",
"resource://gre/modules/Locale.jsm");
// Run a function and report exceptions.
function runSafeSyncWithoutClone(f, ...args)
{
function runSafeSyncWithoutClone(f, ...args) {
try {
return f(...args);
} catch (e) {
@ -29,8 +28,7 @@ function runSafeSyncWithoutClone(f, ...args)
}
// Run a function and report exceptions.
function runSafeWithoutClone(f, ...args)
{
function runSafeWithoutClone(f, ...args) {
if (typeof(f) != "function") {
dump(`Extension error: expected function\n${Error().stack}`);
return;
@ -43,8 +41,7 @@ function runSafeWithoutClone(f, ...args)
// Run a function, cloning arguments into context.cloneScope, and
// report exceptions. |f| is expected to be in context.cloneScope.
function runSafeSync(context, f, ...args)
{
function runSafeSync(context, f, ...args) {
try {
args = Cu.cloneInto(args, context.cloneScope);
} catch (e) {
@ -56,8 +53,7 @@ function runSafeSync(context, f, ...args)
// Run a function, cloning arguments into context.cloneScope, and
// report exceptions. |f| is expected to be in context.cloneScope.
function runSafe(context, f, ...args)
{
function runSafe(context, f, ...args) {
try {
args = Cu.cloneInto(args, context.cloneScope);
} catch (e) {
@ -90,8 +86,7 @@ function extend(obj, ...args) {
// Similar to a WeakMap, but returns a particular default value for
// |get| if a key is not present.
function DefaultWeakMap(defaultValue)
{
function DefaultWeakMap(defaultValue) {
this.defaultValue = defaultValue;
this.weakmap = new WeakMap();
}
@ -123,7 +118,7 @@ function LocaleData(data) {
// Contains a key for each loaded locale, each of which is a
// Map of message keys to their localized strings.
this.messages = data.messages || new Map();
};
}
LocaleData.prototype = {
// Representation of the object to send to content processes. This
@ -151,7 +146,7 @@ LocaleData.prototype = {
for (let locale of locales) {
let messages = this.messages.get(locale);
if (messages.has(message)) {
let str = messages.get(message)
let str = messages.get(message);
if (!Array.isArray(substitutions)) {
substitutions = [substitutions];
@ -162,7 +157,7 @@ LocaleData.prototype = {
// This is not quite Chrome-compatible. Chrome consumes any number
// of digits following the $, but only accepts 9 substitutions. We
// accept any number of substitutions.
index = parseInt(index) - 1;
index = parseInt(index, 10) - 1;
return index in substitutions ? substitutions[index] : "";
} else {
// For any series of contiguous `$`s, the first is dropped, and
@ -301,8 +296,7 @@ LocaleData.prototype = {
// to register the listener. |register| is only called once, event if
// multiple listeners are registered. |register| should return an
// unregister function that will unregister the listener.
function EventManager(context, name, register)
{
function EventManager(context, name, register) {
this.context = context;
this.name = name;
this.register = register;
@ -375,8 +369,7 @@ EventManager.prototype = {
// Similar to EventManager, but it doesn't try to consolidate event
// notifications. Each addListener call causes us to register once. It
// allows extra arguments to be passed to addListener.
function SingletonEventManager(context, name, register)
{
function SingletonEventManager(context, name, register) {
this.context = context;
this.name = name;
this.register = register;
@ -397,7 +390,7 @@ SingletonEventManager.prototype = {
let unregister = this.unregister.get(callback);
this.unregister.delete(callback);
this.unregister();
unregister();
},
hasListener(callback) {
@ -420,8 +413,7 @@ SingletonEventManager.prototype = {
};
// Simple API for event listeners where events never fire.
function ignoreEvent(context, name)
{
function ignoreEvent(context, name) {
return {
addListener: function(callback) {
let id = context.extension.id;
@ -435,7 +427,7 @@ function ignoreEvent(context, name)
frame.lineNumber, frame.columnNumber,
Ci.nsIScriptError.warningFlag,
"content javascript", winID);
let consoleService = Cc['@mozilla.org/consoleservice;1']
let consoleService = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
consoleService.logMessage(scriptError);
},
@ -445,11 +437,10 @@ function ignoreEvent(context, name)
}
// Copy an API object from |source| into the scope |dest|.
function injectAPI(source, dest)
{
function injectAPI(source, dest) {
for (let prop in source) {
// Skip names prefixed with '_'.
if (prop[0] == '_') {
if (prop[0] == "_") {
continue;
}
@ -484,8 +475,7 @@ var MESSAGES = [
// messages that have a certain value for a particular property in the
// recipient. (If a message doesn't specify the given property, it's
// considered a match.)
function MessageBroker(messageManagers)
{
function MessageBroker(messageManagers) {
this.messageManagers = messageManagers;
for (let mm of this.messageManagers) {
for (let message of MESSAGES) {
@ -516,7 +506,6 @@ MessageBroker.prototype = {
},
removeListener(type, listener) {
let index = -1;
for (let i = 0; i < this.listeners[type].length; i++) {
if (this.listeners[type][i].listener == listener) {
this.listeners[type].splice(i, 1);
@ -550,13 +539,13 @@ MessageBroker.prototype = {
receiveMessage({name, data, target}) {
switch (name) {
case "Extension:Message":
this.runListeners("message", target, data);
break;
case "Extension:Message":
this.runListeners("message", target, data);
break;
case "Extension:Connect":
this.runListeners("connect", target, data);
break;
case "Extension:Connect":
this.runListeners("connect", target, data);
break;
}
},
@ -568,8 +557,7 @@ MessageBroker.prototype = {
};
// Abstraction for a Port object in the extension API. Each port has a unique ID.
function Port(context, messageManager, name, id, sender)
{
function Port(context, messageManager, name, id, sender) {
this.context = context;
this.messageManager = messageManager;
this.name = name;
@ -597,7 +585,7 @@ Port.prototype = {
},
postMessage: json => {
if (this.disconnected) {
throw "Attempt to postMessage on disconnected port";
throw new this.context.contentWindow.Error("Attempt to postMessage on disconnected port");
}
this.messageManager.sendAsyncMessage(this.listenerName, json);
},
@ -657,7 +645,7 @@ Port.prototype = {
disconnect() {
if (this.disconnected) {
throw "Attempt to disconnect() a disconnected port";
throw new this.context.contentWindow.Error("Attempt to disconnect() a disconnected port");
}
this.handleDisconnection();
this.messageManager.sendAsyncMessage(this.disconnectName);
@ -668,8 +656,7 @@ Port.prototype = {
},
};
function getMessageManager(target)
{
function getMessageManager(target) {
if (target instanceof Ci.nsIDOMXULElement) {
return target.messageManager;
} else {
@ -687,8 +674,7 @@ function getMessageManager(target)
// |delegate| is an object that must implement a few methods:
// getSender(context, messageManagerTarget, sender): returns a MessageSender
// See https://developer.chrome.com/extensions/runtime#type-MessageSender.
function Messenger(context, broker, sender, filter, delegate)
{
function Messenger(context, broker, sender, filter, delegate) {
this.context = context;
this.broker = broker;
this.sender = sender;
@ -716,7 +702,7 @@ Messenger.prototype = {
onClose = {
close() {
messageManager.removeMessageListener(replyName, listener);
}
},
};
if (responseCallback) {
messageManager.addMessageListener(replyName, listener);
@ -790,8 +776,7 @@ Messenger.prototype = {
},
};
function flushJarCache(jarFile)
{
function flushJarCache(jarFile) {
Services.obs.notifyObservers(jarFile, "flush-cache-entry", null);
}

View File

@ -1,3 +1,5 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
@ -13,8 +15,7 @@ var alarmsMap = new WeakMap();
var alarmCallbacksMap = new WeakMap();
// Manages an alarm created by the extension (alarms API).
function Alarm(extension, name, alarmInfo)
{
function Alarm(extension, name, alarmInfo) {
this.extension = extension;
this.name = name;
this.when = alarmInfo.when;
@ -75,6 +76,7 @@ Alarm.prototype = {
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("startup", (type, extension) => {
alarmsMap.set(extension, new Set());
alarmCallbacksMap.set(extension, new Set());
@ -87,6 +89,7 @@ extensions.on("shutdown", (type, extension) => {
alarmsMap.delete(extension);
alarmCallbacksMap.delete(extension);
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerAPI((extension, context) => {
return {
@ -121,7 +124,7 @@ extensions.registerAPI((extension, context) => {
getAll: function(callback) {
let alarms = alarmsMap.get(extension);
result = alarms.map(alarm => alarm.data);
let result = alarms.map(alarm => alarm.data);
runSafe(context, callback, result);
},

View File

@ -1,3 +1,5 @@
"use strict";
var { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
@ -6,8 +8,7 @@ Cu.import("resource://gre/modules/Services.jsm");
var backgroundPagesMap = new WeakMap();
// Responsible for the background_page section of the manifest.
function BackgroundPage(options, extension)
{
function BackgroundPage(options, extension) {
this.extension = extension;
this.scripts = options.scripts || [];
this.page = options.page || null;
@ -90,6 +91,7 @@ BackgroundPage.prototype = {
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_background", (type, directive, extension, manifest) => {
let bgPage = new BackgroundPage(manifest.background, extension);
bgPage.build();
@ -102,6 +104,7 @@ extensions.on("shutdown", (type, extension) => {
backgroundPagesMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerAPI((extension, context) => {
return {

View File

@ -1,3 +1,5 @@
"use strict";
const { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
@ -18,8 +20,8 @@ function convert(cookie) {
secure: cookie.isSecure,
httpOnly: cookie.isHttpOnly,
session: cookie.isSession,
storeId: DEFAULT_STORE
}
storeId: DEFAULT_STORE,
};
if (!cookie.isSession) {
result.expirationDate = cookie.expiry;
@ -80,7 +82,7 @@ function* query(allDetails, allowed) {
// "Restricts the retrieved cookies to those that would match the given URL."
if ("url" in details) {
var uri = Services.io.newURI(details.url, null, null);
let uri = Services.io.newURI(details.url, null, null);
if (!domainMatches(uri.host)) {
return false;
@ -222,7 +224,7 @@ extensions.registerPrivilegedAPI("cookies", (extension, context) => {
runSafe(context, callback, {
url: details.url,
name: details.name,
storeId: DEFAULT_STORE
storeId: DEFAULT_STORE,
});
}
// Todo: could there be multiple per subdomain?
@ -243,7 +245,7 @@ extensions.registerPrivilegedAPI("cookies", (extension, context) => {
let observer = (subject, topic, data) => {
let notify = (removed, cookie, cause) => {
fire({removed, cookie: convert(cookie.QueryInterface(Ci.nsICookie2)), cause});
}
};
// We do our best effort here to map the incompatible states.
switch (data) {

View File

@ -1,3 +1,5 @@
"use strict";
extensions.registerAPI((extension, context) => {
return {
extension: {
@ -29,7 +31,7 @@ extensions.registerAPI((extension, context) => {
get inIncognitoContext() {
return context.incognito;
}
},
},
};
});

View File

@ -1,3 +1,5 @@
"use strict";
extensions.registerAPI((extension, context) => {
return {
i18n: {

View File

@ -1,3 +1,5 @@
"use strict";
extensions.registerPrivilegedAPI("idle", (extension, context) => {
return {
idle: {

View File

@ -1,3 +1,5 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
@ -14,8 +16,7 @@ var notificationsMap = new WeakMap();
var notificationCallbacksMap = new WeakMap();
// Manages a notification popup (notifications API) created by the extension.
function Notification(extension, id, options)
{
function Notification(extension, id, options) {
this.extension = extension;
this.id = id;
this.options = options;
@ -63,6 +64,7 @@ Notification.prototype = {
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("startup", (type, extension) => {
notificationsMap.set(extension, new Set());
notificationCallbacksMap.set(extension, new Set());
@ -75,6 +77,7 @@ extensions.on("shutdown", (type, extension) => {
notificationsMap.delete(extension);
notificationCallbacksMap.delete(extension);
});
/* eslint-enable mozilla/balanced-listeners */
var nextId = 0;

View File

@ -1,3 +1,5 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -58,7 +60,8 @@ extensions.registerAPI((extension, context) => {
},
sendMessage: function(...args) {
let extensionId, message, options, responseCallback;
let options; // eslint-disable-line no-unused-vars
let extensionId, message, responseCallback;
if (args.length == 1) {
message = args[0];
} else if (args.length == 2) {
@ -87,7 +90,7 @@ extensions.registerAPI((extension, context) => {
}
let abi = Services.appinfo.XPCOMABI;
let [arch, compiler] = abi.split("-");
let [arch] = abi.split("-");
if (arch == "x86") {
arch = "x86-32";
} else if (arch == "x86_64") {

View File

@ -1,3 +1,5 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",

View File

@ -1,3 +1,5 @@
"use strict";
Components.utils.import("resource://gre/modules/ExtensionUtils.jsm");
var {
EventManager,
@ -6,6 +8,7 @@ var {
// WeakMap[Extension -> Set(callback)]
var messageHandlers = new WeakMap();
/* eslint-disable mozilla/balanced-listeners */
extensions.on("startup", (type, extension) => {
messageHandlers.set(extension, new Set());
});
@ -20,6 +23,7 @@ extensions.on("test-message", (type, extension, ...args) => {
handler(...args);
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerAPI((extension, context) => {
return {
@ -49,11 +53,11 @@ extensions.registerAPI((extension, context) => {
},
assertTrue: function(value, msg) {
extension.emit("test-result", value ? true : false, msg);
extension.emit("test-result", Boolean(value), msg);
},
assertFalse: function(value, msg) {
extension.emit("test-result", !value ? true : false, msg);
extension.emit("test-result", !value, msg);
},
assertEq: function(expected, actual, msg) {

View File

@ -1,3 +1,5 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -17,8 +19,7 @@ var {
} = ExtensionUtils;
// Similar to WebRequestEventManager but for WebNavigation.
function WebNavigationEventManager(context, eventName)
{
function WebNavigationEventManager(context, eventName) {
let name = `webNavigation.${eventName}`;
let register = callback => {
let listener = data => {

View File

@ -1,3 +1,5 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -17,8 +19,7 @@ var {
// EventManager-like class specifically for WebRequest. Inherits from
// SingletonEventManager. Takes care of converting |details| parameter
// when invoking listeners.
function WebRequestEventManager(context, eventName)
{
function WebRequestEventManager(context, eventName) {
let name = `webRequest.${eventName}`;
let register = (callback, filter, info) => {
let listener = data => {

View File

@ -0,0 +1,19 @@
{
"extends": "../../.eslintrc",
"globals": {
// DOM window globals
"window": false,
"XMLHttpRequest": false,
"sendAsyncMessage": false,
"NetUtil": true,
"XPCOMUtils": true,
// Test harness globals
"add_task": false,
"ok": false,
"SimpleTest": false,
}
}

View File

@ -1 +1,3 @@
"use strict";
window.failure = true;

View File

@ -1 +1,3 @@
"use strict";
window.success = window.success ? window.success + 1 : 1;

View File

@ -1,2 +1,4 @@
"use strict";
window.failure = true;

View File

@ -1,3 +1,5 @@
"use strict";
var request = new XMLHttpRequest();
request.open("get", "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/xhr_resource", false);
request.send();

View File

@ -1,3 +1,7 @@
"use strict";
/* exported waitForLoad */
function waitForLoad(win) {
return new Promise(resolve => {
win.addEventListener("load", function listener() {

View File

@ -0,0 +1,15 @@
{
"extends": "../../.eslintrc",
"globals": {
"NetUtil": true,
"XPCOMUtils": true,
// Test harness globals
"add_task": false,
"Assert": false,
"do_register_cleanup": false,
"equal": false,
"ok": false,
}
}

View File

@ -0,0 +1,12 @@
{
"extends": "../../components/extensions/.eslintrc",
"globals": {
"addEventListener": false,
"addMessageListener": false,
"removeEventListener": false,
"sendAsyncMessage": false,
"initialProcessData": true,
},
}

View File

@ -8,12 +8,13 @@ const Cu = Components.utils;
this.EXPORTED_SYMBOLS = ["MatchPattern"];
/* globals MatchPattern */
const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "app"];
// This function converts a glob pattern (containing * and possibly ?
// as wildcards) to a regular expression.
function globToRegexp(pat, allowQuestion)
{
function globToRegexp(pat, allowQuestion) {
// Escape everything except ? and *.
pat = pat.replace(/[.+^${}()|[\]\\]/g, "\\$&");
@ -28,12 +29,11 @@ function globToRegexp(pat, allowQuestion)
// These patterns follow the syntax in
// https://developer.chrome.com/extensions/match_patterns
function SingleMatchPattern(pat)
{
function SingleMatchPattern(pat) {
if (pat == "<all_urls>") {
this.scheme = PERMITTED_SCHEMES;
this.host = "*";
this.path = new RegExp('.*');
this.path = new RegExp(".*");
} else if (!pat) {
this.scheme = [];
} else {
@ -45,7 +45,7 @@ function SingleMatchPattern(pat)
return;
}
if (match[1] == '*') {
if (match[1] == "*") {
this.scheme = ["http", "https"];
} else {
this.scheme = [match[1]];
@ -69,18 +69,16 @@ SingleMatchPattern.prototype = {
}
// This code ignores the port, as Chrome does.
if (this.host == '*') {
if (this.host == "*") {
// Don't check anything.
} else if (this.host[0] == '*') {
} else if (this.host[0] == "*") {
// It must be *.foo. We also need to allow foo by itself.
let suffix = this.host.substr(2);
if (uri.host != suffix && !uri.host.endsWith("." + suffix)) {
return false;
}
} else {
if (this.host != uri.host) {
return false;
}
} else if (this.host != uri.host) {
return false;
}
if (!ignorePath && !this.path.test(uri.path)) {
@ -88,11 +86,10 @@ SingleMatchPattern.prototype = {
}
return true;
}
},
};
this.MatchPattern = function(pat)
{
this.MatchPattern = function(pat) {
this.pat = pat;
if (!pat) {
this.matchers = [];
@ -101,7 +98,7 @@ this.MatchPattern = function(pat)
} else {
this.matchers = pat.map(p => new SingleMatchPattern(p));
}
}
};
MatchPattern.prototype = {
// |uri| should be an nsIURI.

View File

@ -66,17 +66,17 @@ var Manager = {
receiveMessage({name, data, target}) {
switch (name) {
case "Extension:StateChange":
this.onStateChange(target, data);
break;
case "Extension:StateChange":
this.onStateChange(target, data);
break;
case "Extension:LocationChange":
this.onLocationChange(target, data);
break;
case "Extension:LocationChange":
this.onLocationChange(target, data);
break;
case "Extension:DOMContentLoaded":
this.onLoad(target, data);
break;
case "Extension:DOMContentLoaded":
this.onLoad(target, data);
break;
}
},
@ -143,8 +143,8 @@ const EVENTS = [
"onErrorOccurred",
"onReferenceFragmentUpdated",
//"onCreatedNavigationTarget",
//"onHistoryStateUpdated",
// "onCreatedNavigationTarget",
// "onHistoryStateUpdated",
];
var WebNavigation = {};
@ -153,5 +153,5 @@ for (let event of EVENTS) {
WebNavigation[event] = {
addListener: Manager.addListener.bind(Manager, event),
removeListener: Manager.removeListener.bind(Manager, event),
}
};
}

View File

@ -1,19 +1,22 @@
"use strict";
/* globals docShell */
var Ci = Components.interfaces;
function getWindowId(window)
{
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function getWindowId(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
}
function getParentWindowId(window)
{
function getParentWindowId(window) {
return getWindowId(window.parent);
}
function loadListener(event)
{
function loadListener(event) {
let document = event.target;
let window = document.defaultView;
let url = document.documentURI;
@ -81,15 +84,7 @@ var WebProgressListener = {
sendAsyncMessage("Extension:LocationChange", data);
},
QueryInterface: function QueryInterface(aIID) {
if (aIID.equals(Ci.nsIWebProgressListener) ||
aIID.equals(Ci.nsISupportsWeakReference) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
};
var disabled = false;

View File

@ -6,6 +6,8 @@
const EXPORTED_SYMBOLS = ["WebRequest"];
/* exported WebRequest */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
@ -37,7 +39,7 @@ function parseExtra(extra, allowed) {
if (extra) {
for (let ex of extra) {
if (allowed.indexOf(ex) == -1) {
throw `Invalid option ${ex}`;
throw new Error(`Invalid option ${ex}`);
}
}
}
@ -81,7 +83,7 @@ var ContentPolicyManager = {
windowId: msg.data.windowId,
parentWindowId: msg.data.parentWindowId,
type: msg.data.type,
browser: browser
browser: browser,
});
} catch (e) {
Cu.reportError(e);
@ -122,8 +124,7 @@ var ContentPolicyManager = {
};
ContentPolicyManager.init();
function StartStopListener(manager, loadContext)
{
function StartStopListener(manager, loadContext) {
this.manager = manager;
this.loadContext = loadContext;
this.orig = null;
@ -147,7 +148,7 @@ StartStopListener.prototype = {
onDataAvailable(...args) {
return this.orig.onDataAvailable(...args);
}
},
};
var HttpObserverManager = {
@ -308,7 +309,7 @@ var HttpObserverManager = {
}
if (opts.requestHeaders && result.requestHeaders) {
// Start by clearing everything.
for (let {name, value} of requestHeaders) {
for (let {name} of requestHeaders) {
channel.setRequestHeader(name, "", false);
}
@ -318,7 +319,7 @@ var HttpObserverManager = {
}
if (opts.responseHeaders && result.responseHeaders) {
// Start by clearing everything.
for (let {name, value} of responseHeaders) {
for (let {name} of responseHeaders) {
channel.setResponseHeader(name, "", false);
}
@ -372,7 +373,7 @@ var onBeforeRequest = {
removeListener(callback) {
ContentPolicyManager.removeListener(callback);
}
},
};
var onBeforeSendHeaders = {
@ -384,7 +385,7 @@ var onBeforeSendHeaders = {
removeListener(callback) {
HttpObserverManager.removeListener("modify", callback);
}
},
};
var onSendHeaders = {
@ -396,7 +397,7 @@ var onSendHeaders = {
removeListener(callback) {
HttpObserverManager.removeListener("afterModify", callback);
}
},
};
var onHeadersReceived = {
@ -408,7 +409,7 @@ var onHeadersReceived = {
removeListener(callback) {
HttpObserverManager.removeListener("headersReceived", callback);
}
},
};
var onResponseStarted = {
@ -420,7 +421,7 @@ var onResponseStarted = {
removeListener(callback) {
HttpObserverManager.removeListener("onStart", callback);
}
},
};
var onCompleted = {
@ -432,7 +433,7 @@ var onCompleted = {
removeListener(callback) {
HttpObserverManager.removeListener("onStop", callback);
}
},
};
var WebRequest = {

View File

@ -2,8 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const EXPORTED_SYMBOLS = ["WebRequestCommon"];
/* exported WebRequestCommon */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
@ -49,5 +53,5 @@ var WebRequestCommon = {
}
return urlFilter.matches(uri);
}
},
};

View File

@ -53,16 +53,16 @@ var ContentPolicy = {
receiveMessage(msg) {
switch (msg.name) {
case "WebRequest:AddContentPolicy":
this.addContentPolicy(msg.data);
break;
case "WebRequest:AddContentPolicy":
this.addContentPolicy(msg.data);
break;
case "WebRequest:RemoveContentPolicy":
this.contentPolicies.delete(msg.data.id);
if (this.contentPolicies.size == 0) {
this.unregister();
}
break;
case "WebRequest:RemoveContentPolicy":
this.contentPolicies.delete(msg.data.id);
if (this.contentPolicies.size == 0) {
this.unregister();
}
break;
}
},
@ -82,8 +82,7 @@ var ContentPolicy = {
let ids = [];
for (let [id, {blocking, filter}] of this.contentPolicies.entries()) {
if (WebRequestCommon.typeMatches(policyType, filter.types) &&
WebRequestCommon.urlMatches(contentLocation, filter.urls))
{
WebRequestCommon.urlMatches(contentLocation, filter.urls)) {
if (blocking) {
block = true;
}
@ -106,8 +105,7 @@ var ContentPolicy = {
}
if (policyType == Ci.nsIContentPolicy.TYPE_SUBDOCUMENT ||
(node instanceof Ci.nsIDOMXULElement && node.localName == "browser"))
{
(node instanceof Ci.nsIDOMXULElement && node.localName == "browser")) {
// Chrome sets frameId to the ID of the sub-window. But when
// Firefox loads an iframe, it sets |node| to the <iframe>
// element, whose window is the parent window. We adopt the