Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into llapi-cleanup

  • Loading branch information...
commit 53ca7eaa2246eeb86eac924c49de14f2499282a5 2 parents 1c4f5bc + 78bd65b
@wbamberg wbamberg authored
View
8 doc/dev-guide-source/tutorials/installation.md
@@ -33,12 +33,18 @@ tar -xf addon-sdk.tar.gz
cd addon-sdk
</pre>
-Then run:
+Then run if you're a Bash user (most people are):
<pre>
source bin/activate
</pre>
+And if you're a non-Bash user, you should run:
+
+<pre>
+bash bin/activate
+</pre>
+
Your command prompt should now have a new prefix containing the name of the
SDK's root directory:
View
2  lib/sdk/content/worker.js
@@ -130,11 +130,11 @@ const WorkerSandbox = EventEmitter.compose({
// Instantiate trusted code in another Sandbox in order to prevent content
// script from messing with standard classes used by proxy and API code.
let apiSandbox = sandbox(window, { wantXrays: true });
+ apiSandbox.console = console;
// Build content proxies only if the document has a non-system principal
// And only on old firefox versions that doesn't ship bug 738244
if (USE_JS_PROXIES && XPCNativeWrapper.unwrap(window) !== window) {
- apiSandbox.console = console;
// Execute the proxy code
load(apiSandbox, CONTENT_PROXY_URL);
// Get a reference of the window's proxy
View
1  lib/sdk/panel.js
@@ -356,6 +356,7 @@ const Panel = Symbiont.resolve({
},
_onChange: function _onChange(e) {
+ this._frameLoadersSwapped = false;
if ('contentURL' in e && this._frame) {
// Cleanup the worker before injecting the content script in the new
// document
View
6 lib/sdk/tabs/tab-fennec.js
@@ -8,7 +8,7 @@ const { Class } = require('../core/heritage');
const { tabNS } = require('./namespace');
const { EventTarget } = require('../event/target');
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
- setTabURL, getOwnerWindow, getTabContentType } = require('./utils');
+ setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
const { emit } = require('../event/core');
const { when: unload } = require('../system/unload');
@@ -63,6 +63,10 @@ const Tab = Class({
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
},
+ get id() {
+ return getTabId(tabNS(this).tab);
+ },
+
/**
* The index of the tab relative to other tabs in the application window.
* Changing this property will change order of the actual position of the tab.
View
7 lib/sdk/tabs/tab-firefox.js
@@ -10,7 +10,7 @@ const { EVENTS } = require("./events");
const { getThumbnailURIForWindow } = require("../content/thumbnail");
const { getFaviconURIForLocation } = require("../io/data");
const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
- getTabURL, setTabURL, getTabContentType } = require('./utils');
+ getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
// Array of the inner instances of all the wrapped tabs.
const TABS = [];
@@ -95,6 +95,11 @@ const TabTrait = Trait.compose(EventEmitter, {
get _contentWindow() this._browser.contentWindow,
/**
+ * Unique id for the tab, actually maps to tab.linkedPanel but with some munging.
+ */
+ get id() getTabId(this._tab),
+
+ /**
* The title of the page currently loaded in the tab.
* Changing this property changes an actual title.
* @type {String}
View
9 lib/sdk/tabs/utils.js
@@ -150,6 +150,15 @@ function getBrowserForTab(tab) {
}
exports.getBrowserForTab = getBrowserForTab;
+function getTabId(tab) {
+ if (tab.browser) // fennec
+ return tab.id
+
+ return String.split(tab.linkedPanel, 'panel').pop();
+}
+exports.getTabId = getTabId;
+
+
function getTabTitle(tab) {
return getBrowserForTab(tab).contentDocument.title || tab.label || "";
}
View
44 lib/sdk/test/httpd.js
@@ -586,6 +586,14 @@ nsHttpServer.prototype =
},
//
+ // see nsIHttpServer.registerPrefixHandler
+ //
+ registerPrefixHandler: function(prefix, handler)
+ {
+ this._handler.registerPrefixHandler(prefix, handler);
+ },
+
+ //
// see nsIHttpServer.registerErrorHandler
//
registerErrorHandler: function(code, handler)
@@ -2144,6 +2152,12 @@ function ServerHandler(server)
* @see ServerHandler.prototype._defaultPaths
*/
this._overridePaths = {};
+
+ /**
+* Custom request handlers for the server in which this resides. Prefix-handler
+* pairs are stored as property-value pairs in this property.
+*/
+ this._overridePrefixes = {};
/**
* Custom request handlers for the error handlers in the server in which this
@@ -2209,7 +2223,23 @@ ServerHandler.prototype =
}
else
{
- this._handleDefault(request, response);
+ let longestPrefix = "";
+ for (let prefix in this._overridePrefixes)
+ {
+ if (prefix.length > longestPrefix.length && path.startsWith(prefix))
+ {
+ longestPrefix = prefix;
+ }
+ }
+ if (longestPrefix.length > 0)
+ {
+ dumpn("calling prefix override for " + longestPrefix);
+ this._overridePrefixes[longestPrefix](request, response);
+ }
+ else
+ {
+ this._handleDefault(request, response);
+ }
}
}
catch (e)
@@ -2315,6 +2345,18 @@ ServerHandler.prototype =
},
//
+ // see nsIHttpServer.registerPrefixHandler
+ //
+ registerPrefixHandler: function(prefix, handler)
+ {
+ // XXX true prefix validation!
+ if (!(prefix.startsWith("/") && prefix.endsWith("/")))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ this._handlerToField(handler, this._overridePrefixes, prefix);
+ },
+
+ //
// see nsIHttpServer.registerDirectory
//
registerDirectory: function(path, directory)
View
12 python-lib/cuddlefish/__init__.py
@@ -484,12 +484,13 @@ def get_config_args(name, env_root):
def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
from templates import PACKAGE_JSON, TEST_MAIN_JS
+ from preflight import create_jid
path = os.getcwd()
addon = os.path.basename(path)
# if more than two arguments
if len(args) > 2:
print >>err, 'Too many arguments.'
- return 1
+ return {"result":1}
if len(args) == 2:
path = os.path.join(path,args[1])
try:
@@ -501,14 +502,17 @@ def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
existing = [fn for fn in os.listdir(path) if not fn.startswith(".")]
if existing:
print >>err, 'This command must be run in an empty directory.'
- return 1
+ return {"result":1}
for d in ['lib','data','test','doc']:
os.mkdir(os.path.join(path,d))
print >>out, '*', d, 'directory created'
open(os.path.join(path,'README.md'),'w').write('')
print >>out, '* README.md written'
+ jid = create_jid()
+ print >>out, '* generated jID automatically:', jid
open(os.path.join(path,'package.json'),'w').write(PACKAGE_JSON % {'name':addon.lower(),
- 'fullName':addon })
+ 'fullName':addon,
+ 'id':jid })
print >>out, '* package.json written'
open(os.path.join(path,'test','test-main.js'),'w').write(TEST_MAIN_JS)
print >>out, '* test/test-main.js written'
@@ -522,7 +526,7 @@ def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
else:
print >>out, '\nYour sample add-on is now ready in the \'' + args[1] + '\' directory.'
print >>out, 'Change to that directory, then do "cfx test" to test it, \nand "cfx run" to try it. Have fun!'
- return 0
+ return {"result":0, "jid":jid}
def buildJID(target_cfg):
if "id" in target_cfg:
View
1  python-lib/cuddlefish/templates.py
@@ -23,6 +23,7 @@
{
"name": "%(name)s",
"fullName": "%(fullName)s",
+ "id": "%(id)s",
"description": "a basic add-on",
"author": "",
"license": "MPL 2.0",
View
21 python-lib/cuddlefish/tests/test_init.py
@@ -25,7 +25,7 @@ def run_init_in_subdir(self, dirname, f, *args, **kwargs):
os.chdir(top)
def do_test_init(self,basedir):
- # Let's init the addon, no error admited
+ # Let's init the addon, no error admitted
f = open(".ignoreme","w")
f.write("stuff")
f.close()
@@ -33,7 +33,7 @@ def do_test_init(self,basedir):
out, err = StringIO(), StringIO()
init_run = initializer(None, ["init"], out, err)
out, err = out.getvalue(), err.getvalue()
- self.assertEqual(init_run, 0)
+ self.assertEqual(init_run["result"], 0)
self.assertTrue("* lib directory created" in out)
self.assertTrue("* data directory created" in out)
self.assertTrue("Have fun!" in out)
@@ -46,16 +46,17 @@ def do_test_init(self,basedir):
self.assertTrue(os.path.exists(package_json))
self.assertTrue(os.path.exists(test_main_js))
self.assertEqual(open(main_js,"r").read(),"")
- self.assertEqual(open(package_json,"r").read(),
+ self.assertEqual(open(package_json,"r").read() % {"id":"tmp_addon_id" },
PACKAGE_JSON % {"name":"tmp_addon_sample",
- "fullName": "tmp_addon_SAMPLE" })
+ "fullName": "tmp_addon_SAMPLE",
+ "id":init_run["jid"] })
self.assertEqual(open(test_main_js,"r").read(),TEST_MAIN_JS)
# Let's check that the addon is initialized
out, err = StringIO(), StringIO()
init_run = initializer(None, ["init"], out, err)
out, err = out.getvalue(), err.getvalue()
- self.failIfEqual(init_run,0)
+ self.failIfEqual(init_run["result"],0)
self.assertTrue("This command must be run in an empty directory." in err)
def test_initializer(self):
@@ -66,7 +67,7 @@ def do_test_args(self, basedir):
out,err = StringIO(), StringIO()
init_run = initializer(None, ["init", "specified-dirname", "extra-arg"], out, err)
out, err = out.getvalue(), err.getvalue()
- self.failIfEqual(init_run, 0)
+ self.failIfEqual(init_run["result"], 0)
self.assertTrue("Too many arguments" in err)
def test_args(self):
@@ -79,7 +80,7 @@ def _test_existing_files(self, basedir):
out,err = StringIO(), StringIO()
rc = initializer(None, ["init"], out, err)
out, err = out.getvalue(), err.getvalue()
- self.assertEqual(rc, 1)
+ self.assertEqual(rc["result"], 1)
self.failUnless("This command must be run in an empty directory" in err,
err)
self.failIf(os.path.exists("lib"))
@@ -102,7 +103,7 @@ def test_init_subdir(self):
out, err = StringIO(), StringIO()
rc = initializer(None, ["init", basedir], out, err)
out, err = out.getvalue(), err.getvalue()
- self.assertEqual(rc, 1)
+ self.assertEqual(rc["result"], 1)
self.assertTrue("testing if directory is empty" in out, out)
self.assertTrue("This command must be run in an empty directory." in err,
err)
@@ -112,7 +113,7 @@ def test_init_subdir(self):
out, err = StringIO(), StringIO()
rc = initializer(None, ["init", basedir], out, err)
out, err = out.getvalue(), err.getvalue()
- self.assertEqual(rc, 0)
+ self.assertEqual(rc["result"], 0)
self.assertTrue("* data directory created" in out, out)
self.assertTrue("Have fun!" in out)
self.assertEqual(err,"")
@@ -127,7 +128,7 @@ def test_init_subdir(self):
out, err = StringIO(), StringIO()
rc = initializer(None, ["init", basedir], out, err)
out, err = out.getvalue(), err.getvalue()
- self.assertEqual(rc, 0)
+ self.assertEqual(rc["result"], 0)
self.assertTrue("* data directory created" in out)
self.assertTrue("Have fun!" in out)
self.assertEqual(err,"")
View
42 test/tabs/test-fennec-tabs.js
@@ -125,6 +125,7 @@ exports.testTabProperties = function(test) {
test.assertEqual(tab.style, null, "style of the new tab matches");
test.assertEqual(tab.index, tabsLen, "index of the new tab matches");
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
+ test.assertNotEqual(tab.id, null, "a tab object always has an id property");
tab.close(function() {
loader.unload();
@@ -619,3 +620,44 @@ exports.testActiveTab_getter_alt = function(test) {
}
});
};
+
+exports.testUniqueTabIds = function(test) {
+ test.waitUntilDone();
+ var tabs = require('sdk/tabs');
+ var tabIds = {};
+ var steps = [
+ function (index) {
+ tabs.open({
+ url: "data:text/html;charset=utf-8,foo",
+ onOpen: function(tab) {
+ tabIds['tab1'] = tab.id;
+ next(index);
+ }
+ });
+ },
+ function (index) {
+ tabs.open({
+ url: "data:text/html;charset=utf-8,bar",
+ onOpen: function(tab) {
+ tabIds['tab2'] = tab.id;
+ next(index);
+ }
+ });
+ },
+ function (index) {
+ test.assertNotEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
+ test.done();
+ }
+ ];
+
+ function next(index) {
+ if (index === steps.length) {
+ return;
+ }
+ let fn = steps[index];
+ index++;
+ fn(index);
+ }
+
+ next(0);
+}
View
48 test/tabs/test-firefox-tabs.js
@@ -177,6 +177,7 @@ exports.testTabProperties = function(test) {
test.assertEqual(tab.style, null, "style of the new tab matches");
test.assertEqual(tab.index, 1, "index of the new tab matches");
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
+ test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
closeBrowserWindow(window, function() test.done());
}
});
@@ -913,6 +914,53 @@ exports['test ready event on new window tab'] = function(test) {
let window = openBrowserWindow(function(){}, uri);
};
+
+exports['test unique tab ids'] = function(test) {
+ test.waitUntilDone();
+
+ var windows = require('windows').browserWindows,
+ tabIds = {}, win1, win2;
+
+ let steps = [
+ function (index) {
+ win1 = windows.open({
+ url: "data:text/html;charset=utf-8,foo",
+ onOpen: function(window) {
+ tabIds['tab1'] = window.tabs.activeTab.id;
+ next(index);
+ }
+ });
+ },
+ function (index) {
+ win2 = windows.open({
+ url: "data:text/html;charset=utf-8,foo",
+ onOpen: function(window) {
+ tabIds['tab2'] = window.tabs.activeTab.id;
+ next(index);
+ }
+ });
+ },
+ function (index) {
+ test.assertNotEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique.");
+ win1.close();
+ win2.close();
+ test.done();
+ }
+ ];
+
+ function next(index) {
+ if (index === steps.length) {
+ return;
+ }
+ let fn = steps[index];
+ index++
+ fn(index);
+ }
+
+ // run!
+ next(0);
+}
+
/******************* helpers *********************/
// Helper for getting the active window
View
54 test/test-content-worker.js
@@ -654,6 +654,60 @@ exports["test:check worker API with page history"] = WorkerTest(
}
);
+exports["test:global postMessage"] = WorkerTest(
+ DEFAULT_CONTENT_URL,
+ function(assert, browser, done) {
+ // Create a new module loader in order to be able to create a `console`
+ // module mockup:
+ let loader = Loader(module, {
+ console: {
+ log: hook.bind(null, "log"),
+ info: hook.bind(null, "info"),
+ warn: hook.bind(null, "warn"),
+ error: hook.bind(null, "error"),
+ debug: hook.bind(null, "debug"),
+ exception: hook.bind(null, "exception")
+ }
+ });
+
+ // Intercept all console method calls
+ let seenMessages = 0;
+ function hook(type, message) {
+ seenMessages++;
+ assert.equal(type, "error", "Should be an error");
+ assert.equal(message, "DEPRECATED: The global `postMessage()` function in " +
+ "content scripts is deprecated in favor of the " +
+ "`self.postMessage()` function, which works the same. " +
+ "Replace calls to `postMessage()` with calls to " +
+ "`self.postMessage()`." +
+ "For more info on `self.on`, see " +
+ "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.",
+ "Should have seen the deprecation message")
+ }
+
+ assert.notEqual(browser.contentWindow.location.href, "about:blank",
+ "window is now on the right document");
+
+ let window = browser.contentWindow
+ let worker = loader.require("sdk/content/worker").Worker({
+ window: window,
+ contentScript: "new " + function WorkerScope() {
+ postMessage("success");
+ },
+ contentScriptWhen: "ready",
+ onMessage: function(msg) {
+ assert.equal("success", msg, "Should have seen the right postMessage call");
+ assert.equal(1, seenMessages, "Should have seen the deprecation message");
+ done();
+ }
+ });
+
+ assert.equal(worker.url, window.location.href,
+ "worker.url works");
+ worker.postMessage("hi!");
+ }
+);
+
if (require("sdk/system/xul-app").is("Fennec")) {
module.exports = {
"test Unsupported Test": function UnsupportedTest (assert) {
View
48 test/test-selection.js
@@ -60,6 +60,20 @@ function close() {
closeTab(getActiveTab(getMostRecentBrowserWindow()));
}
+/**
+ * Reload the window given and return a promise, that will be resolved with the
+ * content window after a small delay.
+ */
+function reload(window) {
+ let { promise, resolve } = defer();
+
+ window.location.reload(true);
+
+ setTimeout(resolve, 250, window);
+
+ return promise;
+}
+
// Selection's unit test utility function
/**
@@ -676,6 +690,40 @@ exports["test Textarea OnSelect Listener on existing document"] = function(asser
then(loader.unload)
};
+exports["test Selection Listener on document reload"] = function(assert, done) {
+ let loader = Loader(module);
+ let selection = loader.require("sdk/selection");
+
+ selection.once("select", function() {
+ assert.equal(selection.text, "fo");
+ done();
+ });
+
+ open(URL).
+ then(reload).
+ then(selectContentFirstDiv).
+ then(dispatchSelectionEvent).
+ then(close).
+ then(loader.unload);
+};
+
+exports["test Textarea OnSelect Listener on document reload"] = function(assert, done) {
+ let loader = Loader(module);
+ let selection = loader.require("sdk/selection");
+
+ selection.once("select", function() {
+ assert.equal(selection.text, "noodles");
+ done();
+ });
+
+ open(URL).
+ then(reload).
+ then(selectTextarea).
+ then(dispatchOnSelectEvent).
+ then(close).
+ then(loader.unload);
+};
+
// TODO: test Selection Listener on long-held connection (Bug 661884)
//
// I didn't find a way to do so with httpd, using `processAsync` I'm able to
Please sign in to comment.
Something went wrong with that request. Please try again.