From 737a51a228b07af1b68f050c76b039e1f0d5b79d Mon Sep 17 00:00:00 2001
From: Erik Vold
Date: Wed, 2 Jul 2014 14:37:05 -0700
Subject: [PATCH] Bug 1032969 - Uplift Add-on SDK to Firefox r=me
---
addon-sdk/source/lib/sdk/addon/runner.js | 3 +-
addon-sdk/source/lib/sdk/context-menu.js | 6 +-
.../lib/sdk/deprecated/unit-test-finder.js | 13 +++-
.../source/lib/sdk/deprecated/unit-test.js | 77 +++++++++++--------
addon-sdk/source/lib/sdk/tabs/tab-fennec.js | 14 +++-
addon-sdk/source/lib/sdk/tabs/tab-firefox.js | 15 +++-
addon-sdk/source/lib/sdk/tabs/utils.js | 5 ++
addon-sdk/source/lib/sdk/test.js | 13 +---
.../source/python-lib/cuddlefish/__init__.py | 1 -
addon-sdk/source/test/test-context-menu.html | 17 ++++
addon-sdk/source/test/test-context-menu.js | 42 ++++++++++
addon-sdk/source/test/test-tab.js | 18 +++++
12 files changed, 168 insertions(+), 56 deletions(-)
diff --git a/addon-sdk/source/lib/sdk/addon/runner.js b/addon-sdk/source/lib/sdk/addon/runner.js
index 13063f34120da..7391c4c6169c8 100644
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -125,8 +125,9 @@ function run(options) {
// Do not enable HTML localization while running test as it is hard to
// disable. Because unit tests are evaluated in a another Loader who
// doesn't have access to this current loader.
- if (options.main !== 'test-harness/run-tests')
+ if (options.main !== 'sdk/test/runner') {
require('../l10n/html').enable();
+ }
}
catch(error) {
console.exception(error);
diff --git a/addon-sdk/source/lib/sdk/context-menu.js b/addon-sdk/source/lib/sdk/context-menu.js
index 0c9ebe0cb03f3..860adf8c0f7e3 100644
--- a/addon-sdk/source/lib/sdk/context-menu.js
+++ b/addon-sdk/source/lib/sdk/context-menu.js
@@ -259,9 +259,13 @@ function populateCallbackNodeData(node) {
data.selectionText = selection.text;
data.srcURL = node.src || null;
- data.linkURL = node.href || null;
data.value = node.value || null;
+ while (!data.linkURL && node) {
+ data.linkURL = node.href || null;
+ node = node.parentNode;
+ }
+
return data;
}
diff --git a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
index 368efdcc6d3f8..750292c011158 100644
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
@@ -10,7 +10,11 @@ module.metadata = {
const file = require("../io/file");
const memory = require('./memory');
const { Loader } = require("../test/loader");
-const cuddlefish = require("../loader/cuddlefish");
+
+const { isNative } = require('@loader/options');
+
+const cuddlefish = isNative ? require("toolkit/loader") : require("../loader/cuddlefish");
+
const { defer, resolve } = require("../core/promise");
const { getAddon } = require("../addon/installer");
const { id } = require("sdk/self");
@@ -22,7 +26,7 @@ const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
-const TEST_REGEX = /(([^\/]+\/)(?:lib\/)?)(tests?\/test-[^\.\/]+)\.js$/;
+const TEST_REGEX = /(([^\/]+\/)(?:lib\/)?)?(tests?\/test-[^\.\/]+)\.js$/;
const { mapcat, map, filter, fromEnumerator } = require("sdk/util/sequence");
@@ -54,8 +58,9 @@ const getSuites = function getSuites({ id }) {
let file = xpiURI.QueryInterface(Ci.nsIFileURL).file;
let suites = [];
let addEntry = (entry) => {
- if (TEST_REGEX.test(entry)) {
- let suite = RegExp.$2 + RegExp.$3;
+ let pass = TEST_REGEX.test(entry);
+ if (pass) {
+ let suite = (isNative ? "./" : "") + RegExp.$2 + RegExp.$3;
suites.push(suite);
}
}
diff --git a/addon-sdk/source/lib/sdk/deprecated/unit-test.js b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
index fb615653e7df3..7fef7ca82592d 100644
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -12,7 +12,7 @@ const timer = require("../timers");
var cfxArgs = require("@test/options");
const { getTabs, getURI } = require("../tabs/utils");
const { windows, isBrowser } = require("../window/utils");
-const { defer } = require("../core/promise");
+const { defer, all } = require("../core/promise");
const findAndRunTests = function findAndRunTests(options) {
var TestFinder = require("./unit-test-finder").TestFinder;
@@ -285,43 +285,58 @@ TestRunner.prototype = {
}
let wins = windows(null, { includePrivate: true });
- let tabs = [];
- for (let win of wins.filter(isBrowser)) {
- for (let tab of getTabs(win)) {
- tabs.push(tab);
+ let winPromises = wins.map(win => {
+ let { promise, resolve } = defer();
+ if (["interactive", "complete"].indexOf(win.document.readyState) >= 0) {
+ resolve()
}
- }
+ else {
+ win.addEventListener("DOMContentLoaded", function onLoad() {
+ win.removeEventListener("DOMContentLoaded", onLoad, false);
+ resolve();
+ }, false);
+ }
+ return promise;
+ });
- if (wins.length != 1)
- this.fail("Should not be any unexpected windows open");
- if (tabs.length != 1)
- this.fail("Should not be any unexpected tabs open");
- if (tabs.length != 1 || wins.length != 1) {
- console.log("Windows open:");
- for (let win of wins) {
- if (isBrowser(win)) {
- tabs = getTabs(win);
- console.log(win.location + " - " + tabs.map(getURI).join(", "));
+ all(winPromises).then(_ => {
+ let tabs = [];
+ for (let win of wins.filter(isBrowser)) {
+ for (let tab of getTabs(win)) {
+ tabs.push(tab);
}
- else {
- console.log(win.location);
+ }
+
+ if (wins.length != 1)
+ this.fail("Should not be any unexpected windows open");
+ if (tabs.length != 1)
+ this.fail("Should not be any unexpected tabs open");
+ if (tabs.length != 1 || wins.length != 1) {
+ console.log("Windows open:");
+ for (let win of wins) {
+ if (isBrowser(win)) {
+ tabs = getTabs(win);
+ console.log(win.location + " - " + tabs.map(getURI).join(", "));
+ }
+ else {
+ console.log(win.location);
+ }
}
}
- }
- this.testRunSummary.push({
- name: this.test.name,
- passed: this.test.passed,
- failed: this.test.failed,
- errors: [error for (error in this.test.errors)].join(", ")
+ this.testRunSummary.push({
+ name: this.test.name,
+ passed: this.test.passed,
+ failed: this.test.failed,
+ errors: [error for (error in this.test.errors)].join(", ")
+ });
+
+ if (this.onDone !== null) {
+ let onDone = this.onDone;
+ this.onDone = null;
+ timer.setTimeout(_ => onDone(this), 0);
+ }
});
-
- if (this.onDone !== null) {
- var onDone = this.onDone;
- var self = this;
- this.onDone = null;
- timer.setTimeout(function() { onDone(self); }, 0);
- }
}
},
diff --git a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
index c78af1b5fcdc2..e823d51053e51 100644
--- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
@@ -7,9 +7,9 @@ const { Cc, Ci } = require('chrome');
const { Class } = require('../core/heritage');
const { tabNS, rawTabNS } = require('./namespace');
const { EventTarget } = require('../event/target');
-const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow,
- getTabForBrowser,
- setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
+const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
+ getTabContentWindow, getTabForBrowser, setTabURL, getOwnerWindow,
+ getTabContentDocument, getTabContentType, getTabId } = require('./utils');
const { emit } = require('../event/core');
const { isPrivate } = require('../private-browsing/utils');
const { isWindowPrivate } = require('../window/utils');
@@ -97,6 +97,14 @@ const Tab = Class({
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
},
+ /**
+ * tab's document readyState, or 'uninitialized' if it doesn't even exist yet.
+ */
+ get readyState() {
+ let doc = getTabContentDocument(tabNS(this).tab);
+ return doc && doc.readyState || 'uninitialized';
+ },
+
get id() {
return getTabId(tabNS(this).tab);
},
diff --git a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
index ae4851c68ba3b..fde97dcba1ea5 100644
--- a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
@@ -10,8 +10,9 @@ const { has } = require("../util/array");
const { EVENTS } = require("./events");
const { getThumbnailURIForWindow } = require("../content/thumbnail");
const { getFaviconURIForLocation } = require("../io/data");
-const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
- getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
+const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle,
+ setTabTitle, getTabContentDocument, getTabURL, setTabURL,
+ getTabContentType, getTabId } = require('./utils');
const { isPrivate } = require('../private-browsing/utils');
const { isWindowPrivate } = require('../window/utils');
const viewNS = require('../core/namespace').ns();
@@ -143,12 +144,20 @@ const TabTrait = Trait.compose(EventEmitter, {
/**
* Document object of the page that is currently loaded in this tab.
*/
- get _contentDocument() this._browser.contentDocument,
+ get _contentDocument() getTabContentDocument(this._tab),
/**
* Window object of the page that is currently loaded in this tab.
*/
get _contentWindow() this._browser.contentWindow,
+ /**
+ * tab's document readyState, or 'uninitialized' if it doesn't even exist yet.
+ */
+ get readyState() {
+ let doc = this._contentDocument;
+ return doc && doc.readyState || 'uninitialized';
+ },
+
/**
* Unique id for the tab, actually maps to tab.linkedPanel but with some munging.
*/
diff --git a/addon-sdk/source/lib/sdk/tabs/utils.js b/addon-sdk/source/lib/sdk/tabs/utils.js
index 6909cce006df5..63f7c92b6918a 100644
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -226,6 +226,11 @@ function setTabTitle(tab, title) {
}
exports.setTabTitle = setTabTitle;
+function getTabContentDocument(tab) {
+ return getBrowserForTab(tab).contentDocument;
+}
+exports.getTabContentDocument = getTabContentDocument;
+
function getTabContentWindow(tab) {
return getBrowserForTab(tab).contentWindow;
}
diff --git a/addon-sdk/source/lib/sdk/test.js b/addon-sdk/source/lib/sdk/test.js
index 46ceab2654af3..4d6ab2cf1ab0f 100644
--- a/addon-sdk/source/lib/sdk/test.js
+++ b/addon-sdk/source/lib/sdk/test.js
@@ -1,7 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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";
module.metadata = {
@@ -13,19 +12,10 @@ const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
const { defer } = require("sdk/core/promise");
const BaseAssert = require("sdk/test/assert").Assert;
const { isFunction, isObject } = require("sdk/lang/type");
+const { extend } = require("sdk/util/object");
exports.Assert = BaseAssert;
-function extend(target) {
- let descriptor = {}
- Array.slice(arguments, 1).forEach(function(source) {
- Object.getOwnPropertyNames(source).forEach(function onEach(name) {
- descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
- });
- });
- return Object.create(target, descriptor);
-}
-
/**
* Function takes test `suite` object in CommonJS format and defines all of the
* tests from that suite and nested suites in a jetpack format on a given
@@ -110,7 +100,6 @@ function defineTestSuite(target, suite, prefix) {
* test runner will be able to run CommonJS test without manual changes.
*/
exports.run = function run(exports) {
-
// We can't leave old properties on exports since those are test in a CommonJS
// format that why we move everything to a new `suite` object.
let suite = {};
diff --git a/addon-sdk/source/python-lib/cuddlefish/__init__.py b/addon-sdk/source/python-lib/cuddlefish/__init__.py
index 6e86615ccbc32..5b721604e7883 100644
--- a/addon-sdk/source/python-lib/cuddlefish/__init__.py
+++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py
@@ -791,7 +791,6 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
extra_environment = {}
if command == "test":
# This should be contained in the test runner package.
- # maybe just do: target_cfg.main = 'test-harness/run-tests'
harness_options['main'] = 'sdk/test/runner'
harness_options['mainPath'] = 'sdk/test/runner'
else:
diff --git a/addon-sdk/source/test/test-context-menu.html b/addon-sdk/source/test/test-context-menu.html
index 1d4304b87351a..63a365ae794a8 100644
--- a/addon-sdk/source/test/test-context-menu.html
+++ b/addon-sdk/source/test/test-context-menu.html
@@ -62,6 +62,23 @@
+
+
+
+
+
+
+
+
+
+
+ A complex nested structure.
+
+
+
+
+
+
diff --git a/addon-sdk/source/test/test-context-menu.js b/addon-sdk/source/test/test-context-menu.js
index 997af80cc0e22..7eafc8b691641 100644
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -3572,6 +3572,48 @@ exports.testPredicateContextTargetLinkNotSet = function (assert, done) {
});
};
+// Test that the data object has the correct link for a nested image
+exports.testPredicateContextTargetLinkSetNestedImage = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.linkURL, TEST_DOC_URL + "#nested-image");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(doc.getElementById("predicate-test-nested-image"), function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
+// Test that the data object has the correct link for a complex nested structure
+exports.testPredicateContextTargetLinkSetNestedStructure = function (assert, done) {
+ let test = new TestHelper(assert, done);
+ let loader = test.newLoader();
+
+ let items = [loader.cm.Item({
+ label: "item",
+ context: loader.cm.PredicateContext(function (data) {
+ assert.strictEqual(data.linkURL, TEST_DOC_URL + "#nested-structure");
+ return true;
+ })
+ })];
+
+ test.withTestDoc(function (window, doc) {
+ test.showMenu(doc.getElementById("predicate-test-nested-structure"), function (popup) {
+ test.checkMenu(items, [], []);
+ test.done();
+ });
+ });
+};
+
// Test that the data object has the value for an input textbox
exports.testPredicateContextTargetValueSet = function (assert, done) {
let test = new TestHelper(assert, done);
diff --git a/addon-sdk/source/test/test-tab.js b/addon-sdk/source/test/test-tab.js
index c49b431dcfd60..5b487092bfc92 100644
--- a/addon-sdk/source/test/test-tab.js
+++ b/addon-sdk/source/test/test-tab.js
@@ -172,4 +172,22 @@ exports["test modelFor(xulTab)"] = (assert, done) => {
});
};
+exports["test tab.readyState"] = (assert, done) => {
+ tabs.open({
+ url: "data:text/html;charset=utf-8,test_readyState",
+ onOpen: (tab) => {
+ assert.equal(tab.readyState, "uninitialized",
+ "tab is 'uninitialized' when opened");
+ },
+ onReady: (tab) => {
+ assert.notEqual(["interactive", "complete"].indexOf(tab.readyState), -1,
+ "tab is either interactive or complete when onReady");
+ },
+ onLoad: (tab) => {
+ assert.equal(tab.readyState, "complete", "tab is complete onLoad");
+ tab.close(defer(done));
+ }
+ });
+}
+
require("sdk/test").run(exports);