Bug 1192281: Uplift Add-on SDK to fx-team. a=me
authorDave Townsend <dtownsend@oxymoronical.com>
Fri, 04 Sep 2015 14:49:04 -0700
changeset 261105 233758966f07dc0ce3217dd99b68109bf932b297
parent 261098 56f5ffd44b7f13d5db5bd497018f428dcbb28fc4
child 261106 0ddcebe7cc4f48a8f80273adebd5cf0927d119de
push id29335
push usercbook@mozilla.com
push dateMon, 07 Sep 2015 09:57:44 +0000
treeherdermozilla-central@df817a53e24b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1192281
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1192281: Uplift Add-on SDK to fx-team. a=me https://github.com/mozilla/addon-sdk/compare/96ae8d914fab9baad903cac07bf9f37da98fc0bc...5ac44b7e088c986bbbd8aaea57b95738eb071ead
addon-sdk/source/lib/sdk/panel/utils.js
addon-sdk/source/lib/sdk/test/utils.js
addon-sdk/source/test/tabs/test-firefox-tabs.js
addon-sdk/source/test/test-context-menu.js
addon-sdk/source/test/test-panel.js
addon-sdk/source/test/test-simple-prefs.js
addon-sdk/source/test/test-unit-test-finder.js
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -218,16 +218,30 @@ function show(panel, options, anchor) {
   let window = anchor && getOwnerBrowserWindow(anchor);
   let { document } = window ? window : getMostRecentBrowserWindow();
   attach(panel, document);
 
   open(panel, options, anchor);
 }
 exports.show = show
 
+function onPanelClick(event) {
+  let { target, metaKey, ctrlKey, shiftKey, button } = event;
+  let accel = platform === "darwin" ? metaKey : ctrlKey;
+  let isLeftClick = button === 0;
+  let isMiddleClick = button === 1;
+
+  if ((isLeftClick && (accel || shiftKey)) || isMiddleClick) {
+    let link = target.closest('a');
+
+    if (link && link.href)
+       getMostRecentBrowserWindow().openUILink(link.href, event)
+  }
+}
+
 function setupPanelFrame(frame) {
   frame.setAttribute("flex", 1);
   frame.setAttribute("transparent", "transparent");
   frame.setAttribute("autocompleteenabled", true);
   if (platform === "darwin") {
     frame.style.borderRadius = "6px";
     frame.style.padding = "1px";
   }
@@ -296,16 +310,18 @@ function make(document) {
     events.emit(type, { subject: panel })
   }
 
   panel.addEventListener("popupshowing", onDisplayChange, false);
   panel.addEventListener("popuphiding", onDisplayChange, false);
   panel.addEventListener("popupshown", onPanelStateChange, false);
   panel.addEventListener("popuphidden", onPanelStateChange, false);
 
+  panel.addEventListener("click", onPanelClick, false);
+
   // Panel content document can be either in panel `viewFrame` or in
   // a `backgroundFrame` depending on panel state. Listeners are set
   // on both to avoid setting and removing listeners on panel state changes.
 
   panel.addEventListener("DOMContentLoaded", onContentReady, true);
   backgroundFrame.addEventListener("DOMContentLoaded", onContentReady, true);
 
   panel.addEventListener("load", onContentLoad, true);
--- a/addon-sdk/source/lib/sdk/test/utils.js
+++ b/addon-sdk/source/lib/sdk/test/utils.js
@@ -8,24 +8,24 @@ module.metadata = {
 };
 
 const { defer } = require('../core/promise');
 const { setInterval, clearInterval } = require('../timers');
 const { getTabs, closeTab } = require("../tabs/utils");
 const { windows: getWindows } = require("../window/utils");
 const { close: closeWindow } = require("../window/helpers");
 const { isGenerator } = require("../lang/type");
-
+const { env } = require("../system/environment");
 const { Task } = require("resource://gre/modules/Task.jsm");
 
-function getTestNames (exports)
-  Object.keys(exports).filter(name => /^test/.test(name))
+const getTestNames = (exports) =>
+  Object.keys(exports).filter(name => /^test/.test(name));
 
-function isTestAsync (fn) fn.length > 1
-function isHelperAsync (fn) fn.length > 2
+const isTestAsync = ({length}) => length > 1;
+const isHelperAsync = ({length}) => length > 2;
 
 /*
  * Takes an `exports` object of a test file and a function `beforeFn`
  * to be run before each test. `beforeFn` is called with a `name` string
  * as the first argument of the test name, and may specify a second
  * argument function `done` to indicate that this function should
  * resolve asynchronously
  */
@@ -190,8 +190,10 @@ let cleanUI = function cleanUI() {
 
   getTabs(windows[0]).slice(1).forEach(closeTab);
 
   resolve();
 
   return promise;
 }
 exports.cleanUI = cleanUI;
+
+exports.isTravisCI = ("TRAVIS" in env && "CI" in env);
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -1229,16 +1229,43 @@ exports['test active tab properties defi
           }
           tab.close(done);
         }
       });
     }
   });
 };
 
+// related to bug 922956
+// https://bugzilla.mozilla.org/show_bug.cgi?id=922956
+exports["test ready event after window.open"] = function (assert, done) {
+  setPref(OPEN_IN_NEW_WINDOW_PREF, 2);
+  setPref(DISABLE_POPUP_PREF, false);
+
+  let firstRun = true;
+  tabs.on('ready', function onReady(tab) {
+    if (firstRun) {
+      assert.pass("tab ready callback after 1st window.open");
+      firstRun = false;
+      tab.close();
+    }
+    else {
+      assert.pass("tab ready callback after 2nd window.open");
+      tabs.removeListener('ready', onReady);
+      tab.close(done);
+    }
+  });
+
+  tabs.activeTab.attach({
+    contentScript: "window.open('about:blank');" +
+                   "window.open('about:blank', '', " +
+                   "'width=800,height=600,resizable=no,status=no,location=no');"
+  });
+}
+
 after(exports, function*(name, assert) {
   resetPopupPrefs();
   yield cleanUI();
 });
 
 const resetPopupPrefs = () => {
   resetPref(OPEN_IN_NEW_WINDOW_PREF);
   resetPref(DISABLE_POPUP_PREF);
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -1,16 +1,17 @@
 /* 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';
 
 require("sdk/context-menu");
 
 const { defer } = require("sdk/core/promise");
+const { isTravisCI } = require("sdk/test/utils");
 const packaging = require('@loader/options');
 
 // These should match the same constants in the module.
 const OVERFLOW_THRESH_DEFAULT = 10;
 const OVERFLOW_THRESH_PREF =
   "extensions.addon-sdk.context-menu.overflowThreshold";
 
 const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html");
@@ -3747,15 +3748,15 @@ exports.testPredicateContextTargetValueN
   test.withTestDoc(function (window, doc) {
     test.showMenu("#image", function (popup) {
       test.checkMenu(items, [], []);
       test.done();
     });
   });
 };
 
-if (packaging.isNative) {
+if (isTravisCI) {
   module.exports = {
     "test skip on jpm": (assert) => assert.pass("skipping this file with jpm")
   };
 }
 
 require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -17,17 +17,18 @@ const self = require('sdk/self');
 const { open, close, focus, ready } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { isWindowPBSupported } = require('sdk/private-browsing/utils');
 const { defer, all } = require('sdk/core/promise');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { URL } = require('sdk/url');
 const { wait } = require('./event/helpers');
 const packaging = require('@loader/options');
-const { cleanUI, after } = require("sdk/test/utils");
+const { cleanUI, after, isTravisCI } = require("sdk/test/utils");
+const { platform } = require('sdk/system');
 
 const fixtures = require('./fixtures')
 
 const SVG_URL = fixtures.url('mofo_logo.SVG');
 
 const Isolate = fn => '(' + fn + ')()';
 
 function ignorePassingDOMNodeWarning(type, message) {
@@ -1310,20 +1311,72 @@ exports["test Panel without contentURL a
     panel.show();
   });
 
   assert.pass("Received show event");
 
   loader.unload();
 }
 
+exports["test Panel links"] = function*(assert) {
+  const loader = Loader(module);
+
+  const { Panel } = loader.require('sdk/panel');
+  const { getActiveView } = loader.require('sdk/view/core');
+  const tabs = loader.require('sdk/tabs');
+
+  const synthesizeClick = (panel, options) => {
+    let { contentWindow } = getActiveView(panel).querySelector('iframe');
+    let event = new contentWindow.MouseEvent('click', options);
+
+    contentWindow.document.querySelector('a').dispatchEvent(event);
+  }
+
+  const linkURL = 'data:text/html;charset=utf-8,' +
+                  encodeURIComponent('<html><a href="#">foo</a></html>');
+
+  const contentURL = 'data:text/html;charset=utf-8,' +
+          encodeURIComponent(`<html><a href="${linkURL}">page</a></html>`);
+
+  let panel = Panel({
+    contentURL,
+    contentScript: Isolate(() => self.postMessage(document.URL))
+  });
+
+  panel.show();
+
+  let url = yield wait(panel, 'message');
+
+  assert.equal(url, contentURL,
+    'content URL loaded');
+
+  synthesizeClick(panel, { bubbles: true });
+
+  url = yield wait(panel, 'message');
+
+  assert.equal(url, linkURL,
+    'link URL loaded in the panel after click');
+
+  synthesizeClick(panel, {
+    bubbles: true,
+    [platform === 'darwin' ? 'metaKey' : 'ctrlKey']: true
+  });
+
+  let tab = yield wait(tabs, 'ready');
+
+  assert.equal(tab.url, linkURL + '#',
+      'link URL loaded in a new tab after click + accel');
+
+  loader.unload();
+}
+
 after(exports, function*(name, assert) {
   yield cleanUI();
   assert.pass("ui was cleaned.");
 });
 
-if (packaging.isNative) {
+if (isTravisCI) {
   module.exports = {
     "test skip on jpm": (assert) => assert.pass("skipping this file with jpm")
   };
 }
 
 require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-simple-prefs.js
+++ b/addon-sdk/source/test/test-simple-prefs.js
@@ -307,20 +307,20 @@ exports.testUnloadOfDynamicPrefGeneratio
   assert.pass('unload');
 
   // hide and show the inline prefs
   let { promise, resolve } = defer();
   modelFor(getTabForId(tabId)).close(resolve);
   yield promise;
 
   // reopen the add-on prefs page
-  ({ tabId, document }) = yield open(addon);
+  ({ tabId, document } = yield open(addon));
 
   // confirm dynamic pref generation did not occur
-  ({ promise, resolve }) = defer();
+  ({ promise, resolve } = defer());
   results = document.querySelectorAll("*[data-jetpack-id=\"" + id + "\"]");
   assert.equal(0, results.length, "the prefs were not setup after unload");
   modelFor(getTabForId(tabId)).close(resolve);
   yield promise;
 
   // uninstall the add-on
   yield uninstall(id);
 
--- a/addon-sdk/source/test/test-unit-test-finder.js
+++ b/addon-sdk/source/test/test-unit-test-finder.js
@@ -20,38 +20,38 @@ exports["test makeFilters no filter"] = 
   testMethods.forEach(m => assert.ok(testFilter(m), "using no options on method name " + m + " works"));
 }
 
 exports["test makeFilters no method filter"] = (assert) => {
   let { fileFilter, testFilter } = makeFilters({ filter: "i" });
   testFiles.forEach(f => assert.ok(fileFilter(f), "using filter 'i' on filename " + f + " works"));
   testMethods.forEach(m => assert.ok(testFilter(m), "using filter 'i' on method name " + m + " works"));
 
-  ({ fileFilter, testFilter }) = makeFilters({ filter: "i:" });
+  ({ fileFilter, testFilter } = makeFilters({ filter: "i:" }));
   testFiles.forEach(f => assert.ok(fileFilter(f), "using filter 'i:' on filename " + f + " works"));
   testMethods.forEach(m => assert.ok(testFilter(m), "using filter 'i:' on method name " + m + " works"));
 
-  ({ fileFilter, testFilter }) = makeFilters({ filter: "z:" });
+  ({ fileFilter, testFilter } = makeFilters({ filter: "z:" }));
   testFiles.forEach(f => assert.ok(!fileFilter(f), "using filter 'z:' on filename " + f + " dnw"));
   testMethods.forEach(m => assert.ok(testFilter(m), "using filter 'z:' on method name " + m + " works"));
 }
 
 exports["test makeFilters no file filter"] = (assert) => {
   let { fileFilter, testFilter } = makeFilters({ filter: ":i" });
   testFiles.forEach(f => assert.ok(fileFilter(f), "using filter ':i' on filename " + f + " works"));
   testMethods.forEach(m => assert.ok(testFilter(m), "using filter ':i' on method name " + m + " works"));
 
-  ({ fileFilter, testFilter }) = makeFilters({ filter: ":z" });
+  ({ fileFilter, testFilter } = makeFilters({ filter: ":z" }));
   testFiles.forEach(f => assert.ok(fileFilter(f), "using filter ':z' on filename " + f + " works"));
   testMethods.forEach(m => assert.ok(!testFilter(m), "using filter ':z' on method name " + m + " dnw"));
 }
 
 exports["test makeFilters both filters"] = (assert) => {
   let { fileFilter, testFilter } = makeFilters({ filter: "i:i" });
   testFiles.forEach(f => assert.ok(fileFilter(f), "using filter 'i:i' on filename " + f + " works"));
   testMethods.forEach(m => assert.ok(testFilter(m), "using filter 'i:i' on method name " + m + " works"));
 
-  ({ fileFilter, testFilter }) = makeFilters({ filter: "z:z" });
+  ({ fileFilter, testFilter } = makeFilters({ filter: "z:z" }));
   testFiles.forEach(f => assert.ok(!fileFilter(f), "using filter 'z:z' on filename " + f + " dnw"));
   testMethods.forEach(m => assert.ok(!testFilter(m), "using filter 'z:z' on method name " + m + " dnw"));
 }
 
 require("sdk/test").run(exports);