mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge m-c to graphics
MozReview-Commit-ID: 2cIoGVmLEuA
This commit is contained in:
commit
5d5cdc518c
10
.cron.yml
10
.cron.yml
@ -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
|
||||
|
2
CLOBBER
2
CLOBBER
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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()) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 ?
|
||||
|
@ -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"]
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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);
|
||||
|
7
browser/base/content/test/contextMenu/.eslintrc.js
Normal file
7
browser/base/content/test/contextMenu/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/browser-test"
|
||||
]
|
||||
};
|
6
browser/base/content/test/contextMenu/browser.ini
Normal file
6
browser/base/content/test/contextMenu/browser.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
!/browser/base/content/test/general/contextmenu_common.js
|
||||
subtst_contextmenu_webext.html
|
||||
|
||||
[browser_contextmenu_mozextension.js]
|
@ -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);
|
||||
});
|
||||
});
|
@ -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>
|
@ -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]
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
});
|
@ -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.
|
||||
|
10
browser/base/content/test/tabs/test_bug1358314.html
Normal file
10
browser/base/content/test/tabs/test_bug1358314.html
Normal 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>
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
|
@ -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;"
|
||||
|
@ -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",
|
||||
|
@ -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", {});
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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|
|
||||
|
@ -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]
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
});
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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", {});
|
||||
|
||||
|
@ -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();
|
||||
});
|
@ -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");
|
||||
|
@ -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");
|
||||
});
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
},
|
||||
|
||||
|
@ -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* () {
|
||||
|
@ -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]
|
||||
|
@ -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>
|
@ -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);
|
||||
|
@ -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;"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -12,3 +12,4 @@ support-files =
|
||||
[browser_roundedWindow_windowSetting_max.js]
|
||||
[browser_roundedWindow_windowSetting_mid.js]
|
||||
[browser_roundedWindow_windowSetting_min.js]
|
||||
[browser_timezone.js]
|
||||
|
@ -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);
|
||||
});
|
@ -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).
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -52,5 +52,5 @@ add_task(function*() {
|
||||
|
||||
// clean up
|
||||
Services.cookies.removeAll();
|
||||
yield promiseRemoveTab(tab);
|
||||
yield promiseRemoveTab(gBrowser.tabs[1]);
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -26,6 +26,7 @@ let TestAboutPage = {
|
||||
let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
|
||||
aLoadInfo);
|
||||
channel.originalURI = aURI;
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
return channel;
|
||||
},
|
||||
|
||||
|
@ -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],
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
[DEFAULT]
|
||||
head = head.js
|
||||
|
||||
[browser_check_installed.js]
|
||||
[browser_editProfileDialog.js]
|
||||
[browser_privacyPreferences.js]
|
||||
[browser_manageProfilesDialog.js]
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user