Merge fx-team to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-05-04 15:53:32 -04:00
commit ffa09572aa
50 changed files with 803 additions and 264 deletions

View File

@ -30,7 +30,7 @@ var permissionObserver = {
function onLoadPermission()
{
var uri = gDocument.documentURIObject;
var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
var permTab = document.getElementById("permTab");
if (SitePermissions.isSupportedURI(uri)) {
gPermURI = uri;

View File

@ -3,6 +3,8 @@
* 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/. */
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
var security = {
// Display the server certificate (static)
viewCert : function () {
@ -135,7 +137,7 @@ var security = {
getService(Components.interfaces.nsIEffectiveTLDService);
var eTLD;
var uri = gDocument.documentURIObject;
var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
try {
eTLD = eTLDService.getBaseDomain(uri);
}
@ -232,7 +234,7 @@ function securityOnLoad() {
var yesStr = pageInfoBundle.getString("yes");
var noStr = pageInfoBundle.getString("no");
var uri = gDocument.documentURIObject;
var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
setText("security-privacy-cookies-value",
hostHasCookies(uri) ? yesStr : noStr);
setText("security-privacy-passwords-value",

View File

@ -174,7 +174,6 @@ skip-if = buildapp == 'mulet' || e10s # Bug 1099156 - test directly manipulates
skip-if = buildapp == 'mulet'
[browser_bug455852.js]
[browser_bug460146.js]
skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
[browser_bug462289.js]
skip-if = toolkit == "cocoa" || e10s # Bug 1102017 - middle-button mousedown on selected tab2 does not activate tab - Didn't expect [object XULElement], but got it
[browser_bug462673.js]
@ -187,7 +186,6 @@ skip-if = buildapp == 'mulet'
skip-if = buildapp == 'mulet'
[browser_bug495058.js]
[browser_bug517902.js]
skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
[browser_bug519216.js]
[browser_bug520538.js]
[browser_bug521216.js]
@ -335,7 +333,7 @@ skip-if = buildapp == 'mulet' || e10s # Bug 1100662 - content access causing unc
skip-if = buildapp == 'mulet' || e10s # Bug 1093603 - test breaks with PopupNotifications.panel.firstElementChild is null
[browser_overflowScroll.js]
[browser_pageInfo.js]
skip-if = buildapp == 'mulet' || e10s # Bug 866413 - PageInfo doesn't work in e10s
skip-if = buildapp == 'mulet'
[browser_page_style_menu.js]
[browser_parsable_css.js]

View File

@ -1,6 +1,4 @@
/* 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";
/*
* Test bug 427559 to make sure focused elements that are no longer on the page
@ -8,42 +6,48 @@
* tab with the now-gone element.
*/
// Default focus on a button and have it kill itself on blur
let testPage = 'data:text/html,<body><button onblur="this.parentNode.removeChild(this);"><script>document.body.firstChild.focus();</script></body>';
// Default focus on a button and have it kill itself on blur.
const URL = 'data:text/html;charset=utf-8,' +
'<body><button onblur="this.remove()">' +
'<script>document.body.firstChild.focus()</script></body>';
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
var browser = gBrowser.selectedBrowser;
browser.addEventListener("load", function () {
browser.removeEventListener("load", arguments.callee, true);
executeSoon(function () {
var testPageWin = content;
is(browser.contentDocumentAsCPOW.activeElement.localName, "button", "button is focused");
addEventListener("focus", function focusedWindow(event) {
if (!String(event.target.location).startsWith("data:"))
return;
removeEventListener("focus", focusedWindow, true);
// Make sure focus is given to the window because the element is now gone
is(browser.contentDocumentAsCPOW.activeElement.localName, "body", "body is focused");
gBrowser.removeCurrentTab();
finish();
}, true);
// The test page loaded, so open an empty tab, select it, then restore
// the test tab. This causes the test page's focused element to be removed
// from its document.
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.removeCurrentTab();
});
}, true);
content.location = testPage;
function getFocusedLocalName(browser) {
return ContentTask.spawn(browser, null, function* () {
return content.document.activeElement.localName;
});
}
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab(URL);
let browser = gBrowser.selectedBrowser;
yield BrowserTestUtils.browserLoaded(browser);
is((yield getFocusedLocalName(browser)), "button", "button is focused");
let promiseFocused = ContentTask.spawn(browser, null, function* () {
return new Promise(resolve => {
content.addEventListener("focus", function onFocus({target}) {
if (String(target.location).startsWith("data:")) {
content.removeEventListener("focus", onFocus);
resolve();
}
});
});
});
// The test page loaded, so open an empty tab, select it, then restore
// the test tab. This causes the test page's focused element to be removed
// from its document.
gBrowser.selectedTab = gBrowser.addTab("about:blank");
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
gBrowser.removeCurrentTab();
// Wait until the original tab is focused again.
yield promiseFocused;
// Make sure focus is given to the window because the element is now gone.
is((yield getFocusedLocalName(browser)), "body", "body is focused");
// Cleanup.
gBrowser.removeCurrentTab();
});

View File

@ -79,7 +79,6 @@ skip-if = !crashreporter
[browser_pluginCrashCommentAndURL.js]
skip-if = !crashreporter
[browser_pageInfo_plugins.js]
skip-if = e10s # Bug 866413
[browser_pluginplaypreview.js]
skip-if = e10s # bug 1148827
[browser_pluginplaypreview2.js]

View File

@ -468,10 +468,14 @@ let gEditItemOverlay = {
if (!anyChanges)
return false;
// The panel could have been closed in the meanwhile.
if (!this._paneInfo)
return false;
// Ensure the tagsField is in sync, clean it up from empty tags
currentTags = this._paneInfo.bulkTagging ?
this._getCommonTags() :
PlacesUtils.tagging.getTagsForURI(this._uri);
PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
this._initTextField(this._tagsField, currentTags.join(", "), false);
return true;
}),
@ -505,7 +509,7 @@ let gEditItemOverlay = {
// Here we update either the item title or its cached static title
let newTitle = this._namePicker.value;
if (!newTitle &&
PlacesUtils.bookmarks.getFolderIdForItem(itemId) == PlacesUtils.tagsFolderId) {
PlacesUtils.bookmarks.getFolderIdForItem(this._paneInfo.itemId) == PlacesUtils.tagsFolderId) {
// We don't allow setting an empty title for a tag, restore the old one.
this._initNamePicker();
}
@ -656,17 +660,17 @@ let gEditItemOverlay = {
* The identifier of the bookmarks folder.
*/
_getFolderMenuItem(aFolderId) {
let menuPopup = this._folderMenuList.menupopup;
let menupopup = this._folderMenuList.menupopup;
let menuItem = Array.prototype.find.call(
menuPopup.childNodes, menuItem => menuItem.folderId === aFolderId);
menupopup.childNodes, menuItem => menuItem.folderId === aFolderId);
if (menuItem !== undefined)
return menuItem;
// 3 special folders + separator + folder-items-count limit
if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
menupopup.removeChild(menuPopup.lastChild);
menupopup.removeChild(menupopup.lastChild);
return this._appendFolderItemToMenupopup(menuPopup, aFolderId);
return this._appendFolderItemToMenupopup(menupopup, aFolderId);
},
onFolderMenuListCommand(aEvent) {
@ -688,7 +692,8 @@ let gEditItemOverlay = {
// Move the item
let containerId = this._getFolderIdFromMenuList();
if (PlacesUtils.bookmarks.getFolderIdForItem(this._paneInfo.itemId) != containerId) {
if (PlacesUtils.bookmarks.getFolderIdForItem(this._paneInfo.itemId) != containerId &&
this._paneInfo.itemId != containerId) {
if (PlacesUIUtils.useAsyncTransactions) {
Task.spawn(function* () {
let newParentGuid = yield PlacesUtils.promiseItemGuid(containerId);
@ -708,7 +713,7 @@ let gEditItemOverlay = {
if (containerId != PlacesUtils.unfiledBookmarksFolderId &&
containerId != PlacesUtils.toolbarFolderId &&
containerId != PlacesUtils.bookmarksMenuFolderId) {
this._markFolderAsRecentlyUsed(container)
this._markFolderAsRecentlyUsed(containerId)
.catch(Components.utils.reportError);
}
}
@ -718,8 +723,8 @@ let gEditItemOverlay = {
if (!folderTreeRow.collapsed) {
var selectedNode = this._folderTree.selectedNode;
if (!selectedNode ||
PlacesUtils.getConcreteItemId(selectedNode) != container)
this._folderTree.selectItems([container]);
PlacesUtils.getConcreteItemId(selectedNode) != containerId)
this._folderTree.selectItems([containerId]);
}
},
@ -750,13 +755,15 @@ let gEditItemOverlay = {
let annotation = this._getLastUsedAnnotationObject(false);
while (this._recentFolders.length > MAX_FOLDER_ITEM_IN_MENU_LIST) {
let folderId = this._recentFolders.pop().folderId;
let annoTxn = new PlacesSetItemAnnotationTransaction(folderId, anno);
let annoTxn = new PlacesSetItemAnnotationTransaction(folderId,
annotation);
txns.push(annoTxn);
}
// Mark folder as recently used
annotation = this._getLastUsedAnnotationObject(true);
let annoTxn = new PlacesSetItemAnnotationTransaction(aFolderId, anno);
let annoTxn = new PlacesSetItemAnnotationTransaction(aFolderId,
annotation);
txns.push(annoTxn);
let aggregate =
@ -983,7 +990,7 @@ let gEditItemOverlay = {
return;
if (aItemId == this._paneInfo.itemId) {
this._paneInfo.title = aNewTitle;
this._initTextField(this._namePicker);
this._initTextField(this._namePicker, aNewTitle);
}
else if (this._paneInfo.visibleRows.has("folderRow")) {
// If the title of a folder which is listed within the folders
@ -991,7 +998,7 @@ let gEditItemOverlay = {
// representing element.
let menupopup = this._folderMenuList.menupopup;
for (menuitem of menupopup.childNodes) {
if ("folderId" in menuItem && menuItem.folderId == aItemId) {
if ("folderId" in menuitem && menuitem.folderId == aItemId) {
menuitem.label = aNewTitle;
break;
}
@ -1002,16 +1009,18 @@ let gEditItemOverlay = {
// nsINavBookmarkObserver
onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aValue,
aLastModified, aItemType) {
if (aProperty == "tags" && this._paneInfo.visibleRows.has("tagsRow"))
if (aProperty == "tags" && this._paneInfo.visibleRows.has("tagsRow")) {
this._onTagsChange(aItemId);
else if (!this._paneInfo.isItem || this._paneInfo.itemId != aItemId)
}
else if (aProperty == "title" && this._paneInfo.isItem) {
// This also updates titles of folders in the folder menu list.
this._onItemTitleChange(aItemId, aValue);
}
else if (!this._paneInfo.isItem || this._paneInfo.itemId != aItemId) {
return;
}
switch (aProperty) {
case "title":
if (this._paneInfo.isItem)
this._onItemTitleChange(aItemId, aValue);
break;
case "uri":
let newURI = NetUtil.newURI(aValue);
if (!newURI.equals(this._paneInfo.uri)) {

View File

@ -1225,7 +1225,6 @@ var ViewMenu = {
url: { key: "URI", dir: "ascending" },
date: { key: "DATE", dir: "descending" },
visitCount: { key: "VISITCOUNT", dir: "descending" },
keyword: { key: "KEYWORD", dir: "ascending" },
dateAdded: { key: "DATEADDED", dir: "descending" },
lastModified: { key: "LASTMODIFIED", dir: "descending" },
description: { key: "ANNOTATION",

View File

@ -390,9 +390,6 @@
<treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.keyword.label;" id="placesContentKeyword" anonid="keyword" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>
<treecol label="&col.description.label;" id="placesContentDescription" anonid="description" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/>

View File

@ -518,11 +518,10 @@ PlacesTreeView.prototype = {
COLUMN_TYPE_URI: 2,
COLUMN_TYPE_DATE: 3,
COLUMN_TYPE_VISITCOUNT: 4,
COLUMN_TYPE_KEYWORD: 5,
COLUMN_TYPE_DESCRIPTION: 6,
COLUMN_TYPE_DATEADDED: 7,
COLUMN_TYPE_LASTMODIFIED: 8,
COLUMN_TYPE_TAGS: 9,
COLUMN_TYPE_DESCRIPTION: 5,
COLUMN_TYPE_DATEADDED: 6,
COLUMN_TYPE_LASTMODIFIED: 7,
COLUMN_TYPE_TAGS: 8,
_getColumnType: function PTV__getColumnType(aColumn) {
let columnType = aColumn.element.getAttribute("anonid") || aColumn.id;
@ -536,8 +535,6 @@ PlacesTreeView.prototype = {
return this.COLUMN_TYPE_DATE;
case "visitCount":
return this.COLUMN_TYPE_VISITCOUNT;
case "keyword":
return this.COLUMN_TYPE_KEYWORD;
case "description":
return this.COLUMN_TYPE_DESCRIPTION;
case "dateAdded":
@ -568,10 +565,6 @@ PlacesTreeView.prototype = {
return [this.COLUMN_TYPE_VISITCOUNT, false];
case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING:
return [this.COLUMN_TYPE_VISITCOUNT, true];
case Ci.nsINavHistoryQueryOptions.SORT_BY_KEYWORD_ASCENDING:
return [this.COLUMN_TYPE_KEYWORD, false];
case Ci.nsINavHistoryQueryOptions.SORT_BY_KEYWORD_DESCENDING:
return [this.COLUMN_TYPE_KEYWORD, true];
case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_ASCENDING:
if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
return [this.COLUMN_TYPE_DESCRIPTION, false];
@ -849,9 +842,7 @@ PlacesTreeView.prototype = {
this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS);
},
nodeKeywordChanged: function PTV_nodeKeywordChanged(aNode, aNewKeyword) {
this._invalidateCellValue(aNode, this.COLUMN_TYPE_KEYWORD);
},
nodeKeywordChanged(aNode, aNewKeyword) {},
nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) {
if (aAnno == PlacesUIUtils.DESCRIPTION_ANNO) {
@ -1444,10 +1435,6 @@ PlacesTreeView.prototype = {
return this._convertPRTimeToString(nodeTime);
case this.COLUMN_TYPE_VISITCOUNT:
return node.accessCount;
case this.COLUMN_TYPE_KEYWORD:
if (PlacesUtils.nodeIsBookmark(node))
return PlacesUtils.bookmarks.getKeywordForBookmark(node.itemId);
return "";
case this.COLUMN_TYPE_DESCRIPTION:
if (node.itemId != -1) {
try {
@ -1582,15 +1569,6 @@ PlacesTreeView.prototype = {
else
newSort = NHQO.SORT_BY_VISITCOUNT_DESCENDING;
break;
case this.COLUMN_TYPE_KEYWORD:
if (oldSort == NHQO.SORT_BY_KEYWORD_ASCENDING)
newSort = NHQO.SORT_BY_KEYWORD_DESCENDING;
else if (allowTriState && oldSort == NHQO.SORT_BY_KEYWORD_DESCENDING)
newSort = NHQO.SORT_BY_NONE;
else
newSort = NHQO.SORT_BY_KEYWORD_ASCENDING;
break;
case this.COLUMN_TYPE_DESCRIPTION:
if (oldSort == NHQO.SORT_BY_ANNOTATION_ASCENDING &&

View File

@ -35,7 +35,6 @@ const SORT_LOOKUP_TABLE = {
url: { key: "URI", dir: "ASCENDING" },
date: { key: "DATE", dir: "DESCENDING" },
visitCount: { key: "VISITCOUNT", dir: "DESCENDING" },
keyword: { key: "KEYWORD", dir: "ASCENDING" },
dateAdded: { key: "DATEADDED", dir: "DESCENDING" },
lastModified: { key: "LASTMODIFIED", dir: "DESCENDING" },
description: { key: "ANNOTATION",

View File

@ -572,7 +572,11 @@ let gDevToolsBrowser = {
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
toolbox ? toolbox.destroy() : gDevTools.showToolbox(target);
// If a toolbox exists, using toggle from the Main window :
// - should close a docked toolbox
// - should focus a windowed toolbox
let isDocked = toolbox && toolbox.hostType != devtools.Toolbox.HostType.WINDOW;
isDocked ? toolbox.destroy() : gDevTools.showToolbox(target);
},
toggleBrowserToolboxCommand: function(gBrowser) {

View File

@ -47,6 +47,7 @@ skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
[browser_toolbox_sidebar_overflow_menu.js]
[browser_toolbox_tabsswitch_shortcuts.js]
[browser_toolbox_textbox_context_menu.js]
[browser_toolbox_toggle.js]
[browser_toolbox_tool_ready.js]
[browser_toolbox_tool_remote_reopen.js]
[browser_toolbox_transport_events.js]

View File

@ -0,0 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf-8,Test toggling devtools using keyboard shortcuts";
add_task(function*() {
// Test with ACCEL+SHIFT+I / ACCEL+ALT+I (MacOSX) ; modifiers should match :
// - toolbox-key-toggle in browser/devtools/framework/toolbox-window.xul
// - key_devToolboxMenuItem in browser/base/content/browser.xul
info('Test toggle using CTRL+SHIFT+I/CMD+ALT+I');
yield testToggle('I', {
accelKey : true,
shiftKey : !navigator.userAgent.match(/Mac/),
altKey : navigator.userAgent.match(/Mac/),
});
// Test with F12 ; no modifiers
info('Test toggle using F12');
yield testToggle('VK_F12', {});
});
function* testToggle(key, modifiers) {
let tab = yield addTab(URL + " ; key : '" + key + "'");
yield gDevTools.showToolbox(TargetFactory.forTab(tab));
yield testToggleDockedToolbox(tab, key, modifiers);
yield testToggleDetachedToolbox(tab, key, modifiers);
yield cleanup();
}
function* testToggleDockedToolbox (tab, key, modifiers) {
let toolbox = getToolboxForTab(tab);
isnot(toolbox.hostType, devtools.Toolbox.HostType.WINDOW, "Toolbox is docked in the main window");
info('verify docked toolbox is destroyed when using toggle key');
let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
EventUtils.synthesizeKey(key, modifiers);
yield onToolboxDestroyed;
ok(true, "Docked toolbox is destroyed when using a toggle key");
info('verify new toolbox is created when using toggle key');
let onToolboxReady = once(gDevTools, "toolbox-ready");
EventUtils.synthesizeKey(key, modifiers);
yield onToolboxReady;
ok(true, "Toolbox is created by using when toggle key");
}
function* testToggleDetachedToolbox (tab, key, modifiers) {
let toolbox = getToolboxForTab(tab);
info('change the toolbox hostType to WINDOW');
yield toolbox.switchHost(devtools.Toolbox.HostType.WINDOW);
is(toolbox.hostType, devtools.Toolbox.HostType.WINDOW, "Toolbox opened on separate window");
let toolboxWindow = toolbox._host._window;
info('Wait for focus on the toolbox window')
yield new Promise(resolve => waitForFocus(resolve, toolboxWindow));
info('Focus main window')
let onMainWindowFocus = once(window, "focus");
window.focus();
yield onMainWindowFocus;
ok(true, "Main window focused");
info('verify windowed toolbox is focused when using toggle key from the main window')
let onToolboxWindowFocus = once(toolboxWindow, "focus");
EventUtils.synthesizeKey(key, modifiers);
yield onToolboxWindowFocus;
ok(true, "Toolbox focused and not destroyed");
info('verify windowed toolbox is destroyed when using toggle key from its own window')
let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
EventUtils.synthesizeKey(key, modifiers, toolboxWindow);
yield onToolboxDestroyed;
ok(true, "Toolbox destroyed");
}
function getToolboxForTab(tab) {
return gDevTools.getToolbox(TargetFactory.forTab(tab));
}
function* cleanup(toolbox) {
Services.prefs.setCharPref("devtools.toolbox.host", devtools.Toolbox.HostType.BOTTOM);
gBrowser.removeCurrentTab();
}

View File

@ -26,6 +26,19 @@
key="&closeCmd.key;"
command="toolbox-cmd-close"
modifiers="accel"/>
<key id="toolbox-key-toggle"
key="&toggleToolbox.key;"
command="toolbox-cmd-close"
#ifdef XP_MACOSX
modifiers="accel,alt"
#else
modifiers="accel,shift"
#endif
/>
<key id="toolbox-key-toggle-F12"
keycode="&toggleToolboxF12.keycode;"
keytext="&toggleToolboxF12.keytext;"
command="toolbox-cmd-close"/>
</keyset>
<iframe id="toolbox-iframe" flex="1" forceOwnRefreshDriver=""></iframe>

View File

@ -774,9 +774,7 @@ InspectorPanel.prototype = {
}
let type = popupNode.dataset.type;
// Bug 1158822 will make "resource" type URLs open in devtools, but for now
// they're considered like "uri".
if (type === "uri" || type === "resource") {
if (type === "uri" || type === "cssresource" || type === "jsresource") {
// First make sure the target can resolve relative URLs.
this.target.actorHasMethod("inspector", "resolveRelativeURL").then(canResolve => {
if (!canResolve) {
@ -786,11 +784,20 @@ InspectorPanel.prototype = {
linkSeparator.removeAttribute("hidden");
// Links can't be opened in new tabs in the browser toolbox.
if (!this.target.chrome) {
if (type === "uri" && !this.target.chrome) {
linkFollow.removeAttribute("hidden");
linkFollow.setAttribute("label", this.strings.GetStringFromName(
"inspector.menu.openUrlInNewTab.label"));
} else if (type === "cssresource") {
linkFollow.removeAttribute("hidden");
linkFollow.setAttribute("label", this.toolboxStrings.GetStringFromName(
"toolbox.viewCssSourceInStyleEditor.label"));
} else if (type === "jsresource") {
linkFollow.removeAttribute("hidden");
linkFollow.setAttribute("label", this.toolboxStrings.GetStringFromName(
"toolbox.viewJsSourceInDebugger.label"));
}
linkCopy.removeAttribute("hidden");
linkCopy.setAttribute("label", this.strings.GetStringFromName(
"inspector.menu.copyUrlToClipboard.label"));
@ -1099,26 +1106,31 @@ InspectorPanel.prototype = {
let type = this.panelDoc.popupNode.dataset.type;
let link = this.panelDoc.popupNode.dataset.link;
// "resource" type links should open appropriate tool instead (bug 1158822).
if (type === "uri" || type === "resource") {
if (type === "uri" || type === "cssresource" || type === "jsresource") {
// Open link in a new tab.
// When the inspector menu was setup on click (see _setupNodeLinkMenu), we
// already checked that resolveRelativeURL existed.
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
let browserWin = this.target.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(url, "tab");
}, console.error);
if (type === "uri") {
let browserWin = this.target.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(url, "tab");
} else if (type === "cssresource") {
return this.toolbox.viewSourceInStyleEditor(url);
} else if (type === "jsresource") {
return this.toolbox.viewSourceInDebugger(url);
}
}).catch(e => console.error(e));
} else if (type == "idref") {
// Select the node in the same document.
this.walker.document(this.selection.nodeFront).then(doc => {
this.walker.querySelector(doc, "#" + CSS.escape(link)).then(node => {
return this.walker.querySelector(doc, "#" + CSS.escape(link)).then(node => {
if (!node) {
this.emit("idref-attribute-link-failed");
return;
}
this.selection.setNodeFront(node);
}, console.error);
}, console.error);
});
}).catch(e => console.error(e));
}
},
@ -1179,18 +1191,21 @@ InspectorPanel.prototype = {
/////////////////////////////////////////////////////////////////////////
//// Initializers
loader.lazyGetter(InspectorPanel.prototype, "strings",
function () {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/inspector.properties");
});
loader.lazyGetter(InspectorPanel.prototype, "strings", function () {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/inspector.properties");
});
loader.lazyGetter(InspectorPanel.prototype, "toolboxStrings", function () {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/toolbox.properties");
});
loader.lazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
});
loader.lazyGetter(this, "DOMUtils", function () {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});

View File

@ -118,7 +118,7 @@ browser.jar:
content/browser/devtools/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml)
content/browser/devtools/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
content/browser/devtools/commandline/commands-index.js (commandline/commands-index.js)
content/browser/devtools/framework/toolbox-window.xul (framework/toolbox-window.xul)
* content/browser/devtools/framework/toolbox-window.xul (framework/toolbox-window.xul)
content/browser/devtools/framework/toolbox-options.xul (framework/toolbox-options.xul)
content/browser/devtools/framework/toolbox-options.js (framework/toolbox-options.js)
content/browser/devtools/framework/toolbox.xul (framework/toolbox.xul)

View File

@ -85,7 +85,7 @@
--></span>
<span id="template-text" save="${elt}" class="editor text">
<pre save="${value}" style="display:inline-block;" tabindex="0"></pre>
<pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="0"></pre>
</span>
<span id="template-comment"

View File

@ -74,6 +74,7 @@ skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
[browser_markupview_links_03.js]
[browser_markupview_links_04.js]
[browser_markupview_links_05.js]
[browser_markupview_links_06.js]
[browser_markupview_load_01.js]
[browser_markupview_html_edit_01.js]
[browser_markupview_html_edit_02.js]

View File

@ -13,7 +13,7 @@ const TEST_DATA = [{
selector: "link",
attributes: [{
attributeName: "href",
links: [{type: "resource", value: "style.css"}]
links: [{type: "cssresource", value: "style.css"}]
}]
}, {
selector: "link[rel=icon]",
@ -95,7 +95,7 @@ const TEST_DATA = [{
selector: "script",
attributes: [{
attributeName: "src",
links: [{type: "resource", value: "lib_jquery_1.0.js"}]
links: [{type: "jsresource", value: "lib_jquery_1.0.js"}]
}]
}];

View File

@ -10,6 +10,8 @@
const TEST_URL = TEST_URL_ROOT + "doc_markup_links.html";
const STRINGS = Services.strings
.createBundle("chrome://browser/locale/devtools/inspector.properties");
const TOOLBOX_STRINGS = Services.strings
.createBundle("chrome://browser/locale/devtools/toolbox.properties");
// The test case array contains objects with the following properties:
// - selector: css selector for the node to select in the inspector
@ -26,7 +28,7 @@ const TEST_DATA = [{
popupNodeSelector: ".link",
isLinkFollowItemVisible: true,
isLinkCopyItemVisible: true,
linkFollowItemLabel: STRINGS.GetStringFromName("inspector.menu.openUrlInNewTab.label"),
linkFollowItemLabel: TOOLBOX_STRINGS.GetStringFromName("toolbox.viewCssSourceInStyleEditor.label"),
linkCopyItemLabel: STRINGS.GetStringFromName("inspector.menu.copyUrlToClipboard.label")
}, {
selector: "link[rel=icon]",
@ -56,7 +58,7 @@ const TEST_DATA = [{
popupNodeSelector: ".link",
isLinkFollowItemVisible: true,
isLinkCopyItemVisible: true,
linkFollowItemLabel: STRINGS.GetStringFromName("inspector.menu.openUrlInNewTab.label"),
linkFollowItemLabel: TOOLBOX_STRINGS.GetStringFromName("toolbox.viewJsSourceInDebugger.label"),
linkCopyItemLabel: STRINGS.GetStringFromName("inspector.menu.copyUrlToClipboard.label")
}, {
selector: "p[for]",

View File

@ -12,9 +12,6 @@ const TEST_URL = TEST_URL_ROOT + "doc_markup_links.html";
add_task(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let linkFollow = inspector.panelDoc.getElementById("node-menu-link-follow");
let linkCopy = inspector.panelDoc.getElementById("node-menu-link-copy");
info("Select a node with a URI attribute");
yield selectNode("video", inspector);

View File

@ -0,0 +1,51 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the contextual menu items shown when clicking on linked attributes
// for <script> and <link> tags actually open the right tools.
const TEST_URL = TEST_URL_ROOT + "doc_markup_links.html";
add_task(function*() {
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
info("Select a node with a cssresource attribute");
yield selectNode("link", inspector);
info("Set the popupNode to the node that contains the uri");
let {editor} = yield getContainerForSelector("link", inspector);
let popupNode = editor.attrElements.get("href").querySelector(".link");
inspector.panelDoc.popupNode = popupNode;
info("Follow the link and wait for the style-editor to open");
let onStyleEditorReady = toolbox.once("styleeditor-ready");
inspector.followAttributeLink();
yield onStyleEditorReady;
// No real need to test that the editor opened on the right file here as this
// is already tested in /framework/test/browser_toolbox_view_source_*
ok(true, "The style-editor was open");
info("Switch back to the inspector");
yield toolbox.selectTool("inspector");
info("Select a node with a jsresource attribute");
yield selectNode("script", inspector);
info("Set the popupNode to the node that contains the uri");
({editor}) = yield getContainerForSelector("script", inspector);
popupNode = editor.attrElements.get("src").querySelector(".link");
inspector.panelDoc.popupNode = popupNode;
info("Follow the link and wait for the debugger to open");
let onDebuggerReady = toolbox.once("jsdebugger-ready");
inspector.followAttributeLink();
yield onDebuggerReady;
// No real need to test that the debugger opened on the right file here as
// this is already tested in /framework/test/browser_toolbox_view_source_*
ok(true, "The debugger was open");
});

View File

@ -247,6 +247,8 @@ function InplaceEditor(aOptions, aEvent)
this.input.addEventListener("keyup", this._onKeyup, false);
}
this._updateSize();
if (aOptions.start) {
aOptions.start(this, aEvent);
}
@ -364,7 +366,6 @@ InplaceEditor.prototype = {
// account for the fact that after adding a newline the <pre> doesn't grow
// unless there's text content on the line.
width += 15;
this._measurement.textContent += "M";
this.input.style.height = this._measurement.offsetHeight + "px";
}

View File

@ -18,8 +18,10 @@
* (e.g. <label for="input-id"> or <key command="command-id">).
* - TYPE_IDREF_LIST: a space separated list of IDREFs (e.g.
* <output for="id1 id2">).
* - TYPE_RESOURCE_URI: a URI to a javascript or css resource that can be opened
* in the devtools (e.g. <script src="uri">).
* - TYPE_JS_RESOURCE_URI: a URI to a javascript resource that can be opened in
* the devtools (e.g. <script src="uri">).
* - TYPE_CSS_RESOURCE_URI: a URI to a css resource that can be opened in the
* devtools (e.g. <link href="uri">).
*
* parseAttribute is the parser entry function, exported on this module.
*/
@ -29,7 +31,8 @@ const TYPE_URI = "uri";
const TYPE_URI_LIST = "uriList";
const TYPE_IDREF = "idref";
const TYPE_IDREF_LIST = "idrefList";
const TYPE_RESOURCE_URI = "resource";
const TYPE_JS_RESOURCE_URI = "jsresource";
const TYPE_CSS_RESOURCE_URI = "cssresource";
const SVG_NS = "http://www.w3.org/2000/svg";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -65,7 +68,7 @@ const ATTRIBUTE_TYPES = [
{namespaceURI: HTML_NS, attributeName: "headers", tagName: "th", type: TYPE_IDREF_LIST},
{namespaceURI: HTML_NS, attributeName: "href", tagName: "a", type: TYPE_URI},
{namespaceURI: HTML_NS, attributeName: "href", tagName: "area", type: TYPE_URI},
{namespaceURI: "*", attributeName: "href", tagName: "link", type: TYPE_RESOURCE_URI,
{namespaceURI: "*", attributeName: "href", tagName: "link", type: TYPE_CSS_RESOURCE_URI,
isValid: (namespaceURI, tagName, attributes) => {
return getAttribute(attributes, "rel") === "stylesheet";
}},
@ -82,7 +85,7 @@ const ATTRIBUTE_TYPES = [
{namespaceURI: HTML_NS, attributeName: "ping", tagName: "area", type: TYPE_URI_LIST},
{namespaceURI: HTML_NS, attributeName: "poster", tagName: "video", type: TYPE_URI},
{namespaceURI: HTML_NS, attributeName: "profile", tagName: "head", type: TYPE_URI},
{namespaceURI: "*", attributeName: "src", tagName: "script", type: TYPE_RESOURCE_URI},
{namespaceURI: "*", attributeName: "src", tagName: "script", type: TYPE_JS_RESOURCE_URI},
{namespaceURI: HTML_NS, attributeName: "src", tagName: "input", type: TYPE_URI},
{namespaceURI: HTML_NS, attributeName: "src", tagName: "frame", type: TYPE_URI},
{namespaceURI: HTML_NS, attributeName: "src", tagName: "iframe", type: TYPE_URI},
@ -137,9 +140,15 @@ let parsers = {
}
return data;
},
[TYPE_RESOURCE_URI]: function(attributeValue) {
[TYPE_JS_RESOURCE_URI]: function(attributeValue) {
return [{
type: TYPE_RESOURCE_URI,
type: TYPE_JS_RESOURCE_URI,
value: attributeValue
}];
},
[TYPE_CSS_RESOURCE_URI]: function(attributeValue) {
return [{
type: TYPE_CSS_RESOURCE_URI,
value: attributeValue
}];
},
@ -169,7 +178,7 @@ let parsers = {
* be an array of {name, value} objects.
* @param {String} attributeName The name of the attribute to parse.
* @return {Array} An array of tokens that represents the value. Each token is
* an object {type: [string|uri|resource|idref], value}.
* an object {type: [string|uri|jsresource|cssresource|idref], value}.
* For instance parsing the ping attribute in <a ping="uri1 uri2"> returns:
* [
* {type: "uri", value: "uri2"},

View File

@ -51,7 +51,7 @@ const TEST_DATA = [{
attributeValue: "styles.css",
otherAttributes: [{name: "rel", value: "stylesheet"}],
expected: [
{value: "styles.css", type: "resource"}
{value: "styles.css", type: "cssresource"}
]
}, {
tagName: "link",
@ -103,7 +103,7 @@ const TEST_DATA = [{
attributeName: "src",
attributeValue: "script.js",
expected: [
{value: "script.js", type: "resource"}
{value: "script.js", type: "jsresource"}
]
}];

View File

@ -93,6 +93,7 @@ const CONSOLE_API_LEVELS_TO_SEVERITIES = {
table: "log",
debug: "log",
dir: "log",
dirxml: "log",
group: "log",
groupCollapsed: "log",
groupEnd: "log",
@ -2982,7 +2983,8 @@ Widgets.ObjectRenderers.add({
_renderDocumentNode: function()
{
let fn = Widgets.ObjectRenderers.byKind.ObjectWithURL.prototype._renderElement;
let fn =
Widgets.ObjectRenderers.byKind.ObjectWithURL.prototype._renderElement;
this.element = fn.call(this, this.objectActor,
this.objectActor.preview.location);
this.element.classList.add("documentNode");

View File

@ -384,3 +384,4 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_console_api_stackframe.js]
[browser_webconsole_column_numbers.js]
[browser_console_open_or_focus.js]
[browser_webconsole_bug_922212_console_dirxml.js]

View File

@ -9,7 +9,7 @@
"use strict";
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 659907: " +
"Expand console object with a dir method"
"Expand console object with a dir method";
let test = asyncTest(function*() {
yield loadTab(TEST_URI);

View File

@ -0,0 +1,49 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that console.dirxml works as intended.
"use strict";
const TEST_URI = `data:text/html;charset=utf-8,Web Console test for bug 922212:
Add console.dirxml`;
let test = asyncTest(function*() {
yield loadTab(TEST_URI);
let hud = yield openConsole();
hud.jsterm.clearOutput();
// Should work like console.log(window)
hud.jsterm.execute("console.dirxml(window)");
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "console.dirxml(window) output:",
text: /Window \u2192/,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
}],
});
hud.jsterm.clearOutput();
hud.jsterm.execute("console.dirxml(document.body)");
// Should work like console.log(document.body);
[result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "console.dirxml(document.body) output:",
text: "<body>",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
}],
});
let msg = [...result.matched][0];
yield checkLinkToInspector(true, msg);
});

View File

@ -1474,7 +1474,8 @@ function checkOutputForInputs(hud, inputTests)
if (typeof entry.inspectorIcon == "boolean") {
let msg = [...result.matched][0];
yield checkLinkToInspector(entry, msg);
info("Checking Inspector Link: " + entry.input);
yield checkLinkToInspector(entry.inspectorIcon, msg);
}
}
@ -1516,7 +1517,8 @@ function checkOutputForInputs(hud, inputTests)
yield checkObjectClick(entry, msg);
}
if (typeof entry.inspectorIcon == "boolean") {
yield checkLinkToInspector(entry, msg);
info("Checking Inspector Link: " + entry.input);
yield checkLinkToInspector(entry.inspectorIcon, msg);
}
}
@ -1556,30 +1558,6 @@ function checkOutputForInputs(hud, inputTests)
yield promise.resolve(null);
}
function checkLinkToInspector(entry, msg)
{
info("Checking Inspector Link: " + entry.input);
let elementNodeWidget = [...msg._messageObject.widgets][0];
if (!elementNodeWidget) {
ok(!entry.inspectorIcon, "The message has no ElementNode widget");
return;
}
return elementNodeWidget.linkToInspector().then(() => {
// linkToInspector resolved, check for the .open-inspector element
if (entry.inspectorIcon) {
ok(msg.querySelectorAll(".open-inspector").length,
"The ElementNode widget is linked to the inspector");
} else {
ok(!msg.querySelectorAll(".open-inspector").length,
"The ElementNode widget isn't linked to the inspector");
}
}, () => {
// linkToInspector promise rejected, node not linked to inspector
ok(!entry.inspectorIcon, "The ElementNode widget isn't linked to the inspector");
});
}
function onVariablesViewOpen(entry, {resolve, reject}, event, view, options)
{
info("Variables view opened: " + entry.input);
@ -1646,6 +1624,36 @@ function once(target, eventName, useCapture=false) {
return deferred.promise;
}
/**
* Checks a link to the inspector
*
* @param {boolean} hasLinkToInspector Set to true if the message should
* link to the inspector panel.
* @param {element} msg The message to test.
*/
function checkLinkToInspector(hasLinkToInspector, msg)
{
let elementNodeWidget = [...msg._messageObject.widgets][0];
if (!elementNodeWidget) {
ok(!hasLinkToInspector, "The message has no ElementNode widget");
return;
}
return elementNodeWidget.linkToInspector().then(() => {
// linkToInspector resolved, check for the .open-inspector element
if (hasLinkToInspector) {
ok(msg.querySelectorAll(".open-inspector").length,
"The ElementNode widget is linked to the inspector");
} else {
ok(!msg.querySelectorAll(".open-inspector").length,
"The ElementNode widget isn't linked to the inspector");
}
}, () => {
// linkToInspector promise rejected, node not linked to inspector
ok(!hasLinkToInspector, "The ElementNode widget isn't linked to the inspector");
});
}
function getSourceActor(aSources, aURL) {
let item = aSources.getItemForAttachment(a => a.source.url === aURL);
return item && item.value;

View File

@ -6,7 +6,7 @@
function test() {
console.log("start");
console.clear()
console.dirxml()
console.timeStamp()
console.log("end");
}
</script>

View File

@ -130,6 +130,7 @@ const LEVELS = {
table: SEVERITY_LOG,
debug: SEVERITY_LOG,
dir: SEVERITY_LOG,
dirxml: SEVERITY_LOG,
group: SEVERITY_LOG,
groupCollapsed: SEVERITY_LOG,
groupEnd: SEVERITY_LOG,
@ -1285,7 +1286,11 @@ WebConsoleFrame.prototype = {
clipboardText = clipboardArray.join(" ");
break;
}
case "dirxml": {
// We just alias console.dirxml() with console.log().
aMessage.level = "log";
return WCF_logConsoleAPIMessage.call(this, aMessage);
}
case "group":
case "groupCollapsed":
clipboardText = body = aMessage.groupName;

View File

@ -85,20 +85,6 @@ inspector.menu.openUrlInNewTab.label=Open Link in New Tab
# that allows to copy that URL in the clipboard.
inspector.menu.copyUrlToClipboard.label=Copy Link Address
# LOCALIZATION NOTE (inspector.menu.openFileInDebugger.label): This is the label
# of a menu item in the inspector contextual-menu that appears when the user
# right-clicks on the attribute of a node in the inspector that is a URL to a
# javascript filename, and that allows to open the corresponding file in the
# debugger.
inspector.menu.openFileInDebugger.label=Open File in Debugger
# LOCALIZATION NOTE (inspector.menu.openFileInStyleEditor.label): This is the
# label of a menu item in the inspector contextual-menu that appears when the
# user right-clicks on the attribute of a node in the inspector that is a URL to
# a css filename, and that allows to open the corresponding file in the style
# editor.
inspector.menu.openFileInStyleEditor.label=Open File in Style-Editor
# LOCALIZATION NOTE (inspector.menu.selectElement.label): This is the label of a
# menu item in the inspector contextual-menu that appears when the user right-
# clicks on the attribute of a node in the inspector that is the ID of another

View File

@ -6,6 +6,9 @@
<!-- LOCALIZATION NOTE : FILE Do not translate key -->
<!ENTITY closeCmd.key "W">
<!ENTITY toggleToolbox.key "I">
<!ENTITY toggleToolboxF12.keycode "VK_F12">
<!ENTITY toggleToolboxF12.keytext "F12">
<!ENTITY toolboxCloseButton.tooltip "Close Developer Tools">
<!ENTITY toolboxOptionsButton.key "O">

View File

@ -88,3 +88,15 @@ options.lightTheme.label=Light theme
# Used as a message in the alert displayed when trying to open a browser
# content toolbox and there is no content process running
toolbox.noContentProcess.message=No content process running.
# LOCALIZATION NOTE (toolbox.viewCssSourceInStyleEditor.label)
# Used as a message in either tooltips or contextual menu items to open the
# corresponding URL as a css file in the Style-Editor tool.
# DEV NOTE: Mostly used wherever toolbox.viewSourceInStyleEditor is used.
toolbox.viewCssSourceInStyleEditor.label=Open File in Style-Editor
# LOCALIZATION NOTE (toolbox.viewJsSourceInDebugger.label)
# Used as a message in either tooltips or contextual menu items to open the
# corresponding URL as a js file in the Debugger tool.
# DEV NOTE: Mostly used wherever toolbox.viewSourceInDebugger is used.
toolbox.viewJsSourceInDebugger.label=Open File in Debugger

View File

@ -85,7 +85,6 @@
<!ENTITY col.url.label "Location">
<!ENTITY col.mostrecentvisit.label "Most Recent Visit">
<!ENTITY col.visitcount.label "Visit Count">
<!ENTITY col.keyword.label "Keyword">
<!ENTITY col.description.label "Description">
<!ENTITY col.dateadded.label "Added">
<!ENTITY col.lastmodified.label "Last Modified">

View File

@ -36,8 +36,6 @@ view.sortBy.1.date.label=Sort by Most Recent Visit
view.sortBy.1.date.accesskey=V
view.sortBy.1.visitCount.label=Sort by Visit Count
view.sortBy.1.visitCount.accesskey=C
view.sortBy.1.keyword.label=Sort by Keyword
view.sortBy.1.keyword.accesskey=K
view.sortBy.1.description.label=Sort by Description
view.sortBy.1.description.accesskey=D
view.sortBy.1.dateAdded.label=Sort by Added

View File

@ -7,6 +7,7 @@
this.EXPORTED_SYMBOLS = [ "Feeds" ];
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
@ -37,8 +38,11 @@ this.Feeds = {
}
if (aIsFeed) {
// re-create the principal as it may be a CPOW.
let principalURI = BrowserUtils.makeURIFromCPOW(aPrincipal.URI);
let principalToCheck = Services.scriptSecurityManager.getNoAppCodebasePrincipal(principalURI);
try {
BrowserUtils.urlSecurityCheck(aLink.href, aPrincipal,
BrowserUtils.urlSecurityCheck(aLink.href, principalToCheck,
Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
return type || "application/rss+xml";
}

View File

@ -814,6 +814,7 @@ Console::Trace(JSContext* aCx)
// Displays an interactive listing of all the properties of an object.
METHOD(Dir, "dir");
METHOD(Dirxml, "dirxml");
METHOD(Group, "group")
METHOD(GroupCollapsed, "groupCollapsed")

View File

@ -74,6 +74,9 @@ public:
void
Dir(JSContext* aCx, const Sequence<JS::Value>& aData);
void
Dirxml(JSContext* aCx, const Sequence<JS::Value>& aData);
void
Group(JSContext* aCx, const Sequence<JS::Value>& aData);
@ -116,6 +119,7 @@ private:
MethodTable,
MethodTrace,
MethodDir,
MethodDirxml,
MethodGroup,
MethodGroupCollapsed,
MethodGroupEnd,

View File

@ -16,6 +16,7 @@ interface Console {
void table(any... data);
void trace();
void dir(any... data);
void dirxml(any... data);
void group(any... data);
void groupCollapsed(any... data);
void groupEnd(any... data);
@ -32,8 +33,6 @@ interface Console {
[BinaryName="noopMethod"]
void clear();
[BinaryName="noopMethod"]
void dirxml();
[BinaryName="noopMethod"]
void markTimeline();
[BinaryName="noopMethod"]
void timeline();

View File

@ -17,13 +17,19 @@ import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@ -32,6 +38,7 @@ import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
@ -40,20 +47,30 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
LayerView.ZoomedViewListener, GeckoEventListener {
private static final String LOGTAG = "Gecko" + ZoomedView.class.getSimpleName();
private static final int DEFAULT_ZOOM_FACTOR = 3;
private static final int W_CAPTURED_VIEW_IN_PERCENT = 80;
private static final float[] ZOOM_FACTORS_LIST = {2.0f, 3.0f, 1.5f};
private static final int W_CAPTURED_VIEW_IN_PERCENT = 50;
private static final int H_CAPTURED_VIEW_IN_PERCENT = 50;
private static final int MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS = 1000000;
private static final int DELAY_BEFORE_NEXT_RENDER_REQUEST_MS = 2000;
private int zoomFactor;
private float zoomFactor;
private int currentZoomFactorIndex;
private ImageView zoomedImageView;
private LayerView layerView;
private int viewWidth;
private int viewHeight;
private int viewHeight; // Only the zoomed view height, no toolbar, no shadow ...
private int viewContainerWidth;
private int viewContainerHeight; // Zoomed view height with toolbar and other elements like shadow, ...
private int containterSize; // shadow, margin, ...
private Point lastPosition;
private boolean shouldSetVisibleOnUpdate;
private PointF returnValue;
private ImageView closeButton;
private TextView changeZoomFactorButton;
private boolean toolbarOnTop;
private float offsetDueToToolBarPosition;
private int toolbarHeight;
private int cornerRadius;
private boolean stopUpdateView;
@ -64,6 +81,39 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
private long startTimeReRender;
private long lastStartTimeReRender;
private class RoundedBitmapDrawable extends BitmapDrawable {
private Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
final float cornerRadius;
final boolean squareOnTopOfDrawable;
RoundedBitmapDrawable(Resources res, Bitmap bitmap, boolean squareOnTop, int radius) {
super(res, bitmap);
squareOnTopOfDrawable = squareOnTop;
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
paint.setAntiAlias(true);
paint.setShader(shader);
cornerRadius = radius;
}
@Override
public void draw(Canvas canvas) {
int height = getBounds().height();
int width = getBounds().width();
RectF rect = new RectF(0.0f, 0.0f, width, height);
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
//draw rectangles over the corners we want to be square
if (squareOnTopOfDrawable) {
canvas.drawRect(0, 0, cornerRadius, cornerRadius, paint);
canvas.drawRect(width - cornerRadius, 0, width, cornerRadius, paint);
} else {
canvas.drawRect(0, height - cornerRadius, cornerRadius, height, paint);
canvas.drawRect(width - cornerRadius, height - cornerRadius, width, height, paint);
}
}
}
private class ZoomedViewTouchListener implements View.OnTouchListener {
private float originRawX;
private float originRawY;
@ -87,16 +137,18 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
if (dragged) {
dragged = false;
} else {
GeckoEvent eClickInZoomedView = GeckoEvent.createBroadcastEvent("Gesture:ClickInZoomedView", "");
GeckoAppShell.sendEventToGecko(eClickInZoomedView);
layerView.dispatchTouchEvent(actionDownEvent);
actionDownEvent.recycle();
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY());
MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y,
event.getMetaState());
layerView.dispatchTouchEvent(e);
e.recycle();
if (isClickInZoomedView(event.getY())) {
GeckoEvent eClickInZoomedView = GeckoEvent.createBroadcastEvent("Gesture:ClickInZoomedView", "");
GeckoAppShell.sendEventToGecko(eClickInZoomedView);
layerView.dispatchTouchEvent(actionDownEvent);
actionDownEvent.recycle();
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY());
MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y,
event.getMetaState());
layerView.dispatchTouchEvent(e);
e.recycle();
}
}
break;
@ -113,6 +165,11 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
return true;
}
private boolean isClickInZoomedView(float y) {
return ((toolbarOnTop && y > toolbarHeight) ||
(!toolbarOnTop && y < ZoomedView.this.viewHeight));
}
private boolean moveZoomedView(MotionEvent event) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ZoomedView.this.getLayoutParams();
if ((!dragged) && (Math.abs((int) (event.getRawX() - originRawX)) < PanZoomController.CLICK_THRESHOLD)
@ -143,19 +200,21 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
public ZoomedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
returnValue = new PointF();
currentZoomFactorIndex = 0;
zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
requestRenderRunnable = new Runnable() {
@Override
public void run() {
requestZoomedViewRender();
}
};
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Gesture:nothingDoneOnLongPress",
EventDispatcher.getInstance().registerGeckoThreadListener(this,
"Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange");
}
void destroy() {
ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Gesture:nothingDoneOnLongPress",
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange");
}
@ -165,15 +224,29 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ImageView closeButton = (ImageView) findViewById(R.id.dialog_close);
closeButton = (ImageView) findViewById(R.id.dialog_close);
closeButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
stopZoomDisplay();
}
});
changeZoomFactorButton = (TextView) findViewById(R.id.change_zoom_factor);
changeZoomFactorButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
changeZoomFactor();
}
});
setTextInZoomFactorButton(ZOOM_FACTORS_LIST[0]);
zoomedImageView = (ImageView) findViewById(R.id.zoomed_image_view);
zoomedImageView.setOnTouchListener(new ZoomedViewTouchListener());
this.setOnTouchListener(new ZoomedViewTouchListener());
toolbarHeight = getResources().getDimensionPixelSize(R.dimen.zoomed_view_toolbar_height);
containterSize = getResources().getDimensionPixelSize(R.dimen.drawable_dropshadow_size);
cornerRadius = getResources().getDimensionPixelSize(R.dimen.button_corner_radius);
moveToolbar(true);
}
/*
@ -181,6 +254,10 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
* LayerView
*/
private PointF getUnzoomedPositionFromPointInZoomedView(float x, float y) {
if (toolbarOnTop && y > toolbarHeight) {
y = y - toolbarHeight;
}
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
PointF offset = metrics.getMarginOffset();
final float parentWidth = metrics.getWidth();
@ -199,14 +276,15 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
*/
(((float) params.leftMargin) - offset.x) *
((parentWidth - offset.x - (viewWidth / zoomFactor)) /
(parentWidth - offset.x - viewWidth)));
(parentWidth - offset.x - viewContainerWidth)));
// Same comments here vertically
returnValue.y = (int) ((y / zoomFactor) +
offset.y +
offset.y -
offsetDueToToolBarPosition +
(((float) params.topMargin) - offset.y) *
((parentHeight - offset.y - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewHeight)));
((parentHeight - offset.y + offsetDueToToolBarPosition - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewContainerHeight)));
return returnValue;
}
@ -232,14 +310,14 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
* the LayerView to the ZoomedView.
*/
((parentWidth - offset.x - (viewWidth / zoomFactor)) /
(parentWidth - offset.x - viewWidth)))
(parentWidth - offset.x - viewContainerWidth)))
+ offset.x); // The offset of the layerView
// Same comments here vertically
returnValue.y = (int) ((((y - (viewHeight / (2 * zoomFactor)))) /
((parentHeight - offset.y - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewHeight)))
returnValue.y = (int) ((((y + offsetDueToToolBarPosition - (viewHeight / (2 * zoomFactor)))) /
((parentHeight - offset.y + offsetDueToToolBarPosition - (viewHeight / zoomFactor)) /
(parentHeight - offset.y - viewContainerHeight)))
+ offset.y);
return returnValue;
@ -259,14 +337,20 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
if (newTopMargin < topMarginMin) {
newLayoutParams.topMargin = topMarginMin;
} else if (newTopMargin + viewHeight > parentHeight) {
newLayoutParams.topMargin = (int) (parentHeight - viewHeight);
} else if (newTopMargin + viewContainerHeight > parentHeight) {
newLayoutParams.topMargin = (int) (parentHeight - viewContainerHeight);
}
if (newLeftMargin < leftMarginMin) {
newLayoutParams.leftMargin = leftMarginMin;
} else if (newLeftMargin + viewWidth > parentWidth) {
newLayoutParams.leftMargin = (int) (parentWidth - viewWidth);
} else if (newLeftMargin + viewContainerWidth > parentWidth) {
newLayoutParams.leftMargin = (int) (parentWidth - viewContainerWidth);
}
if (newLayoutParams.topMargin < topMarginMin + 1) {
moveToolbar(false);
} else if (newLayoutParams.topMargin + viewContainerHeight > parentHeight - 1) {
moveToolbar(true);
}
setLayoutParams(newLayoutParams);
@ -275,6 +359,37 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
requestZoomedViewRender();
}
private void moveToolbar(boolean moveTop) {
if (toolbarOnTop == moveTop) {
return;
}
toolbarOnTop = moveTop;
if (toolbarOnTop) {
offsetDueToToolBarPosition = toolbarHeight;
} else {
offsetDueToToolBarPosition = 0;
}
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) zoomedImageView.getLayoutParams();
RelativeLayout.LayoutParams pChangeZoomFactorButton = (RelativeLayout.LayoutParams) changeZoomFactorButton.getLayoutParams();
RelativeLayout.LayoutParams pCloseButton = (RelativeLayout.LayoutParams) closeButton.getLayoutParams();
if (moveTop) {
p.addRule(RelativeLayout.BELOW, R.id.change_zoom_factor);
pChangeZoomFactorButton.addRule(RelativeLayout.BELOW, 0);
pCloseButton.addRule(RelativeLayout.BELOW, 0);
} else {
p.addRule(RelativeLayout.BELOW, 0);
pChangeZoomFactorButton.addRule(RelativeLayout.BELOW, R.id.zoomed_image_view);
pCloseButton.addRule(RelativeLayout.BELOW, R.id.zoomed_image_view);
}
pChangeZoomFactorButton.addRule(RelativeLayout.ALIGN_LEFT, R.id.zoomed_image_view);
pCloseButton.addRule(RelativeLayout.ALIGN_RIGHT, R.id.zoomed_image_view);
zoomedImageView.setLayoutParams(p);
changeZoomFactorButton.setLayoutParams(pChangeZoomFactorButton);
closeButton.setLayoutParams(pCloseButton);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@ -299,13 +414,12 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
private void setCapturedSize(ImmutableViewportMetrics metrics) {
float parentMinSize = Math.min(metrics.getWidth(), metrics.getHeight());
// For metrics.zoomFactor lower than 1, the zoom factor of the zoomed view is calculated
// to get always the same size for the content in the zoomed view.
// For metrics.zoomFactor greater than 1, the zoom factor is always set to the default
// value DEFAULT_ZOOM_FACTOR, thus the zoomed view is always a zoom of the normal view.
zoomFactor = Math.max(DEFAULT_ZOOM_FACTOR, (int) (DEFAULT_ZOOM_FACTOR / metrics.zoomFactor));
viewWidth = (int) (parentMinSize * W_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor;
viewHeight = (int) (parentMinSize * H_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor;
viewWidth = (int) ((parentMinSize * W_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor);
viewHeight = (int) ((parentMinSize * H_CAPTURED_VIEW_IN_PERCENT / (zoomFactor * 100.0)) * zoomFactor);
viewContainerHeight = viewHeight + toolbarHeight +
2 * containterSize; // Top and bottom shadows
viewContainerWidth = viewWidth +
2 * containterSize; // Right and left shadows
// Display in zoomedview is corrupted when width is an odd number
// More details about this issue here: bug 776906 comment 11
viewWidth &= ~0x1;
@ -343,13 +457,31 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
}
}
private void changeZoomFactor() {
if (currentZoomFactorIndex < ZOOM_FACTORS_LIST.length - 1) {
currentZoomFactorIndex++;
} else {
currentZoomFactorIndex = 0;
}
zoomFactor = ZOOM_FACTORS_LIST[currentZoomFactorIndex];
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
refreshZoomedViewSize(metrics);
setTextInZoomFactorButton(zoomFactor);
}
private void setTextInZoomFactorButton(float zoom) {
final String percentageValue = Integer.toString((int) (100*zoom));
changeZoomFactorButton.setText(getResources().getString(R.string.percent, percentageValue));
}
@Override
public void handleMessage(final String event, final JSONObject message) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
try {
if (event.equals("Gesture:nothingDoneOnLongPress") || event.equals("Gesture:clusteredLinksClicked")) {
if (event.equals("Gesture:clusteredLinksClicked")) {
final JSONObject clickPosition = message.getJSONObject("clickPosition");
int left = clickPosition.getInt("x");
int top = clickPosition.getInt("y");
@ -373,6 +505,10 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
private void moveUsingGeckoPosition(int leftFromGecko, int topFromGecko) {
ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
final float parentHeight = metrics.getHeight();
// moveToolbar is called before getZoomedViewTopLeftPositionFromTouchPosition in order to
// correctly center vertically the zoomed area
moveToolbar((topFromGecko * metrics.zoomFactor > parentHeight / 2));
PointF convertedPosition = getZoomedViewTopLeftPositionFromTouchPosition((leftFromGecko * metrics.zoomFactor),
(topFromGecko * metrics.zoomFactor));
moveZoomedView(metrics, convertedPosition.x, convertedPosition.y);
@ -406,8 +542,8 @@ public class ZoomedView extends FrameLayout implements LayerView.OnMetricsChange
} catch (Exception iae) {
Log.w(LOGTAG, iae.toString());
}
BitmapDrawable ob3 = new BitmapDrawable(getResources(), sb3);
if (zoomedImageView != null) {
RoundedBitmapDrawable ob3 = new RoundedBitmapDrawable(getResources(), sb3, toolbarOnTop, cornerRadius);
zoomedImageView.setImageDrawable(ob3);
}
}

View File

@ -614,6 +614,14 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY colon ":">
<!-- LOCALIZATION NOTE (percent): The percent sign is appended after a number to
display a percentage value. formatS is the number, #37 is the code to display a percent sign.
This format string is typically used by getString method, in such method the percent sign
is a reserved caracter. In order to display one percent sign in the result of getString,
double percent signs must be inserted in the format string.
This entity is used in the zoomed view to display the zoom factor-->
<!ENTITY percent "&formatS;&#37;&#37;">
<!-- These are only used for accessibility for the done and overflow-menu buttons in the actionbar.
They are never shown to users -->
<!ENTITY actionbar_menu "Menu">

View File

@ -10,24 +10,37 @@
android:id="@+id/zoomed_view_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@android:color/white"
android:visibility="gone" >
android:background="@drawable/dropshadow"
android:padding="@dimen/drawable_dropshadow_size"
android:visibility="gone">
<ImageView
android:id="@+id/zoomed_image_view"
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
android:padding="1dip" />
<ImageView
android:id="@+id/dialog_close"
android:background="@drawable/close"
android:layout_height="20dp"
android:layout_width="20dp"
android:layout_gravity ="top|right" />
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@drawable/toolbar_grey_round">
<TextView
android:id="@+id/change_zoom_factor"
android:layout_width="wrap_content"
android:layout_height="@dimen/zoomed_view_toolbar_height"
android:background="@android:color/transparent"
android:padding="12dip"
android:layout_alignLeft="@+id/zoomed_image_view"
android:textSize="16sp"
android:textColor="@color/text_and_tabs_tray_grey"/>
<ImageView
android:id="@+id/dialog_close"
android:scaleType="center"
android:layout_width="@dimen/zoomed_view_toolbar_height"
android:layout_height="@dimen/zoomed_view_toolbar_height"
android:layout_alignRight="@id/zoomed_image_view"
android:src="@drawable/close_edit_mode_selector"/>
<ImageView
android:id="@id/zoomed_image_view"
android:layout_below="@id/change_zoom_factor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</org.mozilla.gecko.ZoomedView>

View File

@ -205,6 +205,10 @@
<dimen name="horizontal_drag_area">256dp</dimen>
<!-- ZoomedView dimensions. -->
<dimen name="zoomed_view_toolbar_height">44dp</dimen>
<dimen name="drawable_dropshadow_size">3dp</dimen>
<!-- Find-In-Page dialog dimensions. -->
<dimen name="find_in_page_text_margin_left">5dip</dimen>
<dimen name="find_in_page_text_margin_right">12dip</dimen>

View File

@ -528,5 +528,7 @@
<string name="colon">&colon;</string>
<string name="percent">&percent;</string>
<string name="remote_tabs_last_synced">&remote_tabs_last_synced;</string>
</resources>

View File

@ -157,6 +157,9 @@ RootActor.prototype = {
// Added in Gecko 40, indicating that the backend isn't stupid about
// sending resumption packets on tab navigation.
noNeedToFakeResumptionOnNavigation: true,
// Added in Firefox 40. Indicates that the backend supports registering custom
// commands through the WebConsoleCommands API.
webConsoleCommands: true,
// Whether root actor exposes tab actors
// if allowChromeProcess is true, you can fetch a ChromeActor instance
// to debug chrome and any non-content ressource via getProcess request

View File

@ -632,18 +632,103 @@ BrowserTabList.prototype.onCloseWindow = DevToolsUtils.makeInfallible(function(a
exports.BrowserTabList = BrowserTabList;
/**
* Creates a tab actor for handling requests to a browser tab, like
* attaching and detaching. TabActor respects the actor factories
* registered with DebuggerServer.addTabActor.
* Creates a TabActor whose main goal is to manage lifetime and
* expose the tab actors being registered via DebuggerServer.registerModule.
* But also track the lifetime of the document being tracked.
*
* ### Main requests:
*
* `attach`/`detach` requests:
* - start/stop document watching:
* Starts watching for new documents and emits `tabNavigated` and
* `frameUpdate` over RDP.
* - retrieve the thread actor:
* Instantiates a ThreadActor that can be later attached to in order to
* debug JS sources in the document.
* `switchToFrame`:
* Change the targeted document of the whole TabActor, and its child tab actors
* to an iframe or back to its original document.
*
* Most of the TabActor properties (like `chromeEventHandler` or `docShells`)
* are meant to be used by the various child tab actors.
*
* ### RDP events:
*
* - `tabNavigated`:
* Sent when the tab is about to navigate or has just navigated to
* a different document.
* This event contains the following attributes:
* * url (string) The new URI being loaded.
* * nativeConsoleAPI (boolean) `false` if the console API of the page has been
* overridden (e.g. by Firebug),
* `true` if the Gecko implementation is used.
* * state (string) `start` if we just start requesting the new URL,
* `stop` if the new URL is done loading.
* * isFrameSwitching (boolean) Indicates the event is dispatched when
* switching the TabActor context to
* a different frame. When we switch to
* an iframe, there is no document load.
* The targeted document is most likely
* going to be already done loading.
* * title (string) The document title being loaded.
* (sent only on state=stop)
*
* - `frameUpdate`:
* Sent when there was a change in the child frames contained in the document
* or when the tab's context was switched to another frame.
* This event can have four different forms depending on the type of incident:
* * One or many frames are updated:
* { frames: [{ id, url, title, parentID }, ...] }
* * One frame got destroyed:
* { frames: [{ id, destroy: true }]}
* * All frames got destroyed:
* { destroyAll: true }
* * We switched the context of the TabActor to a specific frame:
* { selected: #id }
*
* ### Internal, non-rdp events:
* Various events are also dispatched on the TabActor itself that are not
* related to RDP, so, not sent to the client. They all relate to the documents
* tracked by the TabActor (its main targeted document, but also any of its iframes).
* - will-navigate
* This event fires once navigation starts.
* All pending user prompts are dealt with,
* but it is fired before the first request starts.
* - navigate
* This event is fired once the document's readyState is "complete".
* - window-ready
* This event is fired on three distinct scenarios:
* * When a new Window object is crafted, equivalent of `DOMWindowCreated`.
* It is dispatched before any page script is executed.
* * We will have already received a window-ready event for this window
* when it was created, but we received a window-destroyed event when
* it was frozen into the bfcache, and now the user navigated back to
* this page, so it's now live again and we should resume handling it.
* * For each existing document, when an `attach` request is received.
* At this point scripts in the page will be already loaded.
* - window-destroyed
* This event is fired in two cases:
* * When the window object is destroyed, i.e. when the related document
* is garbage collected. This can happen when the tab is closed or the
* iframe is removed from the DOM.
* It is equivalent of `inner-window-destroyed` event.
* * When the page goes into the bfcache and gets frozen.
* The equivalent of `pagehide`.
* - changed-toplevel-document
* This event fires when we switch the TabActor targeted document
* to one of its iframes, or back to its original top document.
* It is dispatched between window-destroyed and window-ready.
*
* Note that *all* these events are dispatched in the following order
* when we switch the context of the TabActor to a given iframe:
* will-navigate, window-destroyed, changed-toplevel-document, window-ready, navigate
*
* This class is subclassed by BrowserTabActor and
* ContentActor. Subclasses are expected to implement a getter
* the docShell properties.
* for the docShell property.
*
* @param aConnection DebuggerServerConnection
* The conection to the client.
* @param aChromeEventHandler
* An object on which listen for DOMWindowCreated and pageshow events.
*/
function TabActor(aConnection)
{

View File

@ -25,6 +25,11 @@ function evaluateJS(input) {
return new Promise((resolve) => gState.client.evaluateJS(input, resolve));
}
function* evaluateJSAndCheckResult(input, result) {
let response = yield evaluateJS(input);
checkObject(response, {result});
}
function startTest()
{
removeEventListener("load", startTest);
@ -138,6 +143,34 @@ tests = [
WebConsoleCommands.unregister("$foo");
ok(!WebConsoleCommands.hasCommand("$foo"), "$foo should be unregistered");
nextTest();
}),
Task.async(function* unregisterAfterOverridingTwice() {
WebConsoleCommands.register("keys", (owner, obj) => "command 1");
info("checking the value of the first override");
yield evaluateJSAndCheckResult("keys('foo');", "command 1");
let orig = WebConsoleCommands.getCommand("keys");
WebConsoleCommands.register("keys", (owner, obj) => {
if (obj === "quack")
return "bang!";
return orig(owner, obj);
});
info("checking the values after the second override");
yield evaluateJSAndCheckResult("keys({});", "command 1");
yield evaluateJSAndCheckResult("keys('quack');", "bang!");
WebConsoleCommands.unregister("keys");
info("checking the value after unregistration (should restore " +
"the original command)");
yield evaluateJSAndCheckResult("keys({});", {
class: "Array",
preview: {items: []}
});
nextTest();
})
];

View File

@ -1529,6 +1529,19 @@ ConsoleAPIListener.prototype =
*/
let WebConsoleCommands = {
_registeredCommands: new Map(),
_originalCommands: new Map(),
/**
* @private
* Reserved for built-in commands. To register a command from the code of an
* add-on, see WebConsoleCommands.register instead.
*
* @see WebConsoleCommands.register
*/
_registerOriginal: function (name, command) {
this.register(name, command);
this._originalCommands.set(name, this.getCommand(name));
},
/**
* Register a new command.
@ -1564,10 +1577,16 @@ let WebConsoleCommands = {
/**
* Unregister a command.
*
* If the command being unregister overrode a built-in command,
* the latter is restored.
*
* @param {string} name The name of the command
*/
unregister: function(name) {
this._registeredCommands.delete(name);
if (this._originalCommands.has(name)) {
this.register(name, this._originalCommands.get(name));
}
},
/**
@ -1611,7 +1630,7 @@ exports.WebConsoleCommands = WebConsoleCommands;
* @return nsIDOMNode or null
* The result of calling document.querySelector(aSelector).
*/
WebConsoleCommands.register("$", function JSTH_$(aOwner, aSelector)
WebConsoleCommands._registerOriginal("$", function JSTH_$(aOwner, aSelector)
{
return aOwner.window.document.querySelector(aSelector);
});
@ -1624,7 +1643,7 @@ WebConsoleCommands.register("$", function JSTH_$(aOwner, aSelector)
* @return nsIDOMNodeList
* Returns the result of document.querySelectorAll(aSelector).
*/
WebConsoleCommands.register("$$", function JSTH_$$(aOwner, aSelector)
WebConsoleCommands._registerOriginal("$$", function JSTH_$$(aOwner, aSelector)
{
return aOwner.window.document.querySelectorAll(aSelector);
});
@ -1635,7 +1654,7 @@ WebConsoleCommands.register("$$", function JSTH_$$(aOwner, aSelector)
* @return object|undefined
* Returns last console evaluation or undefined
*/
WebConsoleCommands.register("$_", {
WebConsoleCommands._registerOriginal("$_", {
get: function(aOwner) {
return aOwner.consoleActor.getLastConsoleInputEvaluation();
}
@ -1651,7 +1670,7 @@ WebConsoleCommands.register("$_", {
* Context to run the xPath query on. Uses window.document if not set.
* @return array of nsIDOMNode
*/
WebConsoleCommands.register("$x", function JSTH_$x(aOwner, aXPath, aContext)
WebConsoleCommands._registerOriginal("$x", function JSTH_$x(aOwner, aXPath, aContext)
{
let nodes = new aOwner.window.wrappedJSObject.Array();
let doc = aOwner.window.document;
@ -1673,7 +1692,7 @@ WebConsoleCommands.register("$x", function JSTH_$x(aOwner, aXPath, aContext)
* @return Object representing the current selection in the
* Inspector, or null if no selection exists.
*/
WebConsoleCommands.register("$0", {
WebConsoleCommands._registerOriginal("$0", {
get: function(aOwner) {
return aOwner.makeDebuggeeValue(aOwner.selectedNode);
}
@ -1682,7 +1701,7 @@ WebConsoleCommands.register("$0", {
/**
* Clears the output of the WebConsole.
*/
WebConsoleCommands.register("clear", function JSTH_clear(aOwner)
WebConsoleCommands._registerOriginal("clear", function JSTH_clear(aOwner)
{
aOwner.helperResult = {
type: "clearOutput",
@ -1692,7 +1711,7 @@ WebConsoleCommands.register("clear", function JSTH_clear(aOwner)
/**
* Clears the input history of the WebConsole.
*/
WebConsoleCommands.register("clearHistory", function JSTH_clearHistory(aOwner)
WebConsoleCommands._registerOriginal("clearHistory", function JSTH_clearHistory(aOwner)
{
aOwner.helperResult = {
type: "clearHistory",
@ -1706,7 +1725,7 @@ WebConsoleCommands.register("clearHistory", function JSTH_clearHistory(aOwner)
* Object to return the property names from.
* @return array of strings
*/
WebConsoleCommands.register("keys", function JSTH_keys(aOwner, aObject)
WebConsoleCommands._registerOriginal("keys", function JSTH_keys(aOwner, aObject)
{
return aOwner.window.wrappedJSObject.Object.keys(WebConsoleUtils.unwrap(aObject));
});
@ -1718,7 +1737,7 @@ WebConsoleCommands.register("keys", function JSTH_keys(aOwner, aObject)
* Object to display the values from.
* @return array of string
*/
WebConsoleCommands.register("values", function JSTH_values(aOwner, aObject)
WebConsoleCommands._registerOriginal("values", function JSTH_values(aOwner, aObject)
{
let arrValues = new aOwner.window.wrappedJSObject.Array();
let obj = WebConsoleUtils.unwrap(aObject);
@ -1733,7 +1752,7 @@ WebConsoleCommands.register("values", function JSTH_values(aOwner, aObject)
/**
* Opens a help window in MDN.
*/
WebConsoleCommands.register("help", function JSTH_help(aOwner)
WebConsoleCommands._registerOriginal("help", function JSTH_help(aOwner)
{
aOwner.helperResult = { type: "help" };
});
@ -1749,7 +1768,7 @@ WebConsoleCommands.register("help", function JSTH_help(aOwner)
* a window object. If you call cd() with no arguments, the current
* eval scope is cleared back to its default (the top window).
*/
WebConsoleCommands.register("cd", function JSTH_cd(aOwner, aWindow)
WebConsoleCommands._registerOriginal("cd", function JSTH_cd(aOwner, aWindow)
{
if (!aWindow) {
aOwner.consoleActor.evalWindow = null;
@ -1778,7 +1797,7 @@ WebConsoleCommands.register("cd", function JSTH_cd(aOwner, aWindow)
* @param object aObject
* Object to inspect.
*/
WebConsoleCommands.register("inspect", function JSTH_inspect(aOwner, aObject)
WebConsoleCommands._registerOriginal("inspect", function JSTH_inspect(aOwner, aObject)
{
let dbgObj = aOwner.makeDebuggeeValue(aObject);
let grip = aOwner.createValueGrip(dbgObj);
@ -1796,7 +1815,7 @@ WebConsoleCommands.register("inspect", function JSTH_inspect(aOwner, aObject)
* Object to print to the output.
* @return string
*/
WebConsoleCommands.register("pprint", function JSTH_pprint(aOwner, aObject)
WebConsoleCommands._registerOriginal("pprint", function JSTH_pprint(aOwner, aObject)
{
if (aObject === null || aObject === undefined || aObject === true ||
aObject === false) {
@ -1843,7 +1862,7 @@ WebConsoleCommands.register("pprint", function JSTH_pprint(aOwner, aObject)
* A value you want to output as a string.
* @return void
*/
WebConsoleCommands.register("print", function JSTH_print(aOwner, aValue)
WebConsoleCommands._registerOriginal("print", function JSTH_print(aOwner, aValue)
{
aOwner.helperResult = { rawOutput: true };
if (typeof aValue === "symbol") {
@ -1863,7 +1882,7 @@ WebConsoleCommands.register("print", function JSTH_print(aOwner, aValue)
* A value you want to copy as a string.
* @return void
*/
WebConsoleCommands.register("copy", function JSTH_copy(aOwner, aValue)
WebConsoleCommands._registerOriginal("copy", function JSTH_copy(aOwner, aValue)
{
let payload;
try {