diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index b66de7faa3a..4ed0359eba5 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index b84c84fd097..7f2f8731f85 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 6ee4d572d82..fd5b6344a4c 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index b66de7faa3a..4ed0359eba5 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index c14e789823d..6f08d6b887f 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"branch": "",
"revision": ""
},
- "revision": "1b537b86025c437cd428abbc2996f376f3b78799",
+ "revision": "de57cd03d28aec6c8d25572741e49348277d283e",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index 0452065dfa8..52ed15ae1de 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index a096993b0e6..6119499f12a 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml
index b5fff2acefe..f0ceef721b5 100644
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml
index 1446d5bf912..08365f5ca30 100644
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml
index 7f60be99e8d..03513a9a808 100644
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index 9aff13e66e6..bf44b763c1b 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js b/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
index d33d00cfc87..57881460dd0 100644
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -23,15 +23,20 @@ function test() {
Task.spawn(function* () {
try {
- yield ensureSourceIs(gPanel, CODE_URL, true);
+ // Refresh and hit the debugger statement before the location we want to
+ // set our breakpoints. We have to pause before the breakpoint locations
+ // so that GC doesn't get a chance to kick in and collect the IIFE's
+ // script, which would causes us to receive a 'noScript' error from the
+ // server when we try to set the breakpoints.
+ const [paused, ] = yield promise.all([
+ waitForThreadEvents(gPanel, "paused"),
+ reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN),
+ ]);
- // Pause and set our breakpoints.
- yield doInterrupt();
+ is(paused.why.type, "debuggerStatement");
+
+ // Set our breakpoints.
const [bp1, bp2, bp3] = yield promise.all([
- setBreakpoint({
- url: CODE_URL,
- line: 2
- }),
setBreakpoint({
url: CODE_URL,
line: 3
@@ -39,23 +44,31 @@ function test() {
setBreakpoint({
url: CODE_URL,
line: 4
+ }),
+ setBreakpoint({
+ url: CODE_URL,
+ line: 5
})
]);
- // Should hit the first breakpoint on reload.
+ // Refresh and hit the debugger statement again.
yield promise.all([
reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN),
- waitForCaretUpdated(gPanel, 2)
+ waitForCaretAndScopes(gPanel, 1)
]);
- // And should hit the other breakpoints as we resume.
+ // And we should hit the breakpoints as we resume.
yield promise.all([
doResume(),
- waitForCaretUpdated(gPanel, 3)
+ waitForCaretAndScopes(gPanel, 3)
]);
yield promise.all([
doResume(),
- waitForCaretUpdated(gPanel, 4)
+ waitForCaretAndScopes(gPanel, 4)
+ ]);
+ yield promise.all([
+ doResume(),
+ waitForCaretAndScopes(gPanel, 5)
]);
// Clean up the breakpoints.
diff --git a/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js b/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js
index f2078e9c40b..a8e8a79731c 100644
--- a/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js
+++ b/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -1,3 +1,4 @@
+debugger;
var a = (function(){
var b = 9;
console.log("x", b);
diff --git a/browser/modules/UITour.jsm b/browser/modules/UITour.jsm
index e17fd83c3fc..8ae7182bbcf 100644
--- a/browser/modules/UITour.jsm
+++ b/browser/modules/UITour.jsm
@@ -811,9 +811,9 @@ this.UITour = {
let minDimension = Math.min(highlightHeight, highlightWidth);
let maxDimension = Math.max(highlightHeight, highlightWidth);
- // If the dimensions are within 110% of each other (to include the bookmarks button),
+ // If the dimensions are within 200% of each other (to include the bookmarks button),
// make the highlight a circle with the largest dimension as the diameter.
- if (maxDimension / minDimension <= 2.1) {
+ if (maxDimension / minDimension <= 3.0) {
highlightHeight = highlightWidth = maxDimension;
highlighter.style.borderRadius = "100%";
} else {
diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js
index 646fb0fabad..338adcb4589 100644
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1918,8 +1918,7 @@ var NativeWindow = {
},
contextmenus: {
items: {}, // a list of context menu items that we may show
- _nativeItemsSeparator: 0, // the index to insert native context menu items at
- _contextId: 0, // id to assign to new context menu items if they are added
+ DEFAULT_HTML5_ORDER: -1, // Sort order for HTML5 context menu items
init: function() {
Services.obs.addObserver(this, "Gesture:LongPress", false);
@@ -1929,27 +1928,26 @@ var NativeWindow = {
Services.obs.removeObserver(this, "Gesture:LongPress");
},
- add: function(aName, aSelector, aCallback) {
- if (!aName)
+ add: function() {
+ let args;
+ if (arguments.length == 1) {
+ args = arguments[0];
+ } else if (arguments.length == 3) {
+ args = {
+ label : arguments[0],
+ selector: arguments[1],
+ callback: arguments[2]
+ };
+ } else {
+ throw "Incorrect number of parameters";
+ }
+
+ if (!args.label)
throw "Menu items must have a name";
- let item = {
- name: aName,
- context: aSelector,
- callback: aCallback,
- matches: function(aElt, aX, aY) {
- return this.context.matches(aElt, aX, aY);
- },
- getValue: function(aElt) {
- return {
- label: (typeof this.name == "function") ? this.name(aElt) : this.name,
- id: this.id
- }
- }
- };
- item.id = this._contextId++;
- this.items[item.id] = item;
- return item.id;
+ let cmItem = new ContextMenuItem(args);
+ this.items[cmItem.id] = cmItem;
+ return cmItem.id;
},
remove: function(aId) {
@@ -2114,130 +2112,129 @@ var NativeWindow = {
else this._targetRef = null;
},
- _addHTMLContextMenuItems: function cm_addContextMenuItems(aMenu, aParent) {
- for (let i = 0; i < aMenu.childNodes.length; i++) {
- let item = aMenu.childNodes[i];
- if (!item.label)
+ _addHTMLContextMenuItemsForElement: function(element) {
+ let htmlMenu = element.contextMenu;
+ if (!htmlMenu)
+ return;
+
+ htmlMenu.QueryInterface(Components.interfaces.nsIHTMLMenu);
+ htmlMenu.sendShowEvent();
+
+ this._addHTMLContextMenuItemsForMenu(htmlMenu, element);
+ },
+
+ _addHTMLContextMenuItemsForMenu: function(menu, target) {
+ for (let i = 0; i < menu.childNodes.length; i++) {
+ let elt = menu.childNodes[i];
+ if (!elt.label)
continue;
- let id = this._contextId++;
- let menuitem = {
- id: id,
- isGroup: false,
- callback: (function(aTarget, aX, aY) {
- // If this is a menu item, show a new context menu with the submenu in it
- if (item instanceof Ci.nsIDOMHTMLMenuElement) {
- this.menuitems = [];
- this._nativeItemsSeparator = 0;
-
- this._addHTMLContextMenuItems(item, id);
- this._innerShow(aTarget, aX, aY);
- } else {
- // oltherwise just click the item
- item.click();
- }
- }).bind(this),
-
- getValue: function(aElt) {
- if (item.hasAttribute("hidden"))
- return null;
-
- return {
- icon: item.icon,
- label: item.label,
- id: id,
- disabled: item.disabled,
- parent: item instanceof Ci.nsIDOMHTMLMenuElement
- }
- }
- };
-
- this.menuitems.splice(this._nativeItemsSeparator, 0, menuitem);
- this._nativeItemsSeparator++;
+ this.menuitems.push(new HTMLContextMenuItem(elt, target));
}
},
- _getMenuItemForId: function(aId) {
+ _containsItem: function(aId) {
if (!this.menuitems)
return null;
- for (let i = 0; i < this.menuitems.length; i++) {
- if (this.menuitems[i].id == aId)
- return this.menuitems[i];
+ let menu = this.menuitems;
+ for (let i = 0; i < menu.length; i++) {
+ if (menu[i].id == aId)
+ return menu[i];
}
+
return null;
},
+ shouldShow: function() {
+ return this.menuitems.length > 0;
+ },
+
+ _addNativeContextMenuItems: function(element, x, y) {
+ for (let itemId of Object.keys(this.items)) {
+ let item = this.items[itemId];
+
+ if (!this._containsItem(item.id) && item.matches(element, x, y)) {
+ this.menuitems.push(item);
+ }
+ }
+ },
+
// Checks if there are context menu items to show, and if it finds them
// sends a contextmenu event to content. We also send showing events to
// any html5 context menus we are about to show
- _sendToContent: function(aX, aY) {
- // find and store the top most element this context menu is being shown for
- // use the highlighted element if possible, otherwise look for nearby clickable elements
- // If we still don't find one we fall back to using anything
- let target = BrowserEventHandler._highlightElement || ElementTouchHelper.elementFromPoint(aX, aY);
+ _sendToContent: function(x, y) {
+ let target = BrowserEventHandler._highlightElement || ElementTouchHelper.elementFromPoint(x, y);
if (!target)
- target = ElementTouchHelper.anyElementFromPoint(aX, aY);
+ target = ElementTouchHelper.anyElementFromPoint(x, y);
if (!target)
return;
- // store a weakref to the target to be used when the context menu event returns
this._target = target;
- this.menuitems = [];
- let menuitemsSet = false;
-
Services.obs.notifyObservers(null, "before-build-contextmenu", "");
-
- // now walk up the tree and for each node look for any context menu items that apply
- let element = target;
- this._nativeItemsSeparator = 0;
- while (element) {
- // first check for any html5 context menus that might exist
- let contextmenu = element.contextMenu;
- if (contextmenu) {
- // send this before we build the list to make sure the site can update the menu
- contextmenu.QueryInterface(Components.interfaces.nsIHTMLMenu);
- contextmenu.sendShowEvent();
- this._addHTMLContextMenuItems(contextmenu, null);
- }
-
- // then check for any context menu items registered in the ui
- for (let itemId of Object.keys(this.items)) {
- let item = this.items[itemId];
- if (!this._getMenuItemForId(item.id) && item.matches(element, aX, aY)) {
- this.menuitems.push(item);
- }
- }
-
- element = element.parentNode;
- }
+ this._buildMenu(x, y);
// only send the contextmenu event to content if we are planning to show a context menu (i.e. not on every long tap)
- if (this.menuitems.length > 0) {
+ if (this.shouldShow()) {
let event = target.ownerDocument.createEvent("MouseEvent");
- event.initMouseEvent("contextmenu", true, true, content,
- 0, aX, aY, aX, aY, false, false, false, false,
+ event.initMouseEvent("contextmenu", true, true, target.defaultView,
+ 0, x, y, x, y, false, false, false, false,
0, null);
target.ownerDocument.defaultView.addEventListener("contextmenu", this, false);
target.dispatchEvent(event);
} else {
- this._target = null;
- BrowserEventHandler._cancelTapHighlight();
+ this.menuitems = null;
+ Services.obs.notifyObservers({target: target, x: x, y: y}, "context-menu-not-shown", "");
if (SelectionHandler.canSelect(target)) {
if (!SelectionHandler.startSelection(target, {
- mode: SelectionHandler.SELECT_AT_POINT,
- x: aX,
- y: aY
- })) {
+ mode: SelectionHandler.SELECT_AT_POINT,
+ x: x,
+ y: y
+ })) {
SelectionHandler.attachCaret(target);
}
}
}
},
+ _getTitle: function(node) {
+ if (node.hasAttribute && node.hasAttribute("title")) {
+ return node.getAttribute("title");
+ }
+ return this._getUrl(node);
+ },
+
+ _getUrl: function(node) {
+ if ((node instanceof Ci.nsIDOMHTMLAnchorElement && node.href) ||
+ (node instanceof Ci.nsIDOMHTMLAreaElement && node.href)) {
+ return this._getLinkURL(node);
+ } else if (node instanceof Ci.nsIImageLoadingContent && node.currentURI) {
+ return node.currentURI.spec;
+ } else if (node instanceof Ci.nsIDOMHTMLMediaElement) {
+ return (node.currentSrc || node.src);
+ }
+ return "";
+ },
+
+ _buildMenu: function(x, y) {
+ // now walk up the tree and for each node look for any context menu items that apply
+ let element = this._target;
+ this.menuitems = [];
+
+ while (element) {
+ // First check for any html5 context menus that might exist...
+ this._addHTMLContextMenuItemsForElement(element);
+ // then check for any context menu items registered in the ui.
+ this._addNativeContextMenuItems(element, x, y);
+
+ // walk up the tree and find more items to show
+ element = element.parentNode;
+ }
+ },
+
// Actually shows the native context menu by passing a list of context menu items to
// show to the Java.
_show: function(aEvent) {
@@ -2249,69 +2246,83 @@ var NativeWindow = {
this._innerShow(popupNode, aEvent.clientX, aEvent.clientY);
},
- _innerShow: function(aTarget, aX, aY) {
+ _findTitle: function(node) {
+ let title = "";
+ while(node && !title) {
+ title = this._getTitle(node);
+ node = node.parentNode;
+ }
+ return title;
+ },
+
+ _getItems: function(target) {
+ return this._getItemsInList(target, this.menuitems);
+ },
+
+ _getItemsInList: function(target, list) {
+ let itemArray = [];
+ for (let i = 0; i < list.length; i++) {
+ let t = target;
+ while(t) {
+ if (list[i].matches(t)) {
+ let val = list[i].getValue(t);
+
+ // hidden menu items will return null from getValue
+ if (val) {
+ itemArray.push(val);
+ break;
+ }
+ }
+
+ t = t.parentNode;
+ }
+ }
+ return itemArray;
+ },
+
+ _innerShow: function(target, x, y) {
Haptic.performSimpleAction(Haptic.LongPress);
// spin through the tree looking for a title for this context menu
- let node = aTarget;
- let title ="";
- while(node && !title) {
- if (node.hasAttribute && node.hasAttribute("title")) {
- title = node.getAttribute("title");
- } else if ((node instanceof Ci.nsIDOMHTMLAnchorElement && node.href) ||
- (node instanceof Ci.nsIDOMHTMLAreaElement && node.href)) {
- title = this._getLinkURL(node);
- } else if (node instanceof Ci.nsIImageLoadingContent && node.currentURI) {
- title = node.currentURI.spec;
- } else if (node instanceof Ci.nsIDOMHTMLMediaElement) {
- title = (node.currentSrc || node.src);
- }
- node = node.parentNode;
- }
+ let title = this._findTitle(target);
- // convert this.menuitems object to an array for sending to native code
- let itemArray = [];
- for (let i = 0; i < this.menuitems.length; i++) {
- let val = this.menuitems[i].getValue(aTarget);
-
- // hidden menu items will return null from getValue
- if (val)
- itemArray.push(val);
- }
-
- if (itemArray.length == 0)
- return;
+ this.menuitems.sort((a,b) => {
+ if (a.order == b.order) return 0;
+ return (a.order > b.order) ? 1 : -1;
+ });
let prompt = new Prompt({
- window: aTarget.ownerDocument.defaultView,
+ window: target.ownerDocument.defaultView,
title: title
- }).setSingleChoiceItems(itemArray)
- .show((function(data) {
- if (data.button == -1) {
- // prompt was cancelled
- return;
- }
+ });
- let selectedId = itemArray[data.button].id;
- let selectedItem = this._getMenuItemForId(selectedId);
+ let items = this._getItems(target);
+ prompt.setSingleChoiceItems(items);
+ prompt.show(this._promptDone.bind(this, target, x, y, items));
+ },
- this.menuitems = null;
- if (selectedItem && selectedItem.callback) {
- if (selectedItem.matches) {
- // for menuitems added using the native UI, pass the dom element that matched that item to the callback
- while (aTarget) {
- if (selectedItem.matches(aTarget, aX, aY)) {
- selectedItem.callback.call(selectedItem, aTarget, aX, aY);
- break;
- }
- aTarget = aTarget.parentNode;
- }
- } else {
- // if this was added using the html5 context menu api, just click on the context menu item
- selectedItem.callback.call(selectedItem, aTarget, aX, aY);
- }
+ _promptDone: function(target, x, y, items, data) {
+ if (data.button == -1) {
+ // prompt was cancelled
+ return;
+ }
+
+ let selectedItemId = items[data.list[0]].id;
+ let selectedItem = this._containsItem(selectedItemId);
+ this.menuitems = null;
+
+ if (!selectedItem || !selectedItem.matches || !selectedItem.callback) {
+ return;
+ }
+
+ // for menuitems added using the native UI, pass the dom element that matched that item to the callback
+ while (target) {
+ if (selectedItem.matches(target, x, y)) {
+ selectedItem.callback(target, x, y);
+ break;
}
- }).bind(this));
+ target = target.parentNode;
+ }
},
handleEvent: function(aEvent) {
@@ -2343,7 +2354,7 @@ var NativeWindow = {
aElement instanceof Ci.nsIDOMHTMLLinkElement ||
aElement.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
try {
- let url = NativeWindow.contextmenus._getLinkURL(aElement);
+ let url = this._getLinkURL(aElement);
return Services.io.newURI(url, null, null);
} catch (e) {}
}
@@ -8264,3 +8275,108 @@ var Tabs = {
}
},
};
+
+function ContextMenuItem(args) {
+ this.id = uuidgen.generateUUID().toString();
+ this.args = args;
+}
+
+ContextMenuItem.prototype = {
+ get order() {
+ return this.args.order || 0;
+ },
+
+ matches: function(elt, x, y) {
+ return this.args.selector.matches(elt, x, y);
+ },
+
+ callback: function(elt) {
+ this.args.callback(elt);
+ },
+
+ addVal: function(name, elt, defaultValue) {
+ if (!(name in this.args))
+ return defaultValue;
+
+ if (typeof this.args[name] == "function")
+ return this.args[name](elt);
+
+ return this.args[name];
+ },
+
+ getValue: function(elt) {
+ return {
+ id: this.id,
+ label: this.addVal("label", elt),
+ shareData: this.addVal("shareData", elt),
+ icon: this.addVal("icon", elt),
+ isGroup: this.addVal("isGroup", elt, false),
+ inGroup: this.addVal("inGroup", elt, false),
+ disabled: this.addVal("disabled", elt, false),
+ selected: this.addVal("selected", elt, false),
+ isParent: this.addVal("isParent", elt, false),
+ };
+ }
+}
+
+function HTMLContextMenuItem(elt, target) {
+ ContextMenuItem.call(this, { });
+
+ this.menuElementRef = Cu.getWeakReference(elt);
+ this.targetElementRef = Cu.getWeakReference(target);
+}
+
+HTMLContextMenuItem.prototype = Object.create(ContextMenuItem.prototype, {
+ order: {
+ value: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER
+ },
+
+ matches: {
+ value: function(target) {
+ let t = this.targetElementRef.get();
+ return t === target;
+ },
+ },
+
+ callback: {
+ value: function(target) {
+ let elt = this.menuElementRef.get();
+ if (!elt) {
+ return;
+ }
+
+ // If this is a menu item, show a new context menu with the submenu in it
+ if (elt instanceof Ci.nsIDOMHTMLMenuElement) {
+ try {
+ NativeWindow.contextmenus.menuitems = [];
+ NativeWindow.contextmenus._addHTMLContextMenuItemsForMenu(elt, target);
+ NativeWindow.contextmenus._innerShow(target);
+ } catch(ex) {
+ Cu.reportError(ex);
+ }
+ } else {
+ // otherwise just click the menu item
+ elt.click();
+ }
+ },
+ },
+
+ getValue: {
+ value: function(target) {
+ let elt = this.menuElementRef.get();
+ if (!elt)
+ return null;
+
+ if (elt.hasAttribute("hidden"))
+ return null;
+
+ return {
+ id: this.id,
+ icon: elt.icon,
+ label: elt.label,
+ disabled: elt.disabled,
+ menu: elt instanceof Ci.nsIDOMHTMLMenuElement
+ };
+ }
+ },
+});
diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js
index 3eb542a62a2..841d3d47eca 100644
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -1362,7 +1362,13 @@ ThreadActor.prototype = {
if (line == null ||
line < 0 ||
this.dbg.findScripts({ url: url }).length == 0) {
- return { error: "noScript" };
+ return {
+ error: "noScript",
+ message: "Requested setting a breakpoint on "
+ + url + ":" + line
+ + (column != null ? ":" + column : "")
+ + " but there is no Debugger.Script at that location"
+ };
}
let response = this._createAndStoreBreakpoint({
@@ -1450,6 +1456,10 @@ ThreadActor.prototype = {
if (scripts.length == 0) {
return {
error: "noScript",
+ message: "Requested setting a breakpoint on "
+ + aLocation.url + ":" + aLocation.line
+ + (aLocation.column != null ? ":" + aLocation.column : "")
+ + " but there is no Debugger.Script at that location",
actor: actor.actorID
};
}