Merge m-c to graphics

MozReview-Commit-ID: 2cIoGVmLEuA
This commit is contained in:
Kartikaya Gupta 2017-05-08 10:50:52 -04:00
commit 5d5cdc518c
2200 changed files with 535090 additions and 38192 deletions

View File

@ -31,6 +31,16 @@ jobs:
- date
when: [] # never (hook only)
- name: nightly-desktop-win64
job:
type: decision-task
treeherder-symbol: Nd-Win64
triggered-by: nightly
target-tasks-method: nightly_win64
run-on-projects:
- date
when: [] # never (hook only)
- name: nightly-android
job:
type: decision-task

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1357107 - Removing a directory from extensions/ requires re-running configure to update MOZ_EXTENSIONS
Bug 1356927 - Mac builds in automation require a clobber for a change in which ranlib they use

View File

@ -795,10 +795,6 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
// modification are done.
mDocument->ProcessInvalidationList();
// We cannot rely on DOM tree to keep aria-owns relations updated. Make
// a validation to remove dead links.
mDocument->ValidateARIAOwned();
// Process relocation list.
for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
if (mRelocations[idx]->IsInDocument()) {

View File

@ -1972,19 +1972,30 @@ DocAccessible::FireEventsOnInsertion(Accessible* aContainer)
}
void
DocAccessible::ContentRemoved(Accessible* aContent)
DocAccessible::ContentRemoved(Accessible* aChild)
{
MOZ_DIAGNOSTIC_ASSERT(aContent->Parent(), "Unattached accessible from tree");
Accessible* parent = aChild->Parent();
MOZ_DIAGNOSTIC_ASSERT(parent, "Unattached accessible from tree");
#ifdef A11Y_LOG
logging::TreeInfo("process content removal", 0,
"container", aContent->Parent(), "child", aContent, nullptr);
"container", parent, "child", aChild, nullptr);
#endif
TreeMutation mt(aContent->Parent());
mt.BeforeRemoval(aContent);
aContent->Parent()->RemoveChild(aContent);
UncacheChildrenInSubtree(aContent);
TreeMutation mt(parent);
mt.BeforeRemoval(aChild);
if (aChild->IsRelocated()) {
nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(parent);
MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
owned->RemoveElement(aChild);
if (owned->Length() == 0) {
mARIAOwnsHash.Remove(parent);
}
}
parent->RemoveChild(aChild);
UncacheChildrenInSubtree(aChild);
mt.Done();
}
@ -1996,11 +2007,11 @@ DocAccessible::ContentRemoved(nsIContent* aContentNode)
if (acc) {
ContentRemoved(acc);
}
else {
TreeWalker walker(this, aContentNode);
while (Accessible* acc = walker.Next()) {
ContentRemoved(acc);
}
dom::AllChildrenIterator iter =
dom::AllChildrenIterator(aContentNode, nsIContent::eAllChildren, true);
while (nsIContent* childNode = iter.GetNextChild()) {
ContentRemoved(childNode);
}
}
@ -2027,50 +2038,6 @@ DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
return false;
}
void
DocAccessible::ValidateARIAOwned()
{
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
Accessible* owner = it.Key();
nsTArray<RefPtr<Accessible> >* children = it.UserData();
// Owner is about to die, put children back if applicable.
if (owner != this &&
(!mAccessibleCache.GetWeak(reinterpret_cast<void*>(owner)) ||
!owner->IsInDocument())) {
PutChildrenBack(children, 0);
it.Remove();
continue;
}
for (uint32_t idx = 0; idx < children->Length(); idx++) {
Accessible* child = children->ElementAt(idx);
if (!child->IsInDocument()) {
children->RemoveElementAt(idx);
idx--;
continue;
}
NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
// If DOM node doesn't have a frame anymore then shutdown its accessible.
if (child->Parent() && !child->GetFrame()) {
ContentRemoved(child);
children->RemoveElementAt(idx);
idx--;
continue;
}
NS_ASSERTION(child->Parent() == owner,
"Illigally stolen ARIA owned child!");
}
if (children->Length() == 0) {
it.Remove();
}
}
}
void
DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
{
@ -2194,7 +2161,7 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
Accessible* prevChild = walker.Prev();
if (prevChild) {
idxInParent = prevChild->IndexInParent() + 1;
MOZ_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
origContainer = prevChild->Parent();
}
else {
@ -2225,8 +2192,12 @@ DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
// If the child was taken from from an ARIA owns element.
if (aChild->IsRelocated()) {
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(curParent);
children->RemoveElement(aChild);
nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(curParent);
MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
owned->RemoveElement(aChild);
if (owned->Length() == 0) {
mARIAOwnsHash.Remove(curParent);
}
}
NotificationController::MoveGuard mguard(mNotificationController);
@ -2327,10 +2298,19 @@ DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
aRoot->mStateFlags |= eIsNotInDocument;
RemoveDependentIDsFor(aRoot);
nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(aRoot);
uint32_t count = aRoot->ContentChildCount();
for (uint32_t idx = 0; idx < count; idx++) {
Accessible* child = aRoot->ContentChildAt(idx);
if (child->IsRelocated()) {
MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
owned->RemoveElement(child);
if (owned->Length() == 0) {
mARIAOwnsHash.Remove(aRoot);
}
}
// Removing this accessible from the document doesn't mean anything about
// accessibles for subdocuments, so skip removing those from the tree.
if (!child->IsDoc()) {

View File

@ -344,7 +344,7 @@ public:
/**
* Update the tree on content removal.
*/
void ContentRemoved(Accessible* aContent);
void ContentRemoved(Accessible* aAccessible);
void ContentRemoved(nsIContent* aContentNode);
/**
@ -504,11 +504,6 @@ protected:
*/
void ProcessInvalidationList();
/**
* Validates all aria-owns connections and updates the tree accordingly.
*/
void ValidateARIAOwned();
/**
* Steals or puts back accessible subtrees.
*/

View File

@ -47,7 +47,7 @@ function Buffer(subject, encoding /*, bufferLength */) {
let buffer = new Uint8Array(subject > 0 ? Math.floor(subject) : 0);
return buffer;
} catch (e) {
if (/size and count too large/.test(e.message) ||
if (/invalid array length/.test(e.message) ||
/invalid arguments/.test(e.message))
throw new RangeError('Could not instantiate buffer: size of buffer may be too large');
else

View File

@ -15,10 +15,6 @@ const self = require('sdk/self');
const { getTabId } = require('../tabs/utils');
const { getInnerId } = require('../window/utils');
const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { require: devtoolsRequire } = devtools;
const { addContentGlobal, removeContentGlobal } = devtoolsRequire("devtools/server/content-globals");
/**
* Make a new sandbox that inherits given `source`'s principals. Source can be
* URI string, DOMWindow or `null` for system principals.
@ -31,13 +27,6 @@ function sandbox(target, options) {
let sandbox = Cu.Sandbox(target || systemPrincipal, options);
Cu.setSandboxMetadata(sandbox, options.metadata);
let innerWindowID = options.metadata['inner-window-id']
if (innerWindowID) {
addContentGlobal({
global: sandbox,
'inner-window-id': innerWindowID
});
}
return sandbox;
}
exports.sandbox = sandbox;

View File

@ -216,8 +216,6 @@ DEFAULT_TEST_PREFS = {
'layout.css.report_errors': True,
'layout.css.grid.enabled': True,
'layout.spammy_warnings.enabled': False,
# Make sure the disk cache doesn't get auto disabled
'network.http.bypass-cachelock-threshold': 200000,
# Always use network provider for geolocation tests
# so we bypass the OSX dialog raised by the corelocation provider
'geo.provider.testing': True,

View File

@ -31,7 +31,6 @@
"layout.css.report_errors": true,
"layout.css.grid.enabled": true,
"layout.spammy_warnings.enabled": false,
"network.http.bypass-cachelock-threshold": 200000,
"geo.provider.testing": true,
"browser.pagethumbnails.capturing_disabled": true,
"browser.download.panel.shown": true,

View File

@ -10,10 +10,6 @@ const { testPageMod, handleReadyState, openNewTab,
const { cleanUI, after } = require("sdk/test/utils");
const { open, getFrames, getMostRecentBrowserWindow, getInnerId } = require("sdk/window/utils");
const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { require: devtoolsRequire } = devtools;
const contentGlobals = devtoolsRequire("devtools/server/content-globals");
// The following adds Debugger constructor to the global namespace.
const { addDebuggerToGlobal } = require('resource://gre/modules/jsdebugger.jsm');
addDebuggerToGlobal(this);
@ -46,18 +42,6 @@ exports.testDebugMetadata = function(assert, done) {
);
};
exports.testDevToolsExtensionsGetContentGlobals = function(assert, done) {
let mods = testPageMod(assert, done, "about:", [{
include: "about:",
contentScriptWhen: "start",
contentScript: "null;",
}], function(win, done) {
assert.equal(contentGlobals.getContentGlobals({ 'inner-window-id': getInnerId(win) }).length, 1);
done();
}
);
};
after(exports, function*(name, assert) {
assert.pass("cleaning ui.");
yield cleanUI();

View File

@ -145,6 +145,7 @@ function testRegister(assert, text) {
var channel = ios.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
channel.originalURI = aURI;
aLoadInfo.resultPrincipalURI = aURI;
return channel;
},
getURIFlags: function(aURI) {

View File

@ -870,6 +870,8 @@ pref("browser.sessionstore.debug", false);
pref("browser.sessionstore.debug.no_auto_updates", false);
// Forget closed windows/tabs after two weeks
pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
// Maximum number of bytes of DOMSessionStorage data we collect per origin.
pref("browser.sessionstore.dom_storage_limit", 2048);
// allow META refresh by default
pref("accessibility.blockautorefresh", false);
@ -1025,7 +1027,7 @@ pref("security.sandbox.windows.log.stackTraceDepth", 0);
// For information on what the level number means, see
// SetSecurityLevelForGPUProcess() in
// security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
pref("security.sandbox.gpu.level", 1);
pref("security.sandbox.gpu.level", 0);
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)

View File

@ -2,7 +2,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/. */
// Note: this file is included in aboutDialog.xul if MOZ_UPDATER is defined.
// Note: this file is included in aboutDialog.xul and preferences/advanced.xul
// if MOZ_UPDATER is defined.
/* import-globals-from aboutDialog.js */

View File

@ -500,6 +500,18 @@ const gExtensionsNotifications = {
ExtensionsUI.off("change", this.boundUpdate);
},
_createAddonButton(text, icon, callback) {
let button = document.createElement("toolbarbutton");
button.setAttribute("label", text);
const DEFAULT_EXTENSION_ICON =
"chrome://mozapps/skin/extensions/extensionGeneric.svg";
button.setAttribute("image", icon || DEFAULT_EXTENSION_ICON);
button.className = "addon-banner-item";
button.addEventListener("click", callback);
PanelUI.addonNotificationContainer.appendChild(button);
},
updateAlerts() {
let sideloaded = ExtensionsUI.sideloaded;
let updates = ExtensionsUI.updates;
@ -509,32 +521,21 @@ const gExtensionsNotifications = {
PanelUI.showBadgeOnlyNotification("addon-alert");
}
let container = document.getElementById("PanelUI-footer-addons");
let container = PanelUI.addonNotificationContainer;
while (container.firstChild) {
container.firstChild.remove();
}
const DEFAULT_EXTENSION_ICON =
"chrome://mozapps/skin/extensions/extensionGeneric.svg";
let items = 0;
for (let update of updates) {
if (++items > 4) {
break;
}
let button = document.createElement("toolbarbutton");
let text = gNavigatorBundle.getFormattedString("webextPerms.updateMenuItem", [update.addon.name]);
button.setAttribute("label", text);
let icon = update.addon.iconURL || DEFAULT_EXTENSION_ICON;
button.setAttribute("image", icon);
button.addEventListener("click", evt => {
this._createAddonButton(text, update.addon.iconURL, evt => {
ExtensionsUI.showUpdate(gBrowser, update);
});
container.appendChild(button);
}
let appName;
@ -547,18 +548,10 @@ const gExtensionsNotifications = {
appName = brandBundle.getString("brandShortName");
}
let button = document.createElement("toolbarbutton");
let text = gNavigatorBundle.getFormattedString("webextPerms.sideloadMenuItem", [addon.name, appName]);
button.setAttribute("label", text);
let icon = addon.iconURL || DEFAULT_EXTENSION_ICON;
button.setAttribute("image", icon);
button.addEventListener("click", evt => {
this._createAddonButton(text, addon.iconURL, evt => {
ExtensionsUI.showSideloaded(gBrowser, addon);
});
container.appendChild(button);
}
},
};

View File

@ -327,39 +327,39 @@ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"]
@media not all and (min-resolution: 1.1dppx) {
.webextension-browser-action {
list-style-image: var(--webextension-toolbar-image);
list-style-image: var(--webextension-toolbar-image, inherit);
}
.webextension-browser-action[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > .webextension-browser-action {
list-style-image: var(--webextension-menupanel-image);
list-style-image: var(--webextension-menupanel-image, inherit);
}
.webextension-page-action {
list-style-image: var(--webextension-urlbar-image);
list-style-image: var(--webextension-urlbar-image, inherit);
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image);
list-style-image: var(--webextension-menuitem-image, inherit);
}
}
@media (min-resolution: 1.1dppx) {
.webextension-browser-action {
list-style-image: var(--webextension-toolbar-image-2x);
list-style-image: var(--webextension-toolbar-image-2x, inherit);
}
.webextension-browser-action[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > .webextension-browser-action {
list-style-image: var(--webextension-menupanel-image-2x);
list-style-image: var(--webextension-menupanel-image-2x, inherit);
}
.webextension-page-action {
list-style-image: var(--webextension-urlbar-image-2x);
list-style-image: var(--webextension-urlbar-image-2x, inherit);
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image-2x);
list-style-image: var(--webextension-menuitem-image-2x, inherit);
}
}
@ -1187,7 +1187,8 @@ toolbarpaletteitem[place="palette"][hidden] {
max-width: 10em;
}
#main-window[customizing=true] .PanelUI-notification-menu-item {
#main-window[customizing=true] .addon-banner-item,
#main-window[customizing=true] .panel-banner-item {
display: none;
}

View File

@ -3489,10 +3489,7 @@ var PrintPreviewListener = {
_lastRequestedPrintPreviewTab: null,
_createPPBrowser() {
if (!this._tabBeforePrintPreview) {
this._tabBeforePrintPreview = gBrowser.selectedTab;
}
let browser = this._tabBeforePrintPreview.linkedBrowser;
let browser = this.getSourceBrowser();
let preferredRemoteType = browser.remoteType;
return gBrowser.loadOneTab("about:printpreview", {
inBackground: true,
@ -3519,7 +3516,7 @@ var PrintPreviewListener = {
return gBrowser.getBrowserForTab(this._simplifiedPrintPreviewTab);
},
createSimplifiedBrowser() {
let browser = this._tabBeforePrintPreview.linkedBrowser;
let browser = this.getSourceBrowser();
this._simplifyPageTab = gBrowser.loadOneTab("about:printpreview", {
inBackground: true,
sameProcessAsFrameLoader: browser.frameLoader
@ -3527,8 +3524,10 @@ var PrintPreviewListener = {
return this.getSimplifiedSourceBrowser();
},
getSourceBrowser() {
return this._tabBeforePrintPreview ?
this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
if (!this._tabBeforePrintPreview) {
this._tabBeforePrintPreview = gBrowser.selectedTab;
}
return this._tabBeforePrintPreview.linkedBrowser;
},
getSimplifiedSourceBrowser() {
return this._simplifyPageTab ?

View File

@ -51,6 +51,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
"resource://gre/modules/WebNavigationFrames.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
"resource:///modules/Feeds.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "findCssSelector",
"resource://gre/modules/css-selector.js");
Cu.importGlobalProperties(["URL"]);
@ -164,6 +166,7 @@ var handleContentContextMenu = function(event) {
let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
let userContextId = loadContext.originAttributes.userContextId;
let popupNodeSelectors = getNodeSelectors(event.target);
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
let editFlags = SpellCheckHelper.isEditable(event.target, content);
@ -183,13 +186,18 @@ var handleContentContextMenu = function(event) {
let customMenuItems = PageMenuChild.build(event.target);
let principal = doc.nodePrincipal;
sendRpcMessage("contextmenu",
{ editFlags, spellInfo, customMenuItems, addonInfo,
principal, docLocation, charSet, baseURI, referrer,
referrerPolicy, contentType, contentDisposition,
frameOuterWindowID, selectionInfo, disableSetDesktopBg,
loginFillInfo, parentAllowsMixedContent, userContextId },
{ event, popupNode: event.target });
loginFillInfo, parentAllowsMixedContent, userContextId,
popupNodeSelectors,
}, {
event,
popupNode: event.target,
});
} else {
// Break out to the parent window and pass the add-on info along
let browser = docShell.chromeEventHandler;
@ -198,6 +206,7 @@ var handleContentContextMenu = function(event) {
isRemote: false,
event,
popupNode: event.target,
popupNodeSelectors,
browser,
addonInfo,
documentURIObject: doc.documentURIObject,
@ -245,6 +254,28 @@ const PREF_SSL_IMPACT = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
return prefs.concat(Services.prefs.getChildList(root));
}, []);
/**
* Retrieve the array of CSS selectors corresponding to the provided node. The first item
* of the array is the selector of the node in its owner document. Additional items are
* used if the node is inside a frame, each representing the CSS selector for finding the
* frame element in its parent document.
*
* This format is expected by DevTools in order to handle the Inspect Node context menu
* item.
*
* @param {Node}
* The node for which the CSS selectors should be computed
* @return {Array} array of css selectors (strings).
*/
function getNodeSelectors(node) {
let selectors = [];
while (node) {
selectors.push(findCssSelector(node));
node = node.ownerGlobal.frameElement;
}
return selectors;
}
function getSerializedSecurityInfo(docShell) {
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]

View File

@ -29,7 +29,7 @@ with Files("sync/**"):
BUG_COMPONENT = ("Firefox", "Sync")
with Files("test/alerts/**"):
BUG_COMPONENT = ("Toolkit", "Notification and Alerts")
BUG_COMPONENT = ("Toolkit", "Notifications and Alerts")
with Files("test/appUpdate/**"):
BUG_COMPONENT = ("Toolkit", "Application Update")
@ -154,9 +154,6 @@ with Files("contentSearch*"):
with Files("*.svg"):
BUG_COMPONENT = ("Firefox", "Theme")
with Files("gcli*"):
BUG_COMPONENT = ("Core", "DOM: Security")
with Files("hiddenWindow.xul"):
BUG_COMPONENT = ("Firefox", "Device Permissions")

View File

@ -33,6 +33,7 @@ function openContextMenu(aMessage) {
gContextMenuContentData = { isRemote: true,
event: aMessage.objects.event,
popupNode: aMessage.objects.popupNode,
popupNodeSelectors: data.popupNodeSelectors,
browser,
editFlags: data.editFlags,
spellInfo,
@ -215,15 +216,15 @@ nsContextMenu.prototype = {
initNavigationItems: function CM_initNavigationItems() {
var shouldShow = !(this.isContentSelected || this.onLink || this.onImage ||
this.onCanvas || this.onVideo || this.onAudio ||
this.onTextInput || this.onSocial);
this.onTextInput) && this.inTabBrowser;
this.showItem("context-navigation", shouldShow);
this.showItem("context-sep-navigation", shouldShow);
let stopped = XULBrowserWindow.stopCommand.getAttribute("disabled") == "true";
let stopReloadItem = "";
if (shouldShow || this.onSocial) {
stopReloadItem = (stopped || this.onSocial) ? "reload" : "stop";
if (shouldShow || !this.inTabBrowser) {
stopReloadItem = (stopped || !this.inTabBrowser) ? "reload" : "stop";
}
this.showItem("context-reload", stopReloadItem == "reload");
@ -290,9 +291,12 @@ nsContextMenu.prototype = {
this.onImage || this.onCanvas ||
this.onVideo || this.onAudio ||
this.onLink || this.onTextInput);
var showInspect = !this.onSocial && gPrefService.getBoolPref("devtools.inspector.enabled");
var showInspect = this.inTabBrowser && gPrefService.getBoolPref("devtools.inspector.enabled");
this.showItem("context-viewsource", shouldShow);
this.showItem("context-viewinfo", shouldShow);
// The page info is broken for WebExtension popups, as the browser is
// destroyed when the popup is closed.
this.setItemAttr("context-viewinfo", "disabled", this.webExtBrowserType === "popup");
this.showItem("inspect-separator", showInspect);
this.showItem("context-inspect", showInspect);
@ -341,6 +345,9 @@ nsContextMenu.prototype = {
.disabled = !this.hasBGImage;
this.showItem("context-viewimageinfo", this.onImage);
// The image info popup is broken for WebExtension popups, since the browser
// is destroyed when the popup is closed.
this.setItemAttr("context-viewimageinfo", "disabled", this.webExtBrowserType === "popup");
this.showItem("context-viewimagedesc", this.onImage && this.imageDescURL !== "");
},
@ -350,11 +357,12 @@ nsContextMenu.prototype = {
this.showItem(bookmarkPage,
!(this.isContentSelected || this.onTextInput || this.onLink ||
this.onImage || this.onVideo || this.onAudio || this.onSocial ||
this.onCanvas));
this.onCanvas || this.inWebExtBrowser));
bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext"));
this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink &&
!this.onSocial) || this.onPlainTextLink);
!this.onSocial && !this.onMozExtLink) ||
this.onPlainTextLink);
this.showItem("context-keywordfield",
this.onTextInput && this.onKeywordField);
this.showItem("frame", this.inFrame);
@ -398,13 +406,14 @@ nsContextMenu.prototype = {
let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
let pageShare = shareEnabled && !(this.isContentSelected ||
this.onTextInput || this.onLink || this.onImage ||
this.onVideo || this.onAudio || this.onCanvas);
this.onVideo || this.onAudio || this.onCanvas ||
this.inWebExtBrowser);
this.showItem("context-sharepage", pageShare);
this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink && !this.onMozExtLink);
this.showItem("context-shareimage", shareEnabled && this.onImage);
this.showItem("context-sharevideo", shareEnabled && this.onVideo);
this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL || this.mediaURL.startsWith("blob:"));
this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL || this.mediaURL.startsWith("blob:") || this.mediaURL.startsWith("moz-extension:"));
},
initSpellingItems() {
@ -612,7 +621,7 @@ nsContextMenu.prototype = {
let gBrowser = this.browser.ownerGlobal.gBrowser;
let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
let { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
return gDevToolsBrowser.inspectNode(gBrowser.selectedTab, this.target);
return gDevToolsBrowser.inspectNode(gBrowser.selectedTab, this.targetSelectors);
},
/**
@ -677,6 +686,10 @@ nsContextMenu.prototype = {
this.onCTPPlugin = false;
this.canSpellCheck = false;
this.onPassword = false;
this.webExtBrowserType = "";
this.inWebExtBrowser = false;
this.inTabBrowser = true;
this.onMozExtLink = false;
if (this.isRemote) {
this.selectionInfo = gContextMenuContentData.selectionInfo;
@ -690,6 +703,12 @@ nsContextMenu.prototype = {
// Remember the node that was clicked.
this.target = aNode;
// Remember the CSS selectors corresponding to clicked node. gContextMenuContentData
// can be null if the menu was triggered by tests in which case use an empty array.
this.targetSelectors = gContextMenuContentData
? gContextMenuContentData.popupNodeSelectors
: [];
let ownerDoc = this.target.ownerDocument;
this.ownerDoc = ownerDoc;
@ -713,6 +732,10 @@ nsContextMenu.prototype = {
this.frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
}
this.onSocial = !!this.browser.getAttribute("origin");
this.webExtBrowserType = this.browser.getAttribute("webextension-view-type");
this.inWebExtBrowser = !!this.webExtBrowserType;
this.inTabBrowser = this.browser.ownerGlobal.gBrowser ?
!!this.browser.ownerGlobal.gBrowser.getTabForBrowser(this.browser) : false;
// Check if we are in a synthetic document (stand alone image, video, etc.).
this.inSyntheticDoc = ownerDoc.mozSyntheticDocument;
@ -869,6 +892,7 @@ nsContextMenu.prototype = {
this.linkTextStr = this.getLinkText();
this.linkProtocol = this.getLinkProtocol();
this.onMailtoLink = (this.linkProtocol == "mailto");
this.onMozExtLink = (this.linkProtocol == "moz-extension");
this.onSaveableLink = this.isLinkSaveable( this.link );
this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
try {
@ -1033,8 +1057,11 @@ nsContextMenu.prototype = {
}
if (!this.isRemote) {
params.frameOuterWindowID = WebNavigationFrames.getFrameId(this.target.ownerGlobal);
// Propagate the frameOuterWindowID value saved when
// the context menu has been opened.
params.frameOuterWindowID = this.frameOuterWindowID;
}
// If we want to change userContextId, we must be sure that we don't
// propagate the referrer.
if ("userContextId" in params &&
@ -1911,7 +1938,7 @@ nsContextMenu.prototype = {
_getTelemetryPageContextInfo() {
let rv = [];
for (let k of ["isContentSelected", "onLink", "onImage", "onCanvas", "onVideo", "onAudio",
"onTextInput", "onSocial"]) {
"onTextInput", "onSocial", "inWebExtBrowser", "inTabBrowser"]) {
if (this[k]) {
rv.push(k.replace(/^(?:is|on)(.)/, (match, firstLetter) => firstLetter.toLowerCase()));
}

View File

@ -208,7 +208,7 @@ function processStep({notificationId, button, beforeClick, cleanup}) {
return;
}
let notification = document.getElementById(`PanelUI-${notificationId}-notification`);
let notification = document.getElementById(`appMenu-${notificationId}-notification`);
is(notification.hidden, false, `${notificationId} notification is showing`);
if (beforeClick) {
yield Task.spawn(beforeClick);

View File

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/browser-test"
]
};

View File

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
!/browser/base/content/test/general/contextmenu_common.js
subtst_contextmenu_webext.html
[browser_contextmenu_mozextension.js]

View File

@ -0,0 +1,82 @@
"use strict";
var { SocialService } = Cu.import("resource:///modules/SocialService.jsm", {});
let contextMenu;
let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
// A social share provider
let manifest = {
name: "provider 1",
origin: "https://example.com",
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
};
add_task(function* test_setup() {
const example_base = "http://example.com/browser/browser/base/content/test/contextMenu/";
const url = example_base + "subtst_contextmenu_webext.html";
yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
const contextmenu_common = chrome_base + "contextmenu_common.js";
/* import-globals-from ../general/contextmenu_common.js */
Services.scriptloader.loadSubScript(contextmenu_common, this);
// Enable social sharing functions in the browser, so the context menu item is shown.
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
yield new Promise((resolve) => SocialService.addProvider(manifest, resolve));
ok(SocialShare.shareButton && !SocialShare.shareButton.disabled, "Sharing is enabled");
});
add_task(function* test_link() {
// gets hidden for this case.
yield test_contextmenu("#link",
["context-openlinkintab", true,
...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
// We need a blank entry here because the containers submenu is
// dynamically generated with no ids.
...(hasContainers ? ["", null] : []),
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,
"context-savelink", true,
"context-copylink", true,
"context-searchselect", true]);
});
add_task(function* test_video() {
yield test_contextmenu("#video",
["context-media-play", null,
"context-media-mute", null,
"context-media-playbackrate", null,
["context-media-playbackrate-050x", null,
"context-media-playbackrate-100x", null,
"context-media-playbackrate-125x", null,
"context-media-playbackrate-150x", null,
"context-media-playbackrate-200x", null], null,
"context-media-loop", null,
"context-media-showcontrols", null,
"context-video-fullscreen", null,
"---", null,
"context-viewvideo", null,
"context-copyvideourl", null,
"---", null,
"context-savevideo", null,
"context-sharevideo", false,
"context-video-saveimage", null,
"context-sendvideo", null,
"context-castvideo", null,
[], null
]);
});
add_task(function* test_cleanup() {
lastElementSelector = null;
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
yield new Promise((resolve) => {
return SocialService.disableProvider(manifest.origin, resolve);
});
});

View File

@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Subtest for browser context menu</title>
</head>
<body>
Browser context menu subtest.
<a href="moz-extension://foo-bar/tab.html" id="link">Link to an extension resource</a>
<video src="moz-extension://foo-bar/video.ogg" id="video"></video>
</body>
</html>

View File

@ -6,3 +6,4 @@ support-files =
skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
[browser_selectpopup_colors.js]
skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
[browser_selectpopup_searchfocus.js]

View File

@ -0,0 +1,42 @@
let SELECT =
"<html><body><select id='one'>";
for (let i = 0; i < 75; i++) {
SELECT +=
` <option>${i}${i}${i}${i}${i}</option>`;
}
SELECT +=
' <option selected="true">{"end": "true"}</option>' +
"</select></body></html>";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
"set": [
["dom.select_popup_in_parent.enabled", true],
["dom.forms.selectSearch", true]
]
});
});
add_task(function* test_focus_on_search_shouldnt_close_popup() {
const pageUrl = "data:text/html," + escape(SELECT);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
let menulist = document.getElementById("ContentSelectDropdown");
let selectPopup = menulist.menupopup;
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
yield popupShownPromise;
let searchInput = selectPopup.querySelector("textbox[type='search']");
searchInput.scrollIntoView();
let searchFocused = BrowserTestUtils.waitForEvent(searchInput, "focus");
yield EventUtils.synthesizeMouseAtCenter(searchInput, {}, window);
yield searchFocused;
is(selectPopup.state, "open", "select popup should still be open after clicking on the search field");
yield hideSelectPopup(selectPopup, "escape");
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -25,7 +25,7 @@ add_task(function*() {
win = SessionStore.undoCloseWindow(0);
yield BrowserTestUtils.waitForEvent(win, "load");
yield BrowserTestUtils.waitForEvent(win.gBrowser.tabs[0], "SSTabRestored");
yield BrowserTestUtils.waitForEvent(win.gBrowser.tabContainer, "SSTabRestored");
is(win.gBrowser.tabs.length, 1, "Should have restored one tab");
is(win.gBrowser.selectedBrowser.currentURI.spec, uri, "Should have restored the right page");

View File

@ -214,10 +214,6 @@ var whitelist = new Set([
// Bug 1348533
{file: "chrome://mozapps/skin/downloads/buttons.png", platforms: ["macosx"]},
{file: "chrome://mozapps/skin/downloads/downloadButtons.png", platforms: ["linux", "win"]},
// Bug 1348555
{file: "chrome://mozapps/skin/extensions/dictionaryGeneric-16.png"},
{file: "chrome://mozapps/skin/extensions/search.png", platforms: ["macosx"]},
{file: "chrome://mozapps/skin/extensions/themeGeneric-16.png"},
// Bug 1348556
{file: "chrome://mozapps/skin/plugins/pluginBlocked.png"},
// Bug 1348558
@ -235,10 +231,6 @@ var whitelist = new Set([
{file: "resource://gre/modules/Manifest.jsm"},
// Bug 1351089
{file: "resource://gre/modules/PresentationDeviceInfoManager.jsm"},
// Bug 1351091
{file: "resource://gre/modules/Profiler.jsm"},
// Bug 1351658
{file: "resource://gre/modules/PropertyListUtils.jsm", platforms: ["linux", "win"]},
// Bug 1351097
{file: "resource://gre/modules/accessibility/AccessFu.jsm"},
// Bug 1351637

View File

@ -1,9 +1,11 @@
[DEFAULT]
support-files =
dummy_page.html
test_bug1358314.html
[browser_abandonment_telemetry.js]
[browser_allow_process_switches_despite_related_browser.js]
[browser_contextmenu_openlink_after_tabnavigated.js]
[browser_tabCloseProbes.js]
[browser_tabSpinnerProbe.js]
skip-if = !e10s # Tab spinner is e10s only.

View File

@ -0,0 +1,43 @@
"use strict";
const example_base = "http://example.com/browser/browser/base/content/test/tabs/";
add_task(function* test_contextmenu_openlink_after_tabnavigated() {
let url = example_base + "test_bug1358314.html";
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
const contextMenu = document.getElementById("contentAreaContextMenu");
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouse("a", 0, 0, {
type: "contextmenu",
button: 2,
}, gBrowser.selectedBrowser);
yield awaitPopupShown;
info("Popup Shown");
info("Navigate the tab with the opened context menu");
gBrowser.selectedBrowser.loadURI("about:blank");
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
let awaitNewTabOpen = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/");
info("Click the 'open link in new tab' menu item");
let openLinkMenuItem = contextMenu.querySelector("#context-openlinkintab");
openLinkMenuItem.click();
info("Wait for the new tab to be opened");
const newTab = yield awaitNewTabOpen;
// Close the contextMenu popup if it has not been closed yet.
contextMenu.hidePopup();
yield BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
const newTabURL = yield ContentTask.spawn(newTab.linkedBrowser, null, function* () {
return content.location.href;
});
is(newTabURL, "http://example.com/", "Got the expected URL loaded in the new tab");
yield BrowserTestUtils.removeTab(newTab);
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -1,12 +1,6 @@
const kURL1 = "data:text/html,Should I stay or should I go?";
const kURL2 = "data:text/html,I shouldn't be here!";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
set: [["dom.ipc.processCount", 1]]
});
});
/**
* Verify that if we open a new tab and try to make it the selected tab while
* print preview is up, that doesn't happen.

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>Test page</p>
<a href="/">Link</a>
</body>
</html>

View File

@ -185,7 +185,7 @@ add_task(function* () {
// Find the menu entries for sideloaded extensions
yield PanelUI.show();
let addons = document.getElementById("PanelUI-footer-addons");
let addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 4, "Have 4 menu entries for sideloaded extensions");
// Click the first sideloaded extension
@ -226,7 +226,7 @@ add_task(function* () {
// Should still have 3 entries in the hamburger menu
yield PanelUI.show();
addons = document.getElementById("PanelUI-footer-addons");
addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 3, "Have 3 menu entries for sideloaded extensions");
// Click the second sideloaded extension and wait for the notification
@ -260,7 +260,7 @@ add_task(function* () {
// Should still have 2 entries in the hamburger menu
yield PanelUI.show();
addons = document.getElementById("PanelUI-footer-addons");
addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions");
// Close the hamburger menu and go directly to the addons manager
@ -299,7 +299,7 @@ add_task(function* () {
// Should still have 1 entry in the hamburger menu
yield PanelUI.show();
addons = document.getElementById("PanelUI-footer-addons");
addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
// Close the hamburger menu and go to the detail page for this addon

View File

@ -81,7 +81,7 @@ function* backgroundUpdateTest(url, id, checkIconFn) {
// Find the menu entry for the update
yield PanelUI.show();
let addons = document.getElementById("PanelUI-footer-addons");
let addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 1, "Have a menu entry for the update");
// Click the menu item
@ -121,7 +121,7 @@ function* backgroundUpdateTest(url, id, checkIconFn) {
is(getBadgeStatus(), "", "Addon alert badge should be gone");
yield PanelUI.show();
addons = document.getElementById("PanelUI-footer-addons");
addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 0, "Update menu entries should be gone");
yield PanelUI.hide();
@ -135,7 +135,7 @@ function* backgroundUpdateTest(url, id, checkIconFn) {
// Find the menu entry for the update
yield PanelUI.show();
addons = document.getElementById("PanelUI-footer-addons");
addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 1, "Have a menu entry for the update");
// Click the menu item

View File

@ -59,7 +59,7 @@ async function testNoPrompt(origUrl, id) {
is(getBadgeStatus(), "", "Should not have addon alert badge");
await PanelUI.show();
let addons = document.getElementById("PanelUI-footer-addons");
let addons = PanelUI.addonNotificationContainer;
is(addons.children.length, 0, "Have 0 updates in the PanelUI menu");
await PanelUI.hide();

View File

@ -12,10 +12,9 @@ skip-if = (os == "linux" && debug) # linux: bug 976544
[browser_devices_get_user_media_multi_process.js]
skip-if = e10s && (asan || debug) # bug 1347625
[browser_devices_get_user_media_screen.js]
skip-if = (os == "linux") || (os == "win" && !debug) # bug 1320994 for linux opt, bug 1338038 for windows and linux debug
[browser_devices_get_user_media_tear_off_tab.js]
[browser_devices_get_user_media_unprompted_access.js]
[browser_devices_get_user_media_unprompted_access_in_frame.js]
[browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
skip-if = (os == "linux") || (os == "win" && bits == 64) # linux: bug 1331616, win8: bug 1334752
skip-if = (os == "win" && bits == 64) # win8: bug 1334752
[browser_webrtc_hooks.js]

View File

@ -55,7 +55,7 @@ var gTests = [
menulist.getItemAtIndex(2).doCommand();
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
"the 'all windows will be shared' warning should now be visible");
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
yield promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden, 100);
ok(!document.getElementById("webRTC-preview").hidden,
"the preview area is visible");
ok(!document.getElementById("webRTC-previewWarning").hidden,
@ -410,7 +410,7 @@ var gTests = [
yield check({video: true, audio: true});
info("Stop the camera, everything should stop.");
yield stopSharing("camera", false, true);
yield stopSharing("camera");
info("Now, share only the screen...");
indicator = promiseIndicatorWindow();
@ -423,7 +423,7 @@ var gTests = [
yield check({video: true, audio: true, screen: "Screen"});
info("Stop the camera, this should stop everything.");
yield stopSharing("camera", false, true);
yield stopSharing("camera");
}
},

View File

@ -62,7 +62,7 @@ var gTests = [
SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser);
// After closing all streams, gUM(audio+camera) causes a prompt.
yield closeStream(false, 0, 2);
yield closeStream();
promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(true, true);
yield promise;
@ -169,7 +169,7 @@ var gTests = [
yield checkSharingUI({audio: false, video: true});
// close all streams
yield closeStream(false, 0, 2);
yield closeStream();
}
},
@ -241,7 +241,7 @@ var gTests = [
yield checkSharingUI({audio: true, video: false});
// close all streams
yield closeStream(false, 0, 2);
yield closeStream();
}
}

View File

@ -57,7 +57,7 @@ var gTests = [
yield expectObserverCalled("recording-device-events");
// close the stream
yield closeStream(false, "frame1", 2);
yield closeStream(false, "frame1");
}
},
@ -197,7 +197,7 @@ var gTests = [
yield expectObserverCalled("recording-window-ended");
// close the stream
yield closeStream(false);
yield closeStream();
SitePermissions.remove(null, "screen", gBrowser.selectedBrowser);
SitePermissions.remove(null, "camera", gBrowser.selectedBrowser);
SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser);

View File

@ -41,7 +41,6 @@ var gTests = [
yield Promise.all(promises);
promises = [promiseObserverCalled("recording-device-events"),
promiseObserverCalled("recording-device-events"),
promiseObserverCalled("recording-window-ended")];
yield BrowserTestUtils.closeWindow(win);
yield Promise.all(promises);

View File

@ -30,9 +30,9 @@ function waitForCondition(condition, nextTest, errorMsg, retryTimes) {
var moveOn = function() { clearInterval(interval); nextTest(); };
}
function promiseWaitForCondition(aConditionFn) {
function promiseWaitForCondition(aConditionFn, retryTimes) {
let deferred = Promise.defer();
waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.");
waitForCondition(aConditionFn, deferred.resolve, "Condition didn't pass.", retryTimes);
return deferred.promise;
}
@ -215,7 +215,7 @@ function expectObserverCalled(aTopic) {
});
}
function expectNoObserverCalled(aIgnoreDeviceEvents = false) {
function expectNoObserverCalled() {
return new Promise(resolve => {
let mm = _mm();
mm.addMessageListener("Test:ExpectNoObserverCalled:Reply",
@ -225,15 +225,7 @@ function expectNoObserverCalled(aIgnoreDeviceEvents = false) {
if (!data[topic])
continue;
// If we are stopping tracks that were created from 2 different
// getUserMedia calls, the "recording-device-events" notification is
// fired twice on Windows and Mac, and intermittently twice on Linux.
if (topic == "recording-device-events" && aIgnoreDeviceEvents) {
todo(false, "Got " + data[topic] + " unexpected " + topic +
" notifications, see bug 1320994");
} else {
is(data[topic], 0, topic + " notification unexpected");
}
is(data[topic], 0, topic + " notification unexpected");
}
resolve();
});
@ -354,8 +346,7 @@ function getMediaCaptureState() {
});
}
function* stopSharing(aType = "camera", aShouldKeepSharing = false,
aExpectDoubleRecordingEvent = false) {
function* stopSharing(aType = "camera", aShouldKeepSharing = false) {
let promiseRecordingEvent = promiseObserverCalled("recording-device-events");
gIdentityHandler._identityBox.click();
let permissions = document.getElementById("identity-popup-permission-list");
@ -372,7 +363,7 @@ function* stopSharing(aType = "camera", aShouldKeepSharing = false,
if (!aShouldKeepSharing)
yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled(aExpectDoubleRecordingEvent);
yield expectNoObserverCalled();
if (!aShouldKeepSharing)
yield* checkNotSharing();
@ -391,16 +382,13 @@ function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType,
});
}
function* closeStream(aAlreadyClosed, aFrameId, aStreamCount = 1) {
function* closeStream(aAlreadyClosed, aFrameId) {
yield expectNoObserverCalled();
let promises;
if (!aAlreadyClosed) {
promises = [];
for (let i = 0; i < aStreamCount; i++) {
promises.push(promiseObserverCalled("recording-device-events"));
}
promises.push(promiseObserverCalled("recording-window-ended"));
promises = [promiseObserverCalled("recording-device-events"),
promiseObserverCalled("recording-window-ended")];
}
info("closing the stream");

View File

@ -17,6 +17,16 @@ add_task(function* test_toolbar_element_restyles_on_activation() {
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
yield new Promise(resolve => waitForFocus(resolve, win2));
// Flush any pending styles before we take a measurement.
win1.getComputedStyle(win1.document.firstElementChild);
win2.getComputedStyle(win2.document.firstElementChild);
// Clear the focused element from each window so that when
// we raise them, the focus of the element doesn't cause an
// unrelated style flush.
Services.focus.clearFocus(win1);
Services.focus.clearFocus(win2);
let utils1 = SpecialPowers.getDOMWindowUtils(win1);
restyles.win1.initial = utils1.elementsRestyled;

View File

@ -117,9 +117,6 @@ browser.jar:
content/browser/defaultthemes/compact.header.png (content/defaultthemes/compact.header.png)
content/browser/defaultthemes/compactdark.icon.svg (content/defaultthemes/compactdark.icon.svg)
content/browser/defaultthemes/compactlight.icon.svg (content/defaultthemes/compactlight.icon.svg)
content/browser/gcli_sec_bad.svg (content/gcli_sec_bad.svg)
content/browser/gcli_sec_good.svg (content/gcli_sec_good.svg)
content/browser/gcli_sec_moderate.svg (content/gcli_sec_moderate.svg)
content/browser/newtab/newTab.xhtml (content/newtab/newTab.xhtml)
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
content/browser/newtab/newTab.css (content/newtab/newTab.css)

View File

@ -17,6 +17,7 @@ MOCHITEST_CHROME_MANIFESTS += [
BROWSER_CHROME_MANIFESTS += [
'content/test/alerts/browser.ini',
'content/test/captivePortal/browser.ini',
'content/test/contextMenu/browser.ini',
'content/test/forms/browser.ini',
'content/test/general/browser.ini',
'content/test/newtab/browser.ini',

View File

@ -172,27 +172,23 @@ AboutRedirector::NewChannel(nsIURI* aURI,
rv = NS_NewURI(getter_AddRefs(tempURI), url);
NS_ENSURE_SUCCESS(rv, rv);
// If tempURI links to an external URI (i.e. something other than
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
// channel which forces the channel owner to reflect the displayed
// URL rather then being the systemPrincipal.
// If tempURI links to an internal URI (chrome://, resource://)
// then set the result principal URL on the channel's load info.
// Otherwise, we leave it null which forces the channel principal
// to reflect the displayed URL rather than being the systemPrincipal.
bool isUIResource = false;
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
&isUIResource);
NS_ENSURE_SUCCESS(rv, rv);
nsLoadFlags loadFlags = isUIResource
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,
aLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
loadFlags);
aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
if (isUIResource) {
aLoadInfo->SetResultPrincipalURI(aURI);
}
tempChannel->SetOriginalURI(aURI);
NS_ADDREF(*result = tempChannel);

View File

@ -17,17 +17,12 @@
<footer id="PanelUI-footer">
<vbox id="PanelUI-footer-addons"></vbox>
<toolbarbutton id="PanelUI-update-available-menu-item"
<toolbarbutton class="panel-banner-item"
label-update-available="&updateAvailable.panelUI.label;"
label-update-manual="&updateManual.panelUI.label;"
label-update-restart="&updateRestart.panelUI.label;"
oncommand="PanelUI._onBannerItemSelected(event)"
wrap="true"
label="&updateAvailable.panelUI.label;"
hidden="true"/>
<toolbarbutton id="PanelUI-update-manual-menu-item"
wrap="true"
label="&updateManual.panelUI.label;"
hidden="true"/>
<toolbarbutton id="PanelUI-update-restart-menu-item"
wrap="true"
label="&updateRestart.panelUI.label;"
hidden="true"/>
<hbox id="PanelUI-footer-fxa">
<hbox id="PanelUI-fxa-status"
@ -431,7 +426,7 @@
oncommand="PanicButtonNotifier.close()"/>
</panel>
<panel id="PanelUI-notification-popup"
<panel id="appMenu-notification-popup"
class="popup-notification-panel"
type="arrow"
position="after_start"
@ -441,7 +436,7 @@
noautohide="true"
nopreventnavboxhide="true"
role="alert">
<popupnotification id="PanelUI-update-available-notification"
<popupnotification id="appMenu-update-available-notification"
popupid="update-available"
label="&updateAvailable.header.message;"
buttonlabel="&updateAvailable.acceptButton.label;"
@ -459,7 +454,7 @@
</popupnotificationcontent>
</popupnotification>
<popupnotification id="PanelUI-update-manual-notification"
<popupnotification id="appMenu-update-manual-notification"
popupid="update-manual"
label="&updateManual.header.message;"
buttonlabel="&updateManual.acceptButton.label;"
@ -477,7 +472,7 @@
</popupnotificationcontent>
</popupnotification>
<popupnotification id="PanelUI-update-restart-notification"
<popupnotification id="appMenu-update-restart-notification"
popupid="update-restart"
label="&updateRestart.header.message2;"
buttonlabel="&updateRestart.acceptButton.label;"
@ -505,6 +500,14 @@
<panelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView">
<panelview id="appMenu-mainView" class="cui-widget-panelview PanelUI-subView">
<vbox class="panel-subview-body">
<vbox id="appMenu-addon-banners"/>
<toolbarbutton class="panel-banner-item"
label-update-available="&updateAvailable.panelUI.label;"
label-update-manual="&updateManual.panelUI.label;"
label-update-restart="&updateRestart.panelUI.label;"
oncommand="PanelUI._onBannerItemSelected(event)"
wrap="true"
hidden="true"/>
<toolbarbutton id="appMenu-new-window-button"
class="subviewbutton subviewbutton-iconic"
label="&newNavigatorCmd.label;"

View File

@ -35,9 +35,9 @@ const PanelUI = {
helpView: "PanelUI-helpView",
menuButton: "PanelUI-menu-button",
panel: gPhotonStructure ? "appMenu-popup" : "PanelUI-popup",
notificationPanel: "PanelUI-notification-popup",
notificationPanel: "appMenu-notification-popup",
scroller: "PanelUI-contents-scroller",
footer: "PanelUI-footer",
addonNotificationContainer: gPhotonStructure ? "appMenu-addon-banners" : "PanelUI-footer-addons",
overflowFixedList: gPhotonStructure ? "widget-overflow-fixed-list" : "",
};
@ -703,13 +703,13 @@ const PanelUI = {
this._hidePopup();
this._clearBadge();
if (!this.notifications[0].options.badgeOnly) {
this._showMenuItem(this.notifications[0]);
this._showBannerItem(this.notifications[0]);
}
} else if (doorhangers.length > 0) {
if (window.fullScreen) {
this._hidePopup();
this._showBadge(doorhangers[0]);
this._showMenuItem(doorhangers[0]);
this._showBannerItem(doorhangers[0]);
} else {
this._clearBadge();
this._showNotificationPanel(doorhangers[0]);
@ -717,7 +717,7 @@ const PanelUI = {
} else {
this._hidePopup();
this._showBadge(this.notifications[0]);
this._showMenuItem(this.notifications[0]);
this._showBannerItem(this.notifications[0]);
}
},
@ -744,7 +744,7 @@ const PanelUI = {
_clearAllNotifications() {
this._clearNotificationPanel();
this._clearBadge();
this._clearMenuItems();
this._clearBannerItem();
},
_refreshNotificationPanel(notification) {
@ -766,32 +766,31 @@ const PanelUI = {
this.menuButton.setAttribute("badge-status", badgeStatus);
},
// "Menu item" here refers to an item in the hamburger panel menu. They will
// typically show up as a colored row near the bottom of the panel.
_showMenuItem(notification) {
this._clearMenuItems();
let menuItemId = this._getMenuItemId(notification);
let menuItem = document.getElementById(menuItemId);
if (menuItem) {
menuItem.notification = notification;
menuItem.setAttribute("oncommand", "PanelUI._onNotificationMenuItemSelected(event)");
menuItem.classList.add("PanelUI-notification-menu-item");
menuItem.hidden = false;
menuItem.fromPanelUINotifications = true;
// "Banner item" here refers to an item in the hamburger panel menu. They will
// typically show up as a colored row in the panel.
_showBannerItem(notification) {
if (!this._panelBannerItem) {
this._panelBannerItem = this.mainView.querySelector(".panel-banner-item");
}
let label = this._panelBannerItem.getAttribute("label-" + notification.id);
// Ignore items we don't know about.
if (!label) {
return;
}
this._panelBannerItem.setAttribute("notificationid", notification.id);
this._panelBannerItem.setAttribute("label", label);
this._panelBannerItem.hidden = false;
this._panelBannerItem.notification = notification;
},
_clearBadge() {
this.menuButton.removeAttribute("badge-status");
},
_clearMenuItems() {
for (let child of this.footer.children) {
if (child.fromPanelUINotifications) {
child.notification = null;
child.hidden = true;
}
_clearBannerItem() {
if (this._panelBannerItem) {
this._panelBannerItem.notification = null;
this._panelBannerItem.hidden = true;
}
},
@ -852,7 +851,7 @@ const PanelUI = {
this._updateNotifications();
},
_onNotificationMenuItemSelected(event) {
_onBannerItemSelected(event) {
let target = event.originalTarget;
if (!target.notification)
throw "menucommand target has no associated action/notification";
@ -870,12 +869,10 @@ const PanelUI = {
this._updateNotifications();
},
_getPopupId(notification) { return "PanelUI-" + notification.id + "-notification"; },
_getPopupId(notification) { return "appMenu-" + notification.id + "-notification"; },
_getBadgeStatus(notification) { return notification.id; },
_getMenuItemId(notification) { return "PanelUI-" + notification.id + "-menu-item"; },
_getPanelAnchor(candidate) {
let iconAnchor =
document.getAnonymousElementByAttribute(candidate, "class",

View File

@ -32,7 +32,7 @@ add_task(function* testMainActionCalled() {
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
mainActionButton.click();
@ -82,7 +82,7 @@ add_task(function* testSecondaryActionWorkflow() {
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
secondaryActionButton.click();
@ -94,7 +94,8 @@ add_task(function* testSecondaryActionWorkflow() {
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button.");
let menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label");
is(menuItem.hidden, false, "update-manual menu item is showing.");
yield PanelUI.hide();
@ -136,7 +137,7 @@ add_task(function* testInteractionWithBadges() {
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
secondaryActionButton.click();
@ -147,7 +148,8 @@ add_task(function* testInteractionWithBadges() {
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button.");
let menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label");
is(menuItem.hidden, false, "update-manual menu item is showing.");
menuItem.click();
@ -179,7 +181,7 @@ add_task(function* testAddingBadgeWhileDoorhangerIsShowing() {
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
mainActionButton.click();
@ -265,7 +267,7 @@ add_task(function* testMultipleNonBadges() {
notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
PanelUI.showNotification("update-restart", updateRestartAction);
@ -273,7 +275,7 @@ add_task(function* testMultipleNonBadges() {
notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-restart-notification", "PanelUI is displaying the update-restart notification.");
is(doorhanger.id, "appMenu-update-restart-notification", "PanelUI is displaying the update-restart notification.");
let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
secondaryActionButton.click();
@ -281,11 +283,10 @@ add_task(function* testMultipleNonBadges() {
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is displaying on PanelUI button.");
let menuItem;
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is hidden on PanelUI button.");
menuItem = doc.getElementById("PanelUI-update-restart-menu-item");
let menuItem = PanelUI.mainView.querySelector(".panel-banner-item");
is(menuItem.label, menuItem.getAttribute("label-update-restart"), "Showing correct label");
is(menuItem.hidden, false, "update-restart menu item is showing.");
menuItem.click();
@ -296,7 +297,7 @@ add_task(function* testMultipleNonBadges() {
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is hidden on PanelUI button.");
menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label");
is(menuItem.hidden, false, "update-manual menu item is showing.");
menuItem.click();
@ -318,7 +319,7 @@ add_task(function* testFullscreen() {
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let popuphiddenPromise = BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popuphidden");
EventUtils.synthesizeKey("VK_F11", {});

View File

@ -233,6 +233,7 @@ class BasePopup {
browser.setAttribute("class", "webextension-popup-browser");
browser.setAttribute("webextension-view-type", "popup");
browser.setAttribute("tooltip", "aHTMLTooltip");
browser.setAttribute("contextmenu", "contentAreaContextMenu");
if (this.extension.remote) {
browser.setAttribute("remote", "true");
@ -286,6 +287,9 @@ class BasePopup {
setupBrowser(browser);
let mm = browser.messageManager;
// Sets the context information for context menus.
mm.loadFrameScript("chrome://browser/content/content.js", true);
mm.loadFrameScript(
"chrome://extensions/content/ext-browser-content.js", false);

View File

@ -19,6 +19,7 @@ Cu.import("resource://gre/modules/EventEmitter.jsm");
Cu.import("resource://gre/modules/Task.jsm");
var {
DefaultWeakMap,
IconDetails,
} = ExtensionUtils;
@ -57,6 +58,8 @@ this.browserAction = class extends ExtensionAPI {
let options = extension.manifest.browser_action;
this.iconData = new DefaultWeakMap(icons => this.getIconData(icons));
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-browser-action`;
this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
@ -321,7 +324,7 @@ this.browserAction = class extends ExtensionAPI {
if (pendingPopup) {
if (pendingPopup.window === window && pendingPopup.popupURL === popupURL) {
if (!this.blockParser) {
if (!blockParser) {
pendingPopup.unblockParser();
}
@ -367,58 +370,74 @@ this.browserAction = class extends ExtensionAPI {
// in |tabData|.
updateButton(node, tabData) {
let title = tabData.title || this.extension.name;
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
if (tabData.badgeText) {
node.setAttribute("badge", tabData.badgeText);
} else {
node.removeAttribute("badge");
}
node.ownerGlobal.requestAnimationFrame(() => {
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
if (tabData.enabled) {
node.removeAttribute("disabled");
} else {
node.setAttribute("disabled", "true");
}
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
"class", "toolbarbutton-badge");
if (badgeNode) {
let color = tabData.badgeBackgroundColor;
if (color) {
color = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`;
if (tabData.badgeText) {
node.setAttribute("badge", tabData.badgeText);
} else {
node.removeAttribute("badge");
}
badgeNode.style.backgroundColor = color || "";
}
const LEGACY_CLASS = "toolbarbutton-legacy-addon";
node.classList.remove(LEGACY_CLASS);
if (tabData.enabled) {
node.removeAttribute("disabled");
} else {
node.setAttribute("disabled", "true");
}
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
"class", "toolbarbutton-badge");
if (badgeNode) {
let color = tabData.badgeBackgroundColor;
if (color) {
color = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`;
}
badgeNode.style.backgroundColor = color || "";
}
let {style, legacy} = this.iconData.get(tabData.icon);
const LEGACY_CLASS = "toolbarbutton-legacy-addon";
if (legacy) {
node.classList.add(LEGACY_CLASS);
} else {
node.classList.remove(LEGACY_CLASS);
}
node.setAttribute("style", style);
});
}
getIconData(icons) {
let baseSize = 16;
let {icon, size} = IconDetails.getPreferredIcon(tabData.icon, this.extension, baseSize);
let {icon, size} = IconDetails.getPreferredIcon(icons, this.extension, baseSize);
let legacy = false;
// If the best available icon size is not divisible by 16, check if we have
// an 18px icon to fall back to, and trim off the padding instead.
if (size % 16 && !icon.endsWith(".svg")) {
let result = IconDetails.getPreferredIcon(tabData.icon, this.extension, 18);
let result = IconDetails.getPreferredIcon(icons, this.extension, 18);
if (result.size % 18 == 0) {
baseSize = 18;
icon = result.icon;
node.classList.add(LEGACY_CLASS);
legacy = true;
}
}
let getIcon = size => IconDetails.escapeUrl(
IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
IconDetails.getPreferredIcon(icons, this.extension, size).icon);
node.setAttribute("style", `
let style = `
--webextension-menupanel-image: url("${getIcon(32)}");
--webextension-menupanel-image-2x: url("${getIcon(64)}");
--webextension-toolbar-image: url("${IconDetails.escapeUrl(icon)}");
--webextension-toolbar-image-2x: url("${getIcon(baseSize * 2)}");
`);
`;
return {style, legacy};
}
// Update the toolbar button for a given window.

View File

@ -8,6 +8,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
Cu.import("resource://gre/modules/Task.jsm");
var {
DefaultWeakMap,
IconDetails,
} = ExtensionUtils;
@ -23,6 +24,8 @@ this.pageAction = class extends ExtensionAPI {
let {extension} = this;
let options = extension.manifest.page_action;
this.iconData = new DefaultWeakMap(icons => this.getIconData(icons));
this.id = makeWidgetId(extension.id) + "-page-action";
this.tabManager = extension.tabManager;
@ -104,31 +107,40 @@ this.pageAction = class extends ExtensionAPI {
return;
}
let button = this.getButton(window);
window.requestAnimationFrame(() => {
let button = this.getButton(window);
if (tabData.show) {
// Update the title and icon only if the button is visible.
if (tabData.show) {
// Update the title and icon only if the button is visible.
let title = tabData.title || this.extension.name;
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
let title = tabData.title || this.extension.name;
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
button.classList.add("webextension-page-action");
// These URLs should already be properly escaped, but make doubly sure CSS
// string escape characters are escaped here, since they could lead to a
// sandbox break.
let escape = str => str.replace(/[\\\s"]/g, encodeURIComponent);
let {style} = this.iconData.get(tabData.icon);
let getIcon = size => escape(IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
button.setAttribute("style", style);
}
button.setAttribute("style", `
--webextension-urlbar-image: url("${getIcon(16)}");
--webextension-urlbar-image-2x: url("${getIcon(32)}");
`);
button.hidden = !tabData.show;
});
}
button.classList.add("webextension-page-action");
}
getIconData(icons) {
// These URLs should already be properly escaped, but make doubly sure CSS
// string escape characters are escaped here, since they could lead to a
// sandbox break.
let escape = str => str.replace(/[\\\s"]/g, encodeURIComponent);
button.hidden = !tabData.show;
let getIcon = size => escape(IconDetails.getPreferredIcon(icons, this.extension, size).icon);
let style = `
--webextension-urlbar-image: url("${getIcon(16)}");
--webextension-urlbar-image-2x: url("${getIcon(32)}");
`;
return {style};
}
// Create an |image| node and add it to the |urlbar-icons|

View File

@ -31,6 +31,7 @@ support-files =
[browser_ext_browserAction_area.js]
[browser_ext_browserAction_context.js]
[browser_ext_browserAction_contextMenu.js]
[browser_ext_browserAction_disabled.js]
[browser_ext_browserAction_pageAction_icon.js]
[browser_ext_browserAction_pageAction_icon_permissions.js]
@ -73,6 +74,7 @@ skip-if = debug || asan # Bug 1354681
[browser_ext_optionsPage_browser_style.js]
[browser_ext_optionsPage_privileges.js]
[browser_ext_pageAction_context.js]
[browser_ext_pageAction_contextMenu.js]
[browser_ext_pageAction_popup.js]
[browser_ext_pageAction_popup_resize.js]
[browser_ext_pageAction_simple.js]
@ -93,6 +95,7 @@ skip-if = debug || asan # Bug 1354681
[browser_ext_sessions_restore.js]
[browser_ext_sidebarAction.js]
[browser_ext_sidebarAction_context.js]
[browser_ext_sidebarAction_contextMenu.js]
[browser_ext_sidebarAction_tabs.js]
[browser_ext_sidebarAction_windows.js]
[browser_ext_simple.js]

View File

@ -124,7 +124,9 @@ function* runTests(options) {
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
extension.onMessage("nextTest", async (expecting, testsRemaining) => {
await promiseAnimationFrame();
checkDetails(expecting);
if (testsRemaining) {

View File

@ -0,0 +1,106 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"permissions": ["contextMenus"],
"browser_action": {
"default_popup": "popup.html",
},
},
useAddonManager: "temporary",
files: {
"popup.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
</head>
<body>
<span id="text">A Test Popup</span>
<img id="testimg" src="data:image/svg+xml,<svg></svg>" height="10" width="10">
</body></html>
`,
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
},
};
let contextMenuItems = {
"context-navigation": "hidden",
"context-sep-navigation": "hidden",
"context-viewsource": "",
"context-viewinfo": "disabled",
"inspect-separator": "hidden",
"context-inspect": "hidden",
"context-bookmarkpage": "hidden",
"context-sharepage": "hidden",
};
add_task(function* browseraction_popup_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield clickBrowserAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension);
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* browseraction_popup_contextmenu_hidden_items() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield clickBrowserAction(extension);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#text");
let item, state;
for (const itemID in contextMenuItems) {
item = contentAreaContextMenu.querySelector(`#${itemID}`);
state = contextMenuItems[itemID];
if (state !== "") {
ok(item[state], `${itemID} is ${state}`);
if (state !== "hidden") {
ok(!item.hidden, `Disabled ${itemID} is not hidden`);
}
} else {
ok(!item.hidden, `${itemID} is not hidden`);
ok(!item.disabled, `${itemID} is not disabled`);
}
}
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* browseraction_popup_image_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield clickBrowserAction(extension);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#testimg");
let item = contentAreaContextMenu.querySelector("#context-viewimageinfo");
ok(!item.hidden);
ok(item.disabled);
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});

View File

@ -268,6 +268,8 @@ add_task(function* testDetailsObjects() {
extension.sendMessage("setIcon", test);
yield extension.awaitMessage("iconSet");
yield promiseAnimationFrame();
let browserActionButton = browserActionWidget.forWindow(window).node;
let pageActionImage = document.getElementById(pageActionId);

View File

@ -105,6 +105,8 @@ add_task(function* testDefaultDetails() {
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let pageActionId = makeWidgetId(extension.id) + "-page-action";
yield promiseAnimationFrame();
let browserActionButton = document.getElementById(browserActionId);
let image = getListStyleImage(browserActionButton);

View File

@ -112,6 +112,7 @@ add_task(function* testBrowserActionDisabled() {
yield extension.startup();
yield extension.awaitMessage("browserAction-disabled");
yield promiseAnimationFrame();
const {GlobalManager, Management: {global: {browserActionFor}}} = Cu.import("resource://gre/modules/Extension.jsm", {});

View File

@ -0,0 +1,116 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"permissions": ["contextMenus"],
"page_action": {
"default_popup": "popup.html",
},
},
useAddonManager: "temporary",
files: {
"popup.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
</head>
<body>
<span id="text">A Test Popup</span>
<img id="testimg" src="data:image/svg+xml,<svg></svg>" height="10" width="10">
</body></html>
`,
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
browser.tabs.query({active: true, currentWindow: true}, tabs => {
const tabId = tabs[0].id;
browser.pageAction.show(tabId).then(() => {
browser.test.sendMessage("action-shown");
});
});
},
};
let contextMenuItems = {
"context-navigation": "hidden",
"context-sep-navigation": "hidden",
"context-viewsource": "",
"context-viewinfo": "disabled",
"inspect-separator": "hidden",
"context-inspect": "hidden",
"context-bookmarkpage": "hidden",
"context-sharepage": "hidden",
};
add_task(function* pageaction_popup_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield extension.awaitMessage("action-shown");
yield clickPageAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension);
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* pageaction_popup_contextmenu_hidden_items() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield extension.awaitMessage("action-shown");
yield clickPageAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#text");
let item, state;
for (const itemID in contextMenuItems) {
item = contentAreaContextMenu.querySelector(`#${itemID}`);
state = contextMenuItems[itemID];
if (state !== "") {
ok(item[state], `${itemID} is ${state}`);
if (state !== "hidden") {
ok(!item.hidden, `Disabled ${itemID} is not hidden`);
}
} else {
ok(!item.hidden, `${itemID} is not hidden`);
ok(!item.disabled, `${itemID} is not disabled`);
}
}
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* pageaction_popup_image_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
yield extension.awaitMessage("action-shown");
yield clickPageAction(extension, window);
let contentAreaContextMenu = yield openContextMenuInPopup(extension, "#testimg");
let item = contentAreaContextMenu.querySelector("#context-viewimageinfo");
ok(!item.hidden);
ok(item.disabled);
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});

View File

@ -4,7 +4,6 @@
let extData = {
manifest: {
"permissions": ["contextMenus"],
"sidebar_action": {
"default_panel": "sidebar.html",
},
@ -31,12 +30,6 @@ let extData = {
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
browser.test.onMessage.addListener(msg => {
if (msg === "set-panel") {
browser.sidebarAction.setPanel({panel: ""}).then(() => {
@ -103,20 +96,6 @@ add_task(function* sidebar_empty_panel() {
yield extension.unload();
});
add_task(function* sidebar_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar();
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* cleanup() {
// This is set on initial sidebar install.
Services.prefs.clearUserPref("extensions.sidebar-button.shown");

View File

@ -0,0 +1,119 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"permissions": ["contextMenus"],
"sidebar_action": {
"default_panel": "sidebar.html",
},
},
useAddonManager: "temporary",
files: {
"sidebar.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
<script src="sidebar.js"></script>
</head>
<body>
<span id="text">A Test Sidebar</span>
<img id="testimg" src="data:image/svg+xml,<svg></svg>" height="10" width="10">
</body></html>
`,
"sidebar.js": function() {
window.onload = () => {
browser.test.sendMessage("sidebar");
};
},
},
background: function() {
browser.contextMenus.create({
id: "clickme-page",
title: "Click me!",
contexts: ["all"],
});
},
};
let contextMenuItems = {
"context-navigation": "hidden",
"context-sep-navigation": "hidden",
"context-viewsource": "",
"context-viewinfo": "",
"inspect-separator": "hidden",
"context-inspect": "hidden",
"context-bookmarkpage": "hidden",
"context-sharepage": "hidden",
};
add_task(function* sidebar_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar();
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for page was found");
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* sidebar_contextmenu_hidden_items() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar("#text");
let item, state;
for (const itemID in contextMenuItems) {
item = contentAreaContextMenu.querySelector(`#${itemID}`);
state = contextMenuItems[itemID];
if (state !== "") {
ok(item[state], `${itemID} is ${state}`);
if (state !== "hidden") {
ok(!item.hidden, `Disabled ${itemID} is not hidden`);
}
} else {
ok(!item.hidden, `${itemID} is not hidden`);
ok(!item.disabled, `${itemID} is not disabled`);
}
}
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* sidebar_image_contextmenu() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
let contentAreaContextMenu = yield openContextMenuInSidebar("#testimg");
let item = contentAreaContextMenu.querySelector("#context-viewimageinfo");
ok(!item.hidden);
ok(!item.disabled);
yield closeContextMenu(contentAreaContextMenu);
yield extension.unload();
});
add_task(function* cleanup() {
// This is set on initial sidebar install.
Services.prefs.clearUserPref("extensions.sidebar-button.shown");
});

View File

@ -8,7 +8,8 @@
* getBrowserActionPopup getPageActionPopup
* closeBrowserAction closePageAction
* promisePopupShown promisePopupHidden
* openContextMenu closeContextMenu openContextMenuInSidebar
* openContextMenu closeContextMenu
* openContextMenuInSidebar openContextMenuInPopup
* openExtensionContextMenu closeExtensionContextMenu
* openActionContextMenu openSubmenu closeActionContextMenu
* openTabContextMenu closeTabContextMenu
@ -17,6 +18,7 @@
* awaitExtensionPanel awaitPopupResize
* promiseContentDimensions alterContent
* promisePrefChangeObserved openContextMenuInFrame
* promiseAnimationFrame
*/
const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
@ -82,6 +84,13 @@ function getListStyleImage(button) {
return match && match[1];
}
async function promiseAnimationFrame(win = window) {
await new Promise(resolve => win.requestAnimationFrame(resolve));
let {mainThread} = Services.tm;
return new Promise(resolve => mainThread.dispatch(resolve, mainThread.DISPATCH_NORMAL));
}
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.state == "open") {
@ -216,6 +225,7 @@ var showBrowserAction = Task.async(function* (extension, win = window) {
});
var clickBrowserAction = Task.async(function* (extension, win = window) {
yield promiseAnimationFrame(win);
yield showBrowserAction(extension, win);
let widget = getBrowserActionWidget(extension).forWindow(win);
@ -232,6 +242,16 @@ function closeBrowserAction(extension, win = window) {
return Promise.resolve();
}
async function openContextMenuInPopup(extension, selector = "body") {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let browser = await awaitExtensionPanel(extension);
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "mousedown", button: 2}, browser);
await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "contextmenu"}, browser);
await popupShownPromise;
return contentAreaContextMenu;
}
async function openContextMenuInSidebar(selector = "body") {
let contentAreaContextMenu = SidebarUI.browser.contentDocument.getElementById("contentAreaContextMenu");
let browser = SidebarUI.browser.contentDocument.getElementById("webext-panels-browser");
@ -268,8 +288,8 @@ async function closeContextMenu(contextMenu) {
await popupHiddenPromise;
}
function* openExtensionContextMenu(selector = "#img1") {
let contextMenu = yield openContextMenu(selector);
async function openExtensionContextMenu(selector = "#img1") {
let contextMenu = await openContextMenu(selector);
let topLevelMenu = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
// Return null if the extension only has one item and therefore no extension menu.
@ -280,23 +300,23 @@ function* openExtensionContextMenu(selector = "#img1") {
let extensionMenu = topLevelMenu[0].childNodes[0];
let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
EventUtils.synthesizeMouseAtCenter(extensionMenu, {});
yield popupShownPromise;
await popupShownPromise;
return extensionMenu;
}
function* closeExtensionContextMenu(itemToSelect, modifiers = {}) {
async function closeExtensionContextMenu(itemToSelect, modifiers = {}) {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
EventUtils.synthesizeMouseAtCenter(itemToSelect, modifiers);
yield popupHiddenPromise;
await popupHiddenPromise;
}
function* openChromeContextMenu(menuId, target, win = window) {
async function openChromeContextMenu(menuId, target, win = window) {
const node = win.document.querySelector(target);
const menu = win.document.getElementById(menuId);
const shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
EventUtils.synthesizeMouseAtCenter(node, {type: "contextmenu"}, win);
yield shown;
await shown;
return menu;
}
@ -319,9 +339,10 @@ function closeChromeContextMenu(menuId, itemToSelect, win = window) {
return hidden;
}
function openActionContextMenu(extension, kind, win = window) {
async function openActionContextMenu(extension, kind, win = window) {
// See comment from clickPageAction below.
SetPageProxyState("valid");
await promiseAnimationFrame(win);
const id = `#${makeWidgetId(extension.id)}-${kind}-action`;
return openChromeContextMenu("toolbar-context-menu", id, win);
}
@ -343,7 +364,7 @@ function getPageActionPopup(extension, win = window) {
return win.document.getElementById(panelId);
}
function clickPageAction(extension, win = window) {
async function clickPageAction(extension, win = window) {
// This would normally be set automatically on navigation, and cleared
// when the user types a value into the URL bar, to show and hide page
// identity info and icons such as page action buttons.
@ -353,6 +374,8 @@ function clickPageAction(extension, win = window) {
/* globals SetPageProxyState */
SetPageProxyState("valid");
await promiseAnimationFrame(win);
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let elem = win.document.getElementById(pageActionId);

View File

@ -3,7 +3,7 @@
"use strict";
/* exported runTests */
/* globals getListStyleImage */
/* globals getListStyleImage, promiseAnimationFrame */
function* runTests(options) {
function background(getTests) {
@ -108,11 +108,13 @@ function* runTests(options) {
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
extension.onMessage("nextTest", async (expecting, testsRemaining) => {
if (!pageActionId) {
pageActionId = `${makeWidgetId(extension.id)}-page-action`;
}
await promiseAnimationFrame();
checkDetails(expecting);
if (testsRemaining) {

View File

@ -1,6 +1,7 @@
[DEFAULT]
support-files =
../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
../../../../../toolkit/components/extensions/test/mochitest/file_sample.html
tags = webextensions
[test_ext_all_apis.html]

View File

@ -253,6 +253,7 @@ FeedConverter.prototype = {
let aboutFeedsURI = ios.newURI("about:feeds");
chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
chromeChannel.originalURI = result.uri;
loadInfo.resultPrincipalURI = result.uri;
// carry the origin attributes from the channel that loaded the feed.
chromeChannel.owner =
@ -560,10 +561,12 @@ GenericProtocolHandler.prototype = {
const schemeId = this._getTelemetrySchemeId();
Services.telemetry.getHistogramById("FEED_PROTOCOL_USAGE").add(schemeId);
if (channel instanceof Components.interfaces.nsIHttpChannel)
if (channel instanceof Components.interfaces.nsIHttpChannel) {
// Set this so we know this is supposed to be a feed
channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
}
channel.originalURI = aUri;
aLoadInfo.resultPrincipalURI = aUri;
return channel;
},

View File

@ -138,6 +138,12 @@ var gEditItemOverlay = {
throw new Error("_initKeywordField called unexpectedly");
}
// Reset the field status synchronously now, eventually we'll reinit it
// later if we find an existing keyword. This way we can ensure to be in a
// consistent status when reusing the panel across different bookmarks.
this._keyword = newKeyword;
this._initTextField(this._keywordField, newKeyword);
if (!newKeyword) {
let entries = [];
yield PlacesUtils.keywords.fetch({ url: this._paneInfo.uri.spec },
@ -152,11 +158,12 @@ var gEditItemOverlay = {
existingKeyword = sameEntry ? sameEntry.keyword : "";
}
if (existingKeyword) {
this._keyword = newKeyword = existingKeyword;
this._keyword = existingKeyword;
// Update the text field to the existing keyword.
this._initTextField(this._keywordField, this._keyword);
}
}
}
this._initTextField(this._keywordField, newKeyword);
}),
_initLoadInSidebar: Task.async(function* () {

View File

@ -9,6 +9,7 @@ support-files = head.js
[test_bug549192.xul]
[test_bug549491.xul]
[test_bug631374_tags_selector_scroll.xul]
[test_editBookmarkOverlay_keywords.xul]
[test_editBookmarkOverlay_tags_liveUpdate.xul]
[test_selectItems_on_nested_tree.xul]
[test_treeview_date.xul]

View File

@ -0,0 +1,99 @@
<?xml version="1.0"?>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
<!DOCTYPE window [
<!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
%editBookmarkOverlayDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Bug 1343256 - Bookmark keywords disappear from one bookmark when adding a keyword to another bookmark"
onload="runTest();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="chrome://browser/content/places/editBookmarkOverlay.js"/>
<body xmlns="http://www.w3.org/1999/xhtml" />
<vbox id="editBookmarkPanelContent"/>
<script type="application/javascript">
<![CDATA[
function runTest() {
SimpleTest.waitForExplicitFinish();
Task.spawn(test.bind(this))
.catch(ex => ok(false, ex))
.then(() => PlacesUtils.bookmarks.eraseEverything())
.then(SimpleTest.finish);
}
function promiseOnItemChanged() {
return new Promise(resolve => {
PlacesUtils.bookmarks.addObserver({
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemAdded() {},
onItemRemoved() {},
onItemVisited() {},
onItemMoved() {},
onItemChanged(id, property, isAnno, value) {
PlacesUtils.bookmarks.removeObserver(this);
resolve({ property, value });
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
});
});
}
function* test() {
ok(gEditItemOverlay, "Sanity check: gEditItemOverlay is in context");
let keywordField = document.getElementById("editBMPanel_keywordField");
for (let i = 0; i < 2; ++i) {
let bm = yield PlacesUtils.bookmarks.insert({
url: `http://www.test${i}.me/`,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
});
info(`Init panel on bookmark #${i+1}`);
let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(bm);
gEditItemOverlay.initPanel({ node });
is(document.getElementById("editBMPanel_keywordField").value, "",
"The keyword field should be empty");
info("Add a keyword to the bookmark");
let promise = promiseOnItemChanged();
keywordField.focus();
keywordField.value = "kw";
synthesizeKey(i.toString(), {});
synthesizeKey("VK_RETURN", {});
keywordField.blur();
let {property, value} = yield promise;
is(property, "keyword", "The keyword should have been changed");
is(value, `kw${i}`, "The new keyword value is correct");
}
for (let i = 0; i < 2; ++i) {
let entry = yield PlacesUtils.keywords.fetch({ url: `http://www.test${i}.me/` });
is(entry.keyword, `kw${i}`, `The keyword for http://www.test${i}.me/ is correct`);
}
};
]]>
</script>
</window>

View File

@ -3,6 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from preferences.js */
/* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */
// Load DownloadUtils module for convertByteUnits
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
@ -20,7 +21,56 @@ var gAdvancedPane = {
this._inited = true;
let version = AppConstants.MOZ_APP_VERSION_DISPLAY;
// Include the build ID if this is an "a#" (nightly) build
if (/a\d+$/.test(version)) {
let buildID = Services.appinfo.appBuildID;
let year = buildID.slice(0, 4);
let month = buildID.slice(4, 6);
let day = buildID.slice(6, 8);
version += ` (${year}-${month}-${day})`;
}
// Append "(32-bit)" or "(64-bit)" build architecture to the version number:
let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
let archResource = Services.appinfo.is64Bit
? "aboutDialog.architecture.sixtyFourBit"
: "aboutDialog.architecture.thirtyTwoBit";
let arch = bundle.GetStringFromName(archResource);
version += ` (${arch})`;
document.getElementById("version").textContent = version;
// Show a release notes link if we have a URL.
let relNotesLink = document.getElementById("releasenotes");
let relNotesPrefType = Services.prefs.getPrefType("app.releaseNotesURL");
if (relNotesPrefType != Services.prefs.PREF_INVALID) {
let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
if (relNotesURL != "about:blank") {
relNotesLink.href = relNotesURL;
relNotesLink.hidden = false;
}
}
let distroId = Services.prefs.getCharPref("distribution.id", "");
if (distroId) {
let distroVersion = Services.prefs.getCharPref("distribution.version");
let distroIdField = document.getElementById("distributionId");
distroIdField.value = distroId + " - " + distroVersion;
distroIdField.hidden = false;
let distroAbout = Services.prefs.getStringPref("distribution.about", "");
if (distroAbout) {
let distroField = document.getElementById("distribution");
distroField.value = distroAbout;
distroField.hidden = false;
}
}
if (AppConstants.MOZ_UPDATER) {
gAppUpdater = new appUpdater();
let onUnload = () => {
window.removeEventListener("unload", onUnload);
Services.prefs.removeObserver("app.update.", this);

View File

@ -4,6 +4,10 @@
<!-- Advanced panel -->
#ifdef MOZ_UPDATER
<script type="application/javascript" src="chrome://browser/content/aboutDialog-appUpdater.js"/>
#endif
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/advanced.js"/>
@ -52,23 +56,16 @@
<!-- Update -->
<groupbox id="updateApp" data-category="paneAdvanced" hidden="true">
<caption><label>&updateApplication.label;</label></caption>
#ifdef MOZ_UPDATER
<description>&updateApplication.description;</description>
<hbox align="start">
<vbox flex="1">
<radiogroup id="updateRadioGroup">
<radio id="autoDesktop"
value="auto"
label="&updateAuto2.label;"
accesskey="&updateAuto2.accesskey;"/>
<radio value="checkOnly"
label="&updateCheckChoose2.label;"
accesskey="&updateCheckChoose2.accesskey;"/>
<radio value="manual"
label="&updateManual2.label;"
accesskey="&updateManual2.accesskey;"/>
</radiogroup>
<description>
&updateApplication.version.pre;<label id="version"/>&updateApplication.version.post;
<label id="releasenotes" class="learnMore text-link" hidden="true">&releaseNotes.link;</label>
</description>
<description id="distribution" class="text-blurb" hidden="true"/>
<description id="distributionId" class="text-blurb" hidden="true"/>
</vbox>
#ifdef MOZ_UPDATER
<spacer flex="1"/>
<vbox>
<button id="showUpdateHistory"
@ -77,7 +74,112 @@
accesskey="&updateHistory2.accesskey;"
preference="app.update.disable_button.showUpdateHistory"/>
</vbox>
#endif
</hbox>
#ifdef MOZ_UPDATER
<vbox id="updateBox">
<deck id="updateDeck" orient="vertical">
<hbox id="checkForUpdates" align="center">
<spacer flex="1"/>
<button id="checkForUpdatesButton"
label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
</hbox>
<hbox id="downloadAndInstall" align="center">
<spacer flex="1"/>
<button id="downloadAndInstallButton"
oncommand="gAppUpdater.startDownload();"/>
<!-- label and accesskey will be filled by JS -->
</hbox>
<hbox id="apply" align="center">
<spacer flex="1"/>
<button id="updateButton"
label="&update.updateButton.label3;"
accesskey="&update.updateButton.accesskey;"
oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
</hbox>
<hbox id="checkingForUpdates" align="center">
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="downloading" align="center">
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
</hbox>
<hbox id="applying" align="center">
<image class="update-throbber"/><label>&update.applying;</label>
</hbox>
<hbox id="downloadFailed" align="center">
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
</hbox>
<hbox id="adminDisabled" align="center">
<label>&update.adminDisabled;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="noUpdatesFound" align="center">
<label>&update.noUpdatesFound;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
</hbox>
<hbox id="otherInstanceHandlingUpdates" align="center">
<label>&update.otherInstanceHandlingUpdates;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="manualUpdate" align="center">
<label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="unsupportedSystem" align="center">
<label>&update.unsupported.start;</label><label id="unsupportedLink" class="text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
<spacer flex="1"/>
<button label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
disabled="true"/>
</hbox>
<hbox id="restarting" align="center">
<label>&update.restarting;</label>
<spacer flex="1"/>
<button label="&update.updateButton.label3;"
accesskey="&update.updateButton.accesskey;"
disabled="true"/>
</hbox>
</deck>
</vbox>
#endif
<separator/>
#ifdef MOZ_UPDATER
<description>&updateApplication.description;</description>
<radiogroup id="updateRadioGroup">
<radio id="autoDesktop"
value="auto"
label="&updateAuto2.label;"
accesskey="&updateAuto2.accesskey;"/>
<radio value="checkOnly"
label="&updateCheckChoose2.label;"
accesskey="&updateCheckChoose2.accesskey;"/>
<radio value="manual"
label="&updateManual2.label;"
accesskey="&updateManual2.accesskey;"/>
</radiogroup>
#ifdef MOZ_MAINTENANCE_SERVICE
<checkbox id="useService"
label="&useService.label;"

View File

@ -203,15 +203,16 @@ var gSearchResultsPane = {
// Building the range for highlighted areas
let rootPreferences = document.getElementById("mainPrefPane")
let rootPreferencesChildren = rootPreferences.children;
let rootPreferencesChildren = rootPreferences
.querySelectorAll(":not([data-hidden-from-search])");
// Showing all the children to bind JS, Access Keys, etc
for (let i = 0; i < rootPreferences.childElementCount; i++) {
for (let i = 0; i < rootPreferencesChildren.length; i++) {
rootPreferencesChildren[i].hidden = false;
}
// Showing or Hiding specific section depending on if words in query are found
for (let i = 0; i < rootPreferences.childElementCount; i++) {
for (let i = 0; i < rootPreferencesChildren.length; i++) {
if (rootPreferencesChildren[i].className != "header" &&
rootPreferencesChildren[i].className != "no-results-message" &&
this.searchWithinNode(rootPreferencesChildren[i], query)) {
@ -295,7 +296,7 @@ var gSearchResultsPane = {
for (let i = 0; i < nodeObject.childNodes.length; i++) {
// Search only if child node is not hidden
if (!nodeObject.childNodes[i].hidden) {
if (!nodeObject.childNodes[i].hidden && nodeObject.getAttribute("data-hidden-from-search") !== "true") {
let result = this.searchWithinNode(nodeObject.childNodes[i], searchPhrase);
matchesFound = matchesFound || result;
}

View File

@ -37,6 +37,8 @@
"chrome://browser/locale/preferences/applications.dtd">
<!ENTITY % advancedDTD SYSTEM
"chrome://browser/locale/preferences/advanced.dtd">
<!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
%aboutDialogDTD;
%brandDTD;
%globalPreferencesDTD;
%preferencesDTD;

View File

@ -110,14 +110,19 @@ add_task(function*() {
});
add_task(function*() {
mockUpdateManager.register();
yield openPreferencesViaOpenPreferencesAPI("advanced", { leaveOpen: true });
let doc = gBrowser.selectedBrowser.contentDocument;
let showBtn = doc.getElementById("showUpdateHistory");
let dialogOverlay = doc.getElementById("dialogOverlay");
// XXX: For unknown reasons, this mock cannot be loaded by
// XPCOMUtils.defineLazyServiceGetter() called in aboutDialog-appUpdater.js.
// It is registered here so that we could assert update history subdialog
// without stopping the preferences advanced pane from loading.
// See bug 1361929.
mockUpdateManager.register();
// Test the dialog window opens
is(dialogOverlay.style.visibility, "", "The dialog should be invisible");
showBtn.doCommand();

View File

@ -171,3 +171,35 @@ add_task(function*() {
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
/**
* Test for "Site Data" case, verifying elements with data-hidden-from-search = true
* are hidden in search result.
*/
add_task(function*() {
yield SpecialPowers.pushPrefEnv({"set": [["browser.storageManager.enabled", false]]});
yield openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
let generalPane = gBrowser.contentDocument.getElementById("header-general");
is_element_hidden(generalPane, "Should not be in general");
// Performs search
let searchInput = gBrowser.contentDocument.getElementById("searchInput");
searchInput.doCommand()
searchInput.value = "site data";
searchInput.doCommand()
let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
let child = mainPrefTag.querySelector("#siteDataGroup");
is_element_hidden(child, "Should be hidden in search results");
// Takes search off
searchInput.value = "";
searchInput.doCommand()
// Checks if back to normal
is_element_visible(generalPane, "Should be in generalPane");
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -32,7 +32,7 @@
class="showNormal"
label="&privatebrowsingpage.openPrivateWindow.label;"
accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"/>
<div class="showPrivate about-content-container">
<div class="showPrivate container">
<h1 class="title">
<span id="title">&privateBrowsing.title;</span>
<span id="titleTracking">&privateBrowsing.title.tracking;</span>

View File

@ -12,3 +12,4 @@ support-files =
[browser_roundedWindow_windowSetting_max.js]
[browser_roundedWindow_windowSetting_mid.js]
[browser_roundedWindow_windowSetting_min.js]
[browser_timezone.js]

View File

@ -0,0 +1,38 @@
/**
* Bug 1330890 - A test case for verifying Date() object of javascript will use
* UTC timezone after fingerprinting resistance is enabled.
*/
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
});
add_task(function* test_timezone() {
// Load a page and verify the timezone.
let tab = yield BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
yield ContentTask.spawn(tab.linkedBrowser, null,
function* () {
let dateObj = new Date();
let dateString = dateObj.toString();
ok(dateString.endsWith("(UTC)"), "The date string is in UTC timezone.");
is(dateObj.getFullYear(), dateObj.getUTCFullYear(),
"The full year reports in UTC timezone.");
is(dateObj.getMonth(), dateObj.getUTCMonth(), "The month reports in UTC timezone.");
is(dateObj.getDate(), dateObj.getUTCDate(), "The month reports in UTC timezone.");
is(dateObj.getDay(), dateObj.getUTCDay(), "The day reports in UTC timezone.");
is(dateObj.getHours(), dateObj.getUTCHours(), "The hours reports in UTC timezone.");
is(dateObj.getTimezoneOffset(), 0, "The difference with UTC timezone is 0.");
}
);
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -15,6 +15,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/Console.jsm");
// A bound to the size of data to store for DOM Storage.
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
// Returns the principal for a given |frame| contained in a given |docShell|.
function getPrincipalForFrame(docShell, frame) {
let ssm = Services.scriptSecurityManager;
@ -179,14 +182,25 @@ var SessionStorageInternal = {
storage = null;
}
if (storage && storage.length) {
for (let i = 0; i < storage.length; i++) {
try {
let key = storage.key(i);
hostData[key] = storage.getItem(key);
} catch (e) {
// This currently throws for secured items (cf. bug 442048).
}
if (!storage || !storage.length) {
return hostData;
}
// If the DOMSessionStorage contains too much data, ignore it.
let usage = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.getStorageUsage(storage);
Services.telemetry.getHistogramById("FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS").add(usage);
if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
return hostData;
}
for (let i = 0; i < storage.length; i++) {
try {
let key = storage.key(i);
hostData[key] = storage.getItem(key);
} catch (e) {
// This currently throws for secured items (cf. bug 442048).
}
}

View File

@ -3228,9 +3228,6 @@ var SessionStoreInternal = {
let overwriteTabs = aOptions && aOptions.overwriteTabs;
let isFollowUp = aOptions && aOptions.isFollowUp;
let firstWindow = aOptions && aOptions.firstWindow;
// See SessionStoreInternal.restoreTabs for a description of what
// selectTab represents.
let selectTab = (overwriteTabs ? parseInt(winData.selected || 1, 10) : 0);
if (isFollowUp) {
this.windowToFocus = aWindow;
@ -3255,22 +3252,25 @@ var SessionStoreInternal = {
winData.tabs = [];
}
var tabbrowser = aWindow.gBrowser;
var openTabCount = overwriteTabs ? tabbrowser.browsers.length : -1;
var newTabCount = winData.tabs.length;
// See SessionStoreInternal.restoreTabs for a description of what
// selectTab represents.
let selectTab = 0;
if (overwriteTabs) {
selectTab = parseInt(winData.selected || 1, 10);
selectTab = Math.max(selectTab, 1);
selectTab = Math.min(selectTab, winData.tabs.length);
}
let tabbrowser = aWindow.gBrowser;
let tabsToRemove = overwriteTabs ? tabbrowser.browsers.length : 0;
let newTabCount = winData.tabs.length;
var tabs = [];
// disable smooth scrolling while adding, moving, removing and selecting tabs
var tabstrip = tabbrowser.tabContainer.mTabstrip;
var smoothScroll = tabstrip.smoothScroll;
let tabstrip = tabbrowser.tabContainer.mTabstrip;
let smoothScroll = tabstrip.smoothScroll;
tabstrip.smoothScroll = false;
// unpin all tabs to ensure they are not reordered in the next loop
if (overwriteTabs) {
for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--)
tabbrowser.unpinTab(tabbrowser.tabs[t]);
}
// We need to keep track of the initially open tabs so that they
// can be moved to the end of the restored tabs.
let initialTabs = [];
@ -3278,36 +3278,31 @@ var SessionStoreInternal = {
initialTabs = Array.slice(tabbrowser.tabs);
}
// make sure that the selected tab won't be closed in order to
// prevent unnecessary flickering
if (overwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
let numVisibleTabs = 0;
let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") &&
this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
for (var t = 0; t < newTabCount; t++) {
// When trying to restore into existing tab, we also take the userContextId
// into account if present.
let userContextId = winData.tabs[t].userContextId;
let createLazyBrowser = restoreTabsLazily && !winData.tabs[t].pinned;
let reuseExisting = t < openTabCount &&
(tabbrowser.tabs[t].getAttribute("usercontextid") == (userContextId || ""));
let tab = reuseExisting ? this._maybeUpdateBrowserRemoteness(tabbrowser.tabs[t])
: tabbrowser.addTab("about:blank",
{ createLazyBrowser,
skipAnimation: true,
userContextId,
skipBackgroundNotify: true });
let select = t == selectTab - 1;
let createLazyBrowser = restoreTabsLazily && !select && !winData.tabs[t].pinned;
let tab = tabbrowser.addTab("about:blank",
{ createLazyBrowser,
skipAnimation: true,
userContextId,
skipBackgroundNotify: true });
// If we inserted a new tab because the userContextId didn't match with the
// open tab, even though `t < openTabCount`, we need to remove that open tab
// and put the newly added tab in its place.
if (!reuseExisting && t < openTabCount) {
tabbrowser.removeTab(tabbrowser.tabs[t]);
tabbrowser.moveTabTo(tab, t);
if (select) {
// Select a new tab first to prevent the removeTab loop from changing
// the selected tab over and over again.
tabbrowser.selectedTab = tab;
// Remove superfluous tabs.
for (let i = 0; i < tabsToRemove; i++) {
tabbrowser.removeTab(tabbrowser.tabs[0]);
}
tabsToRemove = 0;
}
tabs.push(tab);
@ -3318,47 +3313,6 @@ var SessionStoreInternal = {
tabbrowser.showTab(tabs[t]);
numVisibleTabs++;
}
if (!!winData.tabs[t].muted != tabs[t].linkedBrowser.audioMuted) {
tabs[t].toggleMuteAudio(winData.tabs[t].muteReason);
}
}
if (selectTab > 0 && selectTab <= tabs.length) {
// The state we're restoring wants to select a particular tab. This
// implies that we're overwriting tabs.
let currentIndex = tabbrowser.tabContainer.selectedIndex;
let targetIndex = selectTab - 1;
if (currentIndex != targetIndex) {
// We need to change the selected tab. There are two ways of doing this:
//
// 1) The fast path: swap the currently selected tab with the one in the
// position of the selected tab in the restored state. Note that this
// can only work if the user contexts between the two tabs being swapped
// match. This should be the common case.
//
// 2) The slow path: switch to the selected tab.
//
// We'll try to do (1), and then fallback to (2).
let selectedTab = tabbrowser.selectedTab;
let tabAtTargetIndex = tabs[targetIndex];
let userContextsMatch = selectedTab.userContextId == tabAtTargetIndex.userContextId;
if (userContextsMatch) {
tabbrowser.moveTabTo(selectedTab, targetIndex);
tabbrowser.moveTabTo(tabAtTargetIndex, currentIndex);
// We also have to do a similar "move" in the aTabs Array to
// make sure that the restored content shows up in the right
// order.
tabs[targetIndex] = tabs[currentIndex];
tabs[currentIndex] = tabAtTargetIndex;
} else {
// Otherwise, go the slow path, and switch to the target tab.
tabbrowser.selectedTab = tabs[targetIndex];
}
}
}
for (let i = 0; i < newTabCount; ++i) {
@ -3376,6 +3330,7 @@ var SessionStoreInternal = {
// Move the originally open tabs to the end
let endPosition = tabbrowser.tabs.length - 1;
for (let i = 0; i < initialTabs.length; i++) {
tabbrowser.unpinTab(initialTabs[i]);
tabbrowser.moveTabTo(initialTabs[i], endPosition);
}
}
@ -3386,18 +3341,6 @@ var SessionStoreInternal = {
tabbrowser.showTab(tabs[0]);
}
// If overwriting tabs, we want to reset each tab's "restoring" state. Since
// we're overwriting those tabs, they should no longer be restoring. The
// tabs will be rebuilt and marked if they need to be restored after loading
// state (in restoreTabs).
if (overwriteTabs) {
for (let i = 0; i < tabbrowser.tabs.length; i++) {
let tab = tabbrowser.tabs[i];
if (tabbrowser.browsers[i].__SS_restoreState)
this._resetTabRestoringState(tab);
}
}
// We want to correlate the window with data from the last session, so
// assign another id if we have one. Otherwise clear so we don't do
// anything with it.
@ -3405,12 +3348,6 @@ var SessionStoreInternal = {
if (winData.__lastSessionWindowID)
aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
// when overwriting tabs, remove all superflous ones
if (overwriteTabs && newTabCount < openTabCount) {
Array.slice(tabbrowser.tabs, newTabCount, openTabCount)
.forEach(tabbrowser.removeTab, tabbrowser);
}
if (overwriteTabs) {
this.restoreWindowFeatures(aWindow, winData);
delete this._windows[aWindow.__SSi].extData;
@ -3829,7 +3766,6 @@ var SessionStoreInternal = {
loadArguments: aLoadArguments,
isRemotenessUpdate,
});
}
// If the restored browser wants to show view source content, start up a
@ -4077,27 +4013,6 @@ var SessionStoreInternal = {
}, 0);
},
/**
* Determines whether or not a tab that is being restored needs
* to have its remoteness flipped first.
*
* @param tab (<xul:tab>):
* The tab being restored.
*
* @returns tab (<xul:tab>)
* The tab that was passed.
*/
_maybeUpdateBrowserRemoteness(tab) {
let win = tab.ownerGlobal;
let tabbrowser = win.gBrowser;
let browser = tab.linkedBrowser;
if (win.gMultiProcessBrowser && !browser.isRemoteBrowser) {
tabbrowser.updateBrowserRemoteness(browser, true);
}
return tab;
},
/**
* Update the session start time and send a telemetry measurement
* for the number of days elapsed since the session was started.

View File

@ -49,7 +49,7 @@ XPCOMUtils.defineLazyGetter(this, "gContentRestore",
var gCurrentEpoch = 0;
// A bound to the size of data to store for DOM Storage.
const DOM_STORAGE_MAX_CHARS = 10000000; // 10M characters
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
// This pref controls whether or not we send updates to the parent on a timeout
// or not, and should only be used for tests or debugging.
@ -580,37 +580,6 @@ var SessionStorageListener = {
setTimeout(() => this.collect(), 0);
},
// Before DOM Storage can be written to disk, it needs to be serialized
// for sending across frames/processes, then again to be sent across
// threads, then again to be put in a buffer for the disk. Each of these
// serializations is an opportunity to OOM and (depending on the site of
// the OOM), either crash, lose all data for the frame or lose all data
// for the application.
//
// In order to avoid this, compute an estimate of the size of the
// object, and block SessionStorage items that are too large. As
// we also don't want to cause an OOM here, we use a quick and memory-
// efficient approximation: we compute the total sum of string lengths
// involved in this object.
estimateStorageSize(collected) {
if (!collected) {
return 0;
}
let size = 0;
for (let host of Object.keys(collected)) {
size += host.length;
let perHost = collected[host];
for (let key of Object.keys(perHost)) {
size += key.length;
let perKey = perHost[key];
size += perKey.length;
}
}
return size;
},
// We don't want to send all the session storage data for all the frames
// for every change. So if only a few value changed we send them over as
// a "storagechange" event. If however for some reason before we send these
@ -623,55 +592,56 @@ var SessionStorageListener = {
},
collectFromEvent(event) {
// TODO: we should take browser.sessionstore.dom_storage_limit into an account here.
if (docShell) {
let {url, key, newValue} = event;
let uri = Services.io.newURI(url);
let domain = uri.prePath;
if (!this._changes) {
this._changes = {};
}
if (!this._changes[domain]) {
this._changes[domain] = {};
}
this._changes[domain][key] = newValue;
MessageQueue.push("storagechange", () => {
let tmp = this._changes;
// If there were multiple changes we send them merged.
// First one will collect all the changes the rest of
// these messages will be ignored.
this.resetChanges();
return tmp;
});
if (!docShell) {
return;
}
// How much data does DOMSessionStorage contain?
let usage = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.getStorageUsage(event.storageArea);
Services.telemetry.getHistogramById("FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS").add(usage);
// Don't store any data if we exceed the limit. Wipe any data we previously
// collected so that we don't confuse websites with partial state.
if (usage > Preferences.get(DOM_STORAGE_LIMIT_PREF)) {
MessageQueue.push("storage", () => null);
return;
}
let {url, key, newValue} = event;
let uri = Services.io.newURI(url);
let domain = uri.prePath;
if (!this._changes) {
this._changes = {};
}
if (!this._changes[domain]) {
this._changes[domain] = {};
}
this._changes[domain][key] = newValue;
MessageQueue.push("storagechange", () => {
let tmp = this._changes;
// If there were multiple changes we send them merged.
// First one will collect all the changes the rest of
// these messages will be ignored.
this.resetChanges();
return tmp;
});
},
collect() {
if (docShell) {
// We need the entire session storage, let's reset the pending individual change
// messages.
this.resetChanges();
MessageQueue.push("storage", () => {
let collected = SessionStorage.collect(docShell, gFrameTree);
if (collected == null) {
return collected;
}
let size = this.estimateStorageSize(collected);
Services.telemetry.getHistogramById("FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS").add(size);
if (size > Preferences.get("browser.sessionstore.dom_storage_limit", DOM_STORAGE_MAX_CHARS)) {
// Rather than keeping the old storage, which wouldn't match the rest
// of the state of the page, empty the storage. DOM storage will be
// recollected the next time and stored if it is now small enough.
return {};
}
return collected;
});
if (!docShell) {
return;
}
// We need the entire session storage, let's reset the pending individual change
// messages.
this.resetChanges();
MessageQueue.push("storage", () => {
return SessionStorage.collect(docShell, gFrameTree);
});
},
onFrameTreeCollected() {

View File

@ -52,5 +52,5 @@ add_task(function*() {
// clean up
Services.cookies.removeAll();
yield promiseRemoveTab(tab);
yield promiseRemoveTab(gBrowser.tabs[1]);
});

View File

@ -22,7 +22,7 @@ function test() {
is(gBrowser.visibleTabs.length, 1, "only 1 after hiding");
ok(hiddenTab.hidden, "sanity check that it's hidden");
let extraTab = gBrowser.addTab();
gBrowser.addTab();
let state = ss.getBrowserState();
let stateObj = JSON.parse(state);
let tabs = stateObj.windows[0].tabs;
@ -35,16 +35,16 @@ function test() {
tabs[2].hidden = true;
observeOneRestore(function() {
let testWindow = Services.wm.getEnumerator("navigator:browser").getNext();
is(testWindow.gBrowser.visibleTabs.length, 1, "only restored 1 visible tab");
let restoredTabs = testWindow.gBrowser.tabs;
is(gBrowser.visibleTabs.length, 1, "only restored 1 visible tab");
let restoredTabs = gBrowser.tabs;
ok(!restoredTabs[0].hidden, "first is still visible");
ok(restoredTabs[1].hidden, "second tab is still hidden");
ok(restoredTabs[2].hidden, "third tab is now hidden");
// Restore the original state and clean up now that we're done
gBrowser.removeTab(hiddenTab);
gBrowser.removeTab(extraTab);
gBrowser.removeTab(gBrowser.tabs[1]);
gBrowser.removeTab(gBrowser.tabs[1]);
finish();
});

View File

@ -56,13 +56,16 @@ function newWindowWithState(state, callback) {
let win = window.openDialog(getBrowserURL(), "_blank", opts);
win.addEventListener("load", function() {
let tab = win.gBrowser.selectedTab;
// The form data will be restored before SSTabRestored, so we want to listen
// for that on the currently selected tab (it will be reused)
tab.addEventListener("SSTabRestored", function() {
callback(win);
}, {capture: true, once: true});
// for that on the currently selected tab
let onSSTabRestored = event => {
let tab = event.target;
if (tab.selected) {
win.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true);
callback(win);
}
};
win.gBrowser.tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true);
executeSoon(function() {
ss.setWindowState(win, JSON.stringify(state), true);

View File

@ -21,7 +21,6 @@ add_task(function* () {
assertNumberOfTabs(2, "there are two tabs, now");
let [tab1, tab2] = gBrowser.tabs;
let linkedBrowser = tab1.linkedBrowser;
gBrowser.pinTab(tab1);
gBrowser.pinTab(tab2);
assertNumberOfPinnedTabs(2, "both tabs are now pinned");
@ -31,5 +30,4 @@ add_task(function* () {
assertNumberOfTabs(1, "one tab left after setBrowserState()");
assertNumberOfPinnedTabs(0, "there are no pinned tabs");
is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used");
});

View File

@ -26,6 +26,7 @@ let TestAboutPage = {
let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
aLoadInfo);
channel.originalURI = aURI;
aLoadInfo.resultPrincipalURI = aURI;
return channel;
},

View File

@ -86,14 +86,6 @@ const PINNED_STATE = {
* restored window. Leave this at 0 if you don't want to change
* the selection from the initial window state.
*
* expectedFlips:
* an Array that represents the window that we end up with after
* restoring state. Each bool in the Array represents the window tabs,
* in order. A "true" indicates that the tab should have flipped
* its remoteness once. "false" indicates that the tab should never
* have flipped remoteness. Note that any tab that flips its remoteness
* more than once will cause a test failure.
*
* expectedRemoteness:
* an Array that represents the window that we end up with after
* restoring state. Each bool in the Array represents the window
@ -106,10 +98,6 @@ const PINNED_STATE = {
function* runScenarios(scenarios) {
for (let [scenarioIndex, scenario] of scenarios.entries()) {
info("Running scenario " + scenarioIndex);
// Let's make sure our scenario is sane first.
Assert.equal(scenario.expectedFlips.length,
scenario.expectedRemoteness.length,
"All expected flips and remoteness needs to be supplied");
Assert.ok(scenario.initialSelectedTab > 0,
"You must define an initially selected tab");
@ -142,62 +130,15 @@ function* runScenarios(scenarios) {
yield BrowserTestUtils.switchTab(tabbrowser, tabToSelect);
}
// Hook up an event listener to make sure that the right
// tabs flip remoteness, and only once.
let flipListener = {
seenBeforeTabs: new Set(),
seenAfterTabs: new Set(),
handleEvent(e) {
let index = Array.from(tabbrowser.tabs).indexOf(e.target);
switch (e.type) {
case "BeforeTabRemotenessChange":
info(`Saw tab at index ${index} before remoteness flip`);
if (this.seenBeforeTabs.has(e.target)) {
Assert.ok(false, "Saw tab before remoteness flip more than once");
}
this.seenBeforeTabs.add(e.target);
break;
case "TabRemotenessChange":
info(`Saw tab at index ${index} after remoteness flip`);
if (this.seenAfterTabs.has(e.target)) {
Assert.ok(false, "Saw tab after remoteness flip more than once");
}
this.seenAfterTabs.add(e.target);
break;
}
},
};
win.addEventListener("BeforeTabRemotenessChange", flipListener);
win.addEventListener("TabRemotenessChange", flipListener);
// Okay, time to test!
let state = prepareState(scenario.stateToRestore,
scenario.selectedTab);
SessionStore.setWindowState(win, state, true);
win.removeEventListener("BeforeTabRemotenessChange", flipListener);
win.removeEventListener("TabRemotenessChange", flipListener);
// Because we know that scenario.expectedFlips and
// scenario.expectedRemoteness have the same length, we
// can check that we satisfied both with the same loop.
for (let i = 0; i < scenario.expectedFlips.length; ++i) {
let expectedToFlip = scenario.expectedFlips[i];
for (let i = 0; i < scenario.expectedRemoteness.length; ++i) {
let expectedRemoteness = scenario.expectedRemoteness[i];
let tab = tabbrowser.tabs[i];
if (expectedToFlip) {
Assert.ok(flipListener.seenBeforeTabs.has(tab),
`We should have seen tab at index ${i} before remoteness flip`);
Assert.ok(flipListener.seenAfterTabs.has(tab),
`We should have seen tab at index ${i} after remoteness flip`);
} else {
Assert.ok(!flipListener.seenBeforeTabs.has(tab),
`We should not have seen tab at index ${i} before remoteness flip`);
Assert.ok(!flipListener.seenAfterTabs.has(tab),
`We should not have seen tab at index ${i} after remoteness flip`);
}
Assert.equal(tab.linkedBrowser.isRemoteBrowser, expectedRemoteness,
"Should have gotten the expected remoteness " +
@ -210,8 +151,7 @@ function* runScenarios(scenarios) {
/**
* Tests that if we restore state to browser windows with
* a variety of initial remoteness states, that we only flip
* the remoteness on the necessary tabs. For this particular
* a variety of initial remoteness states. For this particular
* set of tests, we assume that tabs are restoring on demand.
*/
add_task(function*() {
@ -233,11 +173,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: SIMPLE_STATE,
selectedTab: 3,
// The initial tab is remote and should go into
// the background state. The second and third tabs
// are new and should initialize remotely as well.
// There should therefore be no remoteness flips.
expectedFlips: [false, false, false],
// All tabs should now be remote.
expectedRemoteness: [true, true, true],
},
@ -249,10 +184,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: SIMPLE_STATE,
selectedTab: 1,
// The initial tab is remote and selected, so it should
// not flip remoteness. The other two new tabs should
// initialize as remote unrestored background tabs.
expectedFlips: [false, false, false],
// All tabs should now be remote.
expectedRemoteness: [true, true, true],
},
@ -265,10 +196,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: SIMPLE_STATE,
selectedTab: 0,
// The initial tab is remote and selected, so it should
// not flip remoteness. The other two new tabs should
// initialize as remote unrestored background tabs.
expectedFlips: [false, false, false],
// All tabs should now be remote.
expectedRemoteness: [true, true, true],
},
@ -281,12 +208,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: PINNED_STATE,
selectedTab: 3,
// The initial tab is pinned and will load right away,
// so it should stay remote. The second tab is new
// and pinned, so it should start remote and not flip.
// The third tab is not pinned, but it is selected,
// so it will start remote.
expectedFlips: [false, false, false],
// Both pinned tabs and the selected tabs should all
// end up being remote.
expectedRemoteness: [true, true, true],
@ -298,14 +219,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: SIMPLE_STATE,
selectedTab: 2,
// The initial tab is non-remote and will flip. The two tabs that
// are added after should be initialized as remote. Since the selected
// tab is changing, SessionStore should swap the initial tab with the
// tab thats in the selectedTab slot. That'll result in a remoteness
// state like this:
//
// [true, false, true]
expectedFlips: [false, true, false],
// All tabs should now be remote.
expectedRemoteness: [true, true, true],
},
@ -316,16 +229,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: SIMPLE_STATE,
selectedTab: 3,
// The initial tab is remote and should stay that way, even
// when put into the background. The initial tab is going to be
// swapped into the selectedTab slot, and both of those tabs are
// remote already. That will result in the tabs remoteness being
// in this order still:
//
// [true, false, true]
//
// This means that we'll only need to flip the second tab.
expectedFlips: [false, true, false],
// All tabs should now be remote.
expectedRemoteness: [true, true, true],
},
@ -338,18 +241,6 @@ add_task(function*() {
initialSelectedTab: 1,
stateToRestore: PINNED_STATE,
selectedTab: 3,
// The initial tab is going to swap into the selected slot,
// and so after the other two tabs from the PINNED_STATE are
// inserted, we'll have a remoteness state like this:
//
// [true, true, false]
//
// The two pinned tabs at the start of the PINNED_STATE should
// load right away, but should not flip remoteness.
//
// The third tab is selected, and should load right away, so
// it should flip remoteness.
expectedFlips: [false, false, true],
// All tabs should now be remote.
expectedRemoteness: [true, true, true],
},

View File

@ -27,7 +27,7 @@ add_task(function* test_telemetry() {
// There is no good way to make sure that the parent received the histogram entries from the child processes.
// Let's stick to the ugly, spinning the event loop until we have a good approach (Bug 1357509).
yield BrowserTestUtils.waitForCondition(() => {
return histogram.snapshot().counts[5] > snap1.counts[5];
return histogram.snapshot().counts[4] > snap1.counts[4];
});
Assert.ok(true);

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
# Add-on signing is not required for DevEdition

View File

@ -7,6 +7,7 @@ ac_add_options --disable-jemalloc
ac_add_options --disable-crashreporter
ac_add_options --disable-elf-hack
ac_add_options --enable-debug
ac_add_options --disable-sandbox
MOZ_CODE_COVERAGE=1
export CFLAGS="--coverage"

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
# Add-on signing is not required for DevEdition

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
# Add-on signing is not required for DevEdition

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/win32/common-opt"

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
. "$topsrcdir/browser/config/mozconfigs/win64/common-opt"

View File

@ -18,6 +18,7 @@ if test "$OS_ARCH" = "WINNT"; then
if ! test "$HAVE_64BIT_BUILD"; then
if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \
"$MOZ_UPDATE_CHANNEL" = "aurora" -o \
"$MOZ_UPDATE_CHANNEL" = "aurora-dev" -o \
"$MOZ_UPDATE_CHANNEL" = "beta" -o \
"$MOZ_UPDATE_CHANNEL" = "beta-dev" -o \
"$MOZ_UPDATE_CHANNEL" = "release" -o \
@ -47,12 +48,17 @@ MOZ_APP_VERSION_DISPLAY=$FIREFOX_VERSION_DISPLAY
MOZ_BRANDING_DIRECTORY=browser/branding/unofficial
MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
# This should usually be the same as the value MAR_CHANNEL_ID.
# ACCEPTED_MAR_CHANNEL_IDS should usually be the same as the value MAR_CHANNEL_ID.
# If more than one ID is needed, then you should use a comma separated list
# of values.
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
MAR_CHANNEL_ID=firefox-mozilla-central
if test "$MOZ_UPDATE_CHANNEL" = "aurora"; then
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-aurora
MAR_CHANNEL_ID=firefox-mozilla-aurora
else
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
MAR_CHANNEL_ID=firefox-mozilla-central
fi
MOZ_PROFILE_MIGRATOR=1
MOZ_JSDOWNLOADS=1

View File

@ -94,11 +94,17 @@ FormAutofillParent.prototype = {
log.debug("observe:", topic, "with data:", data);
switch (topic) {
case "advanced-pane-loaded": {
let formAutofillPreferences = new FormAutofillPreferences();
let useOldOrganization = Services.prefs.getBoolPref("browser.preferences.useOldOrganization",
false);
let formAutofillPreferences = new FormAutofillPreferences({useOldOrganization});
let document = subject.document;
let prefGroup = formAutofillPreferences.init(document);
let parentNode = document.getElementById("mainPrefPane");
let insertBeforeNode = document.getElementById("locationBarGroup");
let parentNode = useOldOrganization ?
document.getElementById("mainPrefPane") :
document.getElementById("passwordsGroup");
let insertBeforeNode = useOldOrganization ?
document.getElementById("locationBarGroup") :
document.getElementById("passwordGrid");
parentNode.insertBefore(prefGroup, insertBeforeNode);
break;
}

View File

@ -14,6 +14,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
const MANAGE_PROFILES_URL = "chrome://formautofill/content/manageProfiles.xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -22,7 +23,8 @@ Cu.import("resource://formautofill/FormAutofillUtils.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
function FormAutofillPreferences() {
function FormAutofillPreferences({useOldOrganization}) {
this.useOldOrganization = useOldOrganization;
this.bundle = Services.strings.createBundle(BUNDLE_URI);
}
@ -36,15 +38,6 @@ FormAutofillPreferences.prototype = {
return Services.prefs.getBoolPref(PREF_AUTOFILL_ENABLED);
},
/**
* Check if the current page is Preferences/Privacy.
*
* @returns {boolean}
*/
get isPrivacyPane() {
return this.refs.document.location.href == "about:preferences#privacy";
},
/**
* Create the Form Autofill preference group.
*
@ -72,45 +65,50 @@ FormAutofillPreferences.prototype = {
* @param {XULDocument} document
*/
createPreferenceGroup(document) {
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
let caption = document.createElementNS(XUL_NS, "caption");
let captionLabel = document.createElementNS(XUL_NS, "label");
let hbox = document.createElementNS(XUL_NS, "hbox");
let enabledCheckbox = document.createElementNS(XUL_NS, "checkbox");
let formAutofillGroup;
let profileAutofill = document.createElementNS(XUL_NS, "hbox");
let profileAutofillCheckbox = document.createElementNS(XUL_NS, "checkbox");
let spacer = document.createElementNS(XUL_NS, "spacer");
let savedProfilesBtn = document.createElementNS(XUL_NS, "button");
if (this.useOldOrganization) {
let caption = document.createElementNS(XUL_NS, "caption");
let captionLabel = document.createElementNS(XUL_NS, "label");
formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
formAutofillGroup.hidden = document.location.href != "about:preferences#privacy";
// Use .setAttribute because HTMLElement.dataset is not available on XUL elements
formAutofillGroup.setAttribute("data-category", "panePrivacy");
formAutofillGroup.appendChild(caption);
caption.appendChild(captionLabel);
captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
} else {
formAutofillGroup = document.createElementNS(XUL_NS, "vbox");
savedProfilesBtn.className = "accessory-button";
}
this.refs = {
document,
formAutofillGroup,
enabledCheckbox,
profileAutofillCheckbox,
savedProfilesBtn,
};
formAutofillGroup.id = "formAutofillGroup";
formAutofillGroup.hidden = !this.isPrivacyPane;
// Use .setAttribute because HTMLElement.dataset is not available on XUL elements
formAutofillGroup.setAttribute("data-category", "panePrivacy");
captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
profileAutofill.id = "profileAutofill";
savedProfilesBtn.setAttribute("label", this.bundle.GetStringFromName("savedProfiles"));
enabledCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableProfileAutofill"));
profileAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableProfileAutofill"));
// Manually set the checked state
if (this.isAutofillEnabled) {
enabledCheckbox.setAttribute("checked", true);
profileAutofillCheckbox.setAttribute("checked", true);
}
spacer.flex = 1;
formAutofillGroup.appendChild(caption);
caption.appendChild(captionLabel);
formAutofillGroup.appendChild(hbox);
hbox.appendChild(enabledCheckbox);
hbox.appendChild(spacer);
hbox.appendChild(savedProfilesBtn);
formAutofillGroup.appendChild(profileAutofill);
profileAutofill.appendChild(profileAutofillCheckbox);
profileAutofill.appendChild(spacer);
profileAutofill.appendChild(savedProfilesBtn);
},
/**
@ -123,7 +121,7 @@ FormAutofillPreferences.prototype = {
case "command": {
let target = event.target;
if (target == this.refs.enabledCheckbox) {
if (target == this.refs.profileAutofillCheckbox) {
// Set preference directly instead of relying on <Preference>
Services.prefs.setBoolPref(PREF_AUTOFILL_ENABLED, target.checked);
} else if (target == this.refs.savedProfilesBtn) {

View File

@ -16,17 +16,17 @@
</head>
<body>
<form>
<label id="first-name-container">
<label id="given-name-container">
<span>First Name</span>
<input id="first-name" type="text"/>
<input id="given-name" type="text"/>
</label>
<label id="middle-name-container">
<label id="additional-name-container">
<span>Middle Name</span>
<input id="middle-name" type="text"/>
<input id="additional-name" type="text"/>
</label>
<label id="last-name-container">
<label id="family-name-container">
<span>Last Name</span>
<input id="last-name" type="text"/>
<input id="family-name" type="text"/>
</label>
<label id="organization-container">
<span>Company</span>

View File

@ -139,6 +139,7 @@ ManageProfileDialog.prototype = {
// as option text. Possibly improve the algorithm in
// ProfileAutoCompleteResult.jsm and reuse it here.
const fieldOrder = [
"name",
"street-address", // Street address
"address-level2", // City/Town
"organization", // Company or organization name
@ -169,7 +170,7 @@ ManageProfileDialog.prototype = {
*/
openEditDialog(profile) {
window.openDialog(EDIT_PROFILE_URL, null,
"chrome,centerscreen,modal,width=600,height=370",
"chrome,centerscreen,modal,width=600,height=450",
profile);
},

View File

@ -52,15 +52,15 @@ button {
width: 15em;
}
#first-name-container,
#middle-name-container,
#given-name-container,
#additional-name-container,
#address-level1-container,
#postal-code-container,
#country-container {
flex: 0 1 50%;
}
#last-name-container,
#family-name-container,
#organization-container,
#street-address-container,
#address-level2-container,
@ -74,7 +74,7 @@ button {
justify-content: end;
}
#last-name,
#family-name,
#organization,
#address-level2,
#tel{
@ -86,10 +86,3 @@ button {
#email {
flex: 1 0 auto;
}
#first-name-container,
#middle-name-container,
#last-name-container {
/* Hide until we support names */
display: none;
}

View File

@ -1,5 +1,7 @@
[DEFAULT]
head = head.js
[browser_check_installed.js]
[browser_editProfileDialog.js]
[browser_privacyPreferences.js]
[browser_manageProfilesDialog.js]

View File

@ -0,0 +1,90 @@
"use strict";
registerCleanupFunction(function* () {
let profiles = yield getProfiles();
if (profiles.length) {
yield removeProfiles(profiles.map(profile => profile.guid));
}
});
add_task(function* test_cancelEditProfileDialog() {
yield new Promise(resolve => {
let win = window.openDialog(EDIT_PROFILE_DIALOG_URL, null, null, null);
win.addEventListener("load", () => {
win.addEventListener("unload", () => {
ok(true, "Edit profile dialog is closed");
resolve();
}, {once: true});
win.document.querySelector("#cancel").click();
}, {once: true});
});
});
add_task(function* test_saveProfile() {
yield new Promise(resolve => {
let win = window.openDialog(EDIT_PROFILE_DIALOG_URL, null, null, null);
win.addEventListener("load", () => {
win.addEventListener("unload", () => {
ok(true, "Edit profile dialog is closed");
resolve();
}, {once: true});
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["given-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["additional-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["family-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1.organization, {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["street-address"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["address-level2"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["address-level1"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["postal-code"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1.country, {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1.email, {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1.tel, {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
info("saving profile");
EventUtils.synthesizeKey("VK_RETURN", {}, win);
}, {once: true});
});
let profiles = yield getProfiles();
is(profiles.length, 1, "only one profile is in storage");
is(Object.keys(TEST_PROFILE_1).length, 11, "Sanity check number of properties");
for (let [fieldName, fieldValue] of Object.entries(TEST_PROFILE_1)) {
is(profiles[0][fieldName], fieldValue, "check " + fieldName);
}
});
add_task(function* test_editProfile() {
let profiles = yield getProfiles();
yield new Promise(resolve => {
let win = window.openDialog(EDIT_PROFILE_DIALOG_URL, null, null, profiles[0]);
win.addEventListener("load", () => {
win.addEventListener("unload", () => {
ok(true, "Edit profile dialog is closed");
resolve();
}, {once: true});
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("test", {}, win);
win.document.querySelector("#save").click();
}, {once: true});
});
profiles = yield getProfiles();
is(profiles.length, 1, "only one profile is in storage");
is(profiles[0]["given-name"], TEST_PROFILE_1["given-name"] + "test", "given-name changed");
yield removeProfiles([profiles[0].guid]);
profiles = yield getProfiles();
is(profiles.length, 0, "Profile storage is empty");
});

Some files were not shown because too many files have changed in this diff Show More