зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to central, a=merge
MozReview-Commit-ID: 8MgCHGpAZO0
This commit is contained in:
Коммит
dbc22c750b
|
@ -65,9 +65,6 @@ pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/Sys
|
|||
// See the SCOPE constants in AddonManager.jsm for values to use here.
|
||||
pref("extensions.autoDisableScopes", 15);
|
||||
|
||||
// Whether or not webextension themes are supported.
|
||||
pref("extensions.webextensions.themes.enabled", false);
|
||||
|
||||
// Add-on content security policies.
|
||||
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
|
||||
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");
|
||||
|
|
|
@ -5740,6 +5740,10 @@ function handleLinkClick(event, href, linkNode) {
|
|||
}
|
||||
}
|
||||
|
||||
let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
|
||||
urlSecurityCheck(href, doc.nodePrincipal);
|
||||
let params = {
|
||||
charset: doc.characterSet,
|
||||
|
@ -5748,6 +5752,7 @@ function handleLinkClick(event, href, linkNode) {
|
|||
referrerPolicy,
|
||||
noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
|
||||
originPrincipal: doc.nodePrincipal,
|
||||
frameOuterWindowID,
|
||||
};
|
||||
|
||||
// The new tab/window must use the same userContextId
|
||||
|
|
|
@ -501,10 +501,14 @@ var ClickEventHandler = {
|
|||
}
|
||||
}
|
||||
|
||||
let frameOuterWindowID = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
|
||||
let json = { button: event.button, shiftKey: event.shiftKey,
|
||||
ctrlKey: event.ctrlKey, metaKey: event.metaKey,
|
||||
altKey: event.altKey, href: null, title: null,
|
||||
bookmark: false, referrerPolicy,
|
||||
bookmark: false, frameOuterWindowID, referrerPolicy,
|
||||
triggeringPrincipal: principal,
|
||||
originAttributes: principal ? principal.originAttributes : {},
|
||||
isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
|
||||
|
|
|
@ -951,11 +951,17 @@ nsContextMenu.prototype = {
|
|||
originPrincipal: this.principal,
|
||||
referrerURI: gContextMenuContentData.documentURIObject,
|
||||
referrerPolicy: gContextMenuContentData.referrerPolicy,
|
||||
frameOuterWindowID: gContextMenuContentData.frameOuterWindowID,
|
||||
noReferrer: this.linkHasNoReferrer };
|
||||
for (let p in extra) {
|
||||
params[p] = extra[p];
|
||||
}
|
||||
|
||||
if (!this.isRemote) {
|
||||
params.frameOuterWindowID = this.target.ownerGlobal
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
||||
}
|
||||
// If we want to change userContextId, we must be sure that we don't
|
||||
// propagate the referrer.
|
||||
if ("userContextId" in params &&
|
||||
|
|
|
@ -304,7 +304,30 @@ function openLinkIn(url, where, params) {
|
|||
features += ",private";
|
||||
}
|
||||
|
||||
Services.ww.openWindow(w || window, getBrowserURL(), null, features, sa);
|
||||
const sourceWindow = (w || window);
|
||||
let win;
|
||||
if (params.frameOuterWindowID && sourceWindow) {
|
||||
// Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
|
||||
// event if it contains the expected frameOuterWindowID params.
|
||||
// (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
|
||||
// opening a new window using the keyboard shortcut).
|
||||
const sourceTabBrowser = sourceWindow.gBrowser.selectedBrowser;
|
||||
let delayedStartupObserver = aSubject => {
|
||||
if (aSubject == win) {
|
||||
Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
|
||||
Services.obs.notifyObservers({
|
||||
wrappedJSObject: {
|
||||
url,
|
||||
createdTabBrowser: win.gBrowser.selectedBrowser,
|
||||
sourceTabBrowser,
|
||||
sourceFrameOuterWindowID: params.frameOuterWindowID,
|
||||
},
|
||||
}, "webNavigation-createdNavigationTarget", null);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
win = Services.ww.openWindow(sourceWindow, getBrowserURL(), null, features, sa);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -406,6 +429,21 @@ function openLinkIn(url, where, params) {
|
|||
triggeringPrincipal: aPrincipal,
|
||||
});
|
||||
targetBrowser = tabUsedForLoad.linkedBrowser;
|
||||
|
||||
if (params.frameOuterWindowID && w) {
|
||||
// Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
|
||||
// event if it contains the expected frameOuterWindowID params.
|
||||
// (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
|
||||
// opening a new tab using the keyboard shortcut).
|
||||
Services.obs.notifyObservers({
|
||||
wrappedJSObject: {
|
||||
url,
|
||||
createdTabBrowser: targetBrowser,
|
||||
sourceTabBrowser: w.gBrowser.selectedBrowser,
|
||||
sourceFrameOuterWindowID: params.frameOuterWindowID,
|
||||
},
|
||||
}, "webNavigation-createdNavigationTarget", null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,5 +15,4 @@ browser.jar:
|
|||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.svg
|
||||
content/branding/silhouette-40.svg
|
||||
content/branding/aboutDialog.css
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-45 31 40 40">
|
||||
<path fill="#ccc" d="M-14.1,54.7c0.7-1.4,1.7-4.4,0.8-6.9c0,0,0,0,0,0.1l0,0c0,0-0.2,0.5-0.4,1.3c0-0.1,0-0.2,0-0.3
|
||||
c0.1-0.9,0-1.9-0.1-2.9c-0.3-1.5-1.4-2.8-2-3.2c0,0,0.1,0,0.1,0.1c-0.1-0.1-0.1-0.1-0.1-0.1s0,0.1,0.1,0.4c-0.7-1.1-1.6-1.5-1.6-1.5
|
||||
s0,0.2,0.1,0.5c-2-1.9-4.7-3-7.6-3c-3,0-5.7,1.2-7.8,3.1c0.1,0.1,0.2,0.3,0.4,0.5c0,0,0.8-0.1,1.7-0.1c1.7-1.2,3.6-1.8,5.7-1.8
|
||||
c2.6,0,5.1,1.1,7,3c-0.2-0.1-0.1,0,0,0.1c-0.6-0.4-1.2-0.8-1.7-0.8c1,0.8,2.6,2.7,2.4,6.2c-0.3-0.6-0.6-1-0.9-1.3
|
||||
c0.4,3.5,0,4.2-0.2,5.1c0-0.4-0.2-0.7-0.3-0.9c0,0,0,1.1-0.7,2.6c-0.5,1.2-1.1,1.5-1.3,1.5c-0.2,0-0.1-0.2-0.1-0.4
|
||||
c0,0-0.4,0.2-0.7,0.6c-0.3,0.4-0.6,0.8-0.8,0.6c0.1-0.1,0.2-0.3,0.3-0.4c-0.1,0.1-0.5,0.4-1.2,0.5c-0.3,0-1.6,0.3-3.3-0.6
|
||||
c0.3,0,0.6-0.1,0.9,0.1c-0.3-0.3-1-0.3-1.5-0.4c-0.5-0.4-1.1-1-1.4-1.4c1.3,0.3,2.8,0.1,3.6-0.5s1.3-1,1.8-0.9
|
||||
c0.4,0.1,0.7-0.4,0.4-0.8c-0.3-0.4-1.2-1-2.3-0.7c-0.8,0.2-1.8,1.1-3.3,0.2c-1.3-0.8-1.3-1.4-1.3-1.8c0-0.3,0.2-0.7,0.5-0.8
|
||||
c0.2,0.1,0.3,0.1,0.3,0.1s-0.1-0.1-0.1-0.2l0,0c0.1,0,0.4,0.2,0.6,0.2c0.2,0.1,0.3,0.2,0.3,0.2s0,0,0-0.1c0,0-0.1-0.2-0.3-0.3l0,0
|
||||
c0.1,0,0.2,0.1,0.4,0.2c0-0.2,0.1-0.4,0.1-0.7c0-0.2,0-0.3-0.1-0.4c-0.1-0.1,0-0.1,0.1,0c0-0.1,0-0.1-0.1-0.2l0,0c0,0,0,0,0-0.1
|
||||
c0.2-0.3,1.8-1.2,1.9-1.3c0.2-0.1,0.3-0.3,0.4-0.5c0.2-0.1,0.3-0.5,0.3-0.8c0-0.1-0.2-0.3-0.4-0.3c-0.1,0-0.4-0.1-0.6,0l0,0
|
||||
c-0.3,0-0.7,0-1.2,0s-0.8-0.3-1-0.6c0-0.1-0.1-0.1-0.1-0.2c0-0.1-0.1-0.2-0.1-0.2c0.2-0.8,0.7-1.5,1.4-2.1c0,0-0.2,0-0.1,0
|
||||
c0,0,0.3-0.2,0.4-0.2c0.1,0-0.3-0.1-0.6-0.1c-0.5,0.2-0.6,0.2-0.8,0.3c0.1-0.1,0.3-0.2,0.2-0.2c-0.3,0.1-0.7,0.4-1.1,0.6v-0.1
|
||||
c-0.2,0.1-0.6,0.4-0.7,0.7c0-0.1,0-0.1,0-0.1c-0.1,0-0.2,0.2-0.3,0.3l0,0c-1.1-0.3-2-0.2-2.8,0c-0.2-0.1-0.6-0.5-0.9-1
|
||||
c0,0,0,0.1-0.1,0.1c-0.1-0.4-0.3-0.9-0.3-1.3v-0.1c0,0-0.1,0.1-0.3,0.3c-0.1,0.2-0.2,0.3-0.2,0.5c0,0.1-0.1,0.2-0.1,0.2v-0.2
|
||||
c0,0.1-0.1,0.2-0.2,0.3c0,0.2,0,0.3-0.1,0.4l0,0c0,0,0-0.2,0-0.1c-0.1,0.2-0.2,0.5-0.2,0.8c-0.1,0.3-0.1,0.5-0.1,0.8s0,0.7,0,1.2
|
||||
c0,0.1,0,0.1,0,0.2c-0.3,0.4-0.5,0.7-0.6,0.9c-0.4,0.7-0.7,1.8-1,3.5c0,0,0.2-0.6,0.6-1.3l0,0c-0.3,0.9-0.5,2.3-0.4,4.4
|
||||
c0-0.1,0.1-0.6,0.2-1.3c0.1,1.4,0.5,3.1,1.5,5c0.8,1.4,1.7,2.4,2.7,3.2c0.2,0.2,0.4,0.3,0.6,0.5c1.3,1,3.3,2.1,5,2.4
|
||||
c-0.6-0.2-1-0.5-1-0.5s2,0.7,3.5,0.6c-0.5-0.1-0.6-0.3-0.6-0.3s4.2,0.2,6.4-1.5c0.5-0.4,0.8-0.8,0.9-1.2c0.6-0.4,1.3-0.8,2-1.6
|
||||
c1.2-1.2,1.3-2.1,1.4-3v0.1C-14,55.2-14,54.9-14.1,54.7z"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 2.6 KiB |
|
@ -15,5 +15,4 @@ browser.jar:
|
|||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.svg
|
||||
content/branding/silhouette-40.svg
|
||||
content/branding/aboutDialog.css
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
До Ширина: | Высота: | Размер: 148 KiB |
|
@ -14,5 +14,4 @@ browser.jar:
|
|||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.svg
|
||||
content/branding/silhouette-40.svg
|
||||
content/branding/aboutDialog.css
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-45 31 40 40">
|
||||
<path fill="#ccc" d="M-14.1,54.7c0.7-1.4,1.7-4.4,0.8-6.9c0,0,0,0,0,0.1l0,0c0,0-0.2,0.5-0.4,1.3c0-0.1,0-0.2,0-0.3
|
||||
c0.1-0.9,0-1.9-0.1-2.9c-0.3-1.5-1.4-2.8-2-3.2c0,0,0.1,0,0.1,0.1c-0.1-0.1-0.1-0.1-0.1-0.1s0,0.1,0.1,0.4c-0.7-1.1-1.6-1.5-1.6-1.5
|
||||
s0,0.2,0.1,0.5c-2-1.9-4.7-3-7.6-3c-3,0-5.7,1.2-7.8,3.1c0.1,0.1,0.2,0.3,0.4,0.5c0,0,0.8-0.1,1.7-0.1c1.7-1.2,3.6-1.8,5.7-1.8
|
||||
c2.6,0,5.1,1.1,7,3c-0.2-0.1-0.1,0,0,0.1c-0.6-0.4-1.2-0.8-1.7-0.8c1,0.8,2.6,2.7,2.4,6.2c-0.3-0.6-0.6-1-0.9-1.3
|
||||
c0.4,3.5,0,4.2-0.2,5.1c0-0.4-0.2-0.7-0.3-0.9c0,0,0,1.1-0.7,2.6c-0.5,1.2-1.1,1.5-1.3,1.5c-0.2,0-0.1-0.2-0.1-0.4
|
||||
c0,0-0.4,0.2-0.7,0.6c-0.3,0.4-0.6,0.8-0.8,0.6c0.1-0.1,0.2-0.3,0.3-0.4c-0.1,0.1-0.5,0.4-1.2,0.5c-0.3,0-1.6,0.3-3.3-0.6
|
||||
c0.3,0,0.6-0.1,0.9,0.1c-0.3-0.3-1-0.3-1.5-0.4c-0.5-0.4-1.1-1-1.4-1.4c1.3,0.3,2.8,0.1,3.6-0.5s1.3-1,1.8-0.9
|
||||
c0.4,0.1,0.7-0.4,0.4-0.8c-0.3-0.4-1.2-1-2.3-0.7c-0.8,0.2-1.8,1.1-3.3,0.2c-1.3-0.8-1.3-1.4-1.3-1.8c0-0.3,0.2-0.7,0.5-0.8
|
||||
c0.2,0.1,0.3,0.1,0.3,0.1s-0.1-0.1-0.1-0.2l0,0c0.1,0,0.4,0.2,0.6,0.2c0.2,0.1,0.3,0.2,0.3,0.2s0,0,0-0.1c0,0-0.1-0.2-0.3-0.3l0,0
|
||||
c0.1,0,0.2,0.1,0.4,0.2c0-0.2,0.1-0.4,0.1-0.7c0-0.2,0-0.3-0.1-0.4c-0.1-0.1,0-0.1,0.1,0c0-0.1,0-0.1-0.1-0.2l0,0c0,0,0,0,0-0.1
|
||||
c0.2-0.3,1.8-1.2,1.9-1.3c0.2-0.1,0.3-0.3,0.4-0.5c0.2-0.1,0.3-0.5,0.3-0.8c0-0.1-0.2-0.3-0.4-0.3c-0.1,0-0.4-0.1-0.6,0l0,0
|
||||
c-0.3,0-0.7,0-1.2,0s-0.8-0.3-1-0.6c0-0.1-0.1-0.1-0.1-0.2c0-0.1-0.1-0.2-0.1-0.2c0.2-0.8,0.7-1.5,1.4-2.1c0,0-0.2,0-0.1,0
|
||||
c0,0,0.3-0.2,0.4-0.2c0.1,0-0.3-0.1-0.6-0.1c-0.5,0.2-0.6,0.2-0.8,0.3c0.1-0.1,0.3-0.2,0.2-0.2c-0.3,0.1-0.7,0.4-1.1,0.6v-0.1
|
||||
c-0.2,0.1-0.6,0.4-0.7,0.7c0-0.1,0-0.1,0-0.1c-0.1,0-0.2,0.2-0.3,0.3l0,0c-1.1-0.3-2-0.2-2.8,0c-0.2-0.1-0.6-0.5-0.9-1
|
||||
c0,0,0,0.1-0.1,0.1c-0.1-0.4-0.3-0.9-0.3-1.3v-0.1c0,0-0.1,0.1-0.3,0.3c-0.1,0.2-0.2,0.3-0.2,0.5c0,0.1-0.1,0.2-0.1,0.2v-0.2
|
||||
c0,0.1-0.1,0.2-0.2,0.3c0,0.2,0,0.3-0.1,0.4l0,0c0,0,0-0.2,0-0.1c-0.1,0.2-0.2,0.5-0.2,0.8c-0.1,0.3-0.1,0.5-0.1,0.8s0,0.7,0,1.2
|
||||
c0,0.1,0,0.1,0,0.2c-0.3,0.4-0.5,0.7-0.6,0.9c-0.4,0.7-0.7,1.8-1,3.5c0,0,0.2-0.6,0.6-1.3l0,0c-0.3,0.9-0.5,2.3-0.4,4.4
|
||||
c0-0.1,0.1-0.6,0.2-1.3c0.1,1.4,0.5,3.1,1.5,5c0.8,1.4,1.7,2.4,2.7,3.2c0.2,0.2,0.4,0.3,0.6,0.5c1.3,1,3.3,2.1,5,2.4
|
||||
c-0.6-0.2-1-0.5-1-0.5s2,0.7,3.5,0.6c-0.5-0.1-0.6-0.3-0.6-0.3s4.2,0.2,6.4-1.5c0.5-0.4,0.8-0.8,0.9-1.2c0.6-0.4,1.3-0.8,2-1.6
|
||||
c1.2-1.2,1.3-2.1,1.4-3v0.1C-14,55.2-14,54.9-14.1,54.7z"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 2.6 KiB |
|
@ -15,5 +15,4 @@ browser.jar:
|
|||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.svg
|
||||
content/branding/silhouette-40.svg
|
||||
content/branding/aboutDialog.css
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
До Ширина: | Высота: | Размер: 148 KiB |
|
@ -430,14 +430,6 @@ const DownloadsIndicatorView = {
|
|||
* progress is not visible if the current progress is unknown.
|
||||
*/
|
||||
set percentComplete(aValue) {
|
||||
// For arrow type only:
|
||||
// We show portion of the success icon in propotional with the download
|
||||
// progress as an indicator. the PROGRESS_ICON_EMPTY_HEIGHT_PERCENT and
|
||||
// PROGRESS_ICON_FULL_HEIGHT_PERCENT correspond to how much portion of the
|
||||
// icon should be displayed in 0% and 100%.
|
||||
const PROGRESS_ICON_EMPTY_HEIGHT_PERCENT = 35;
|
||||
const PROGRESS_ICON_FULL_HEIGHT_PERCENT = 87;
|
||||
|
||||
if (!this._operational) {
|
||||
return this._percentComplete;
|
||||
}
|
||||
|
@ -448,13 +440,13 @@ const DownloadsIndicatorView = {
|
|||
|
||||
if (this._percentComplete >= 0) {
|
||||
this.indicator.setAttribute("progress", "true");
|
||||
this._progressIcon.style.height = (this._percentComplete *
|
||||
(PROGRESS_ICON_FULL_HEIGHT_PERCENT -
|
||||
PROGRESS_ICON_EMPTY_HEIGHT_PERCENT) / 100 +
|
||||
PROGRESS_ICON_EMPTY_HEIGHT_PERCENT) + "%";
|
||||
// For arrow type only:
|
||||
// We set animationDelay to a minus value (0s ~ -100s) to show the
|
||||
// corresponding frame needed for progress.
|
||||
this._progressIcon.style.animationDelay = (-this._percentComplete) + "s";
|
||||
} else {
|
||||
this.indicator.removeAttribute("progress");
|
||||
this._progressIcon.style.height = "0";
|
||||
this._progressIcon.style.animationDelay = "1s";
|
||||
}
|
||||
// We have to set the attribute instead of using the property because the
|
||||
// XBL binding isn't applied if the element is invisible for any reason.
|
||||
|
|
|
@ -15,7 +15,6 @@ category webextension-scripts pageAction chrome://browser/content/ext-pageAction
|
|||
category webextension-scripts sessions chrome://browser/content/ext-sessions.js
|
||||
category webextension-scripts sidebarAction chrome://browser/content/ext-sidebarAction.js
|
||||
category webextension-scripts tabs chrome://browser/content/ext-tabs.js
|
||||
category webextension-scripts theme chrome://browser/content/ext-theme.js
|
||||
category webextension-scripts url-overrides chrome://browser/content/ext-url-overrides.js
|
||||
category webextension-scripts utils chrome://browser/content/ext-utils.js
|
||||
category webextension-scripts windows chrome://browser/content/ext-windows.js
|
||||
|
@ -46,6 +45,5 @@ category webextension-schemas page_action chrome://browser/content/schemas/page_
|
|||
category webextension-schemas sessions chrome://browser/content/schemas/sessions.json
|
||||
category webextension-schemas sidebar_action chrome://browser/content/schemas/sidebar_action.json
|
||||
category webextension-schemas tabs chrome://browser/content/schemas/tabs.json
|
||||
category webextension-schemas theme chrome://browser/content/schemas/theme.json
|
||||
category webextension-schemas url_overrides chrome://browser/content/schemas/url_overrides.json
|
||||
category webextension-schemas windows chrome://browser/content/schemas/windows.json
|
||||
|
|
|
@ -28,7 +28,6 @@ browser.jar:
|
|||
content/browser/ext-sessions.js
|
||||
content/browser/ext-sidebarAction.js
|
||||
content/browser/ext-tabs.js
|
||||
content/browser/ext-theme.js
|
||||
content/browser/ext-url-overrides.js
|
||||
content/browser/ext-utils.js
|
||||
content/browser/ext-windows.js
|
||||
|
|
|
@ -19,6 +19,5 @@ browser.jar:
|
|||
content/browser/schemas/sessions.json
|
||||
content/browser/schemas/sidebar_action.json
|
||||
content/browser/schemas/tabs.json
|
||||
content/browser/schemas/theme.json
|
||||
content/browser/schemas/url_overrides.json
|
||||
content/browser/schemas/windows.json
|
||||
|
|
|
@ -19,6 +19,9 @@ support-files =
|
|||
file_dummy.html
|
||||
file_inspectedwindow_reload_target.sjs
|
||||
file_serviceWorker.html
|
||||
webNav_createdTarget.html
|
||||
webNav_createdTargetSource.html
|
||||
webNav_createdTargetSource_subframe.html
|
||||
serviceWorker.js
|
||||
searchSuggestionEngine.xml
|
||||
searchSuggestionEngine.sjs
|
||||
|
@ -113,9 +116,6 @@ support-files =
|
|||
[browser_ext_tabs_update.js]
|
||||
[browser_ext_tabs_zoom.js]
|
||||
[browser_ext_tabs_update_url.js]
|
||||
[browser_ext_themes_chromeparity.js]
|
||||
[browser_ext_themes_dynamic_updates.js]
|
||||
[browser_ext_themes_lwtsupport.js]
|
||||
[browser_ext_topwindowid.js]
|
||||
[browser_ext_url_overrides_all.js]
|
||||
[browser_ext_url_overrides_home.js]
|
||||
|
@ -123,6 +123,8 @@ support-files =
|
|||
[browser_ext_webRequest.js]
|
||||
[browser_ext_webNavigation_frameId0.js]
|
||||
[browser_ext_webNavigation_getFrames.js]
|
||||
[browser_ext_webNavigation_onCreatedNavigationTarget.js]
|
||||
[browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js]
|
||||
[browser_ext_webNavigation_urlbar_transitions.js]
|
||||
[browser_ext_windows.js]
|
||||
[browser_ext_windows_create.js]
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
|
||||
const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
|
||||
const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
|
||||
|
||||
async function background() {
|
||||
const tabs = await browser.tabs.query({active: true, currentWindow: true});
|
||||
const sourceTabId = tabs[0].id;
|
||||
|
||||
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
|
||||
|
||||
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
|
||||
browser.test.sendMessage("webNavOnCreated", msg);
|
||||
});
|
||||
|
||||
browser.webNavigation.onCompleted.addListener(async (msg) => {
|
||||
// NOTE: checking the url is currently necessary because of Bug 1252129
|
||||
// ( Filter out webNavigation events related to new window initialization phase).
|
||||
if (msg.tabId !== sourceTabId && msg.url !== "about:blank") {
|
||||
await browser.tabs.remove(msg.tabId);
|
||||
browser.test.sendMessage("webNavOnCompleted", msg);
|
||||
}
|
||||
});
|
||||
|
||||
browser.tabs.onCreated.addListener((tab) => {
|
||||
browser.test.sendMessage("tabsOnCreated", tab.id);
|
||||
});
|
||||
|
||||
browser.test.sendMessage("expectedSourceTab", {
|
||||
sourceTabId, sourceTabFrames,
|
||||
});
|
||||
}
|
||||
|
||||
async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
|
||||
await openNavTarget();
|
||||
|
||||
const webNavMsg = await extension.awaitMessage("webNavOnCreated");
|
||||
const createdTabId = await extension.awaitMessage("tabsOnCreated");
|
||||
const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
|
||||
|
||||
let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
|
||||
|
||||
is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
|
||||
is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
|
||||
is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
|
||||
is(webNavMsg.url, url, "Got the expected url property");
|
||||
|
||||
is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
|
||||
is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
|
||||
}
|
||||
|
||||
async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
|
||||
const contentAreaContextMenu = await openContextMenu(pageElementSelector);
|
||||
const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
|
||||
is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
|
||||
item[0].click();
|
||||
await closeContextMenu();
|
||||
}
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_mouse_click() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open link in a new tab using Ctrl-click");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-tab-from-mouse-click",
|
||||
{ctrlKey: true, metaKey: true},
|
||||
tab.linkedBrowser);
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-tab-from-mouse-click`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open link in a new window using Shift-click");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-window-from-mouse-click",
|
||||
{shiftKey: true},
|
||||
tab.linkedBrowser);
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-window-from-mouse-click`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_context_menu() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open link in a new tab from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: "#test-create-new-tab-from-context-menu",
|
||||
contextMenuItemLabel: "Open Link in New Tab",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-tab-from-context-menu`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open link in a new window from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector: "#test-create-new-window-from-context-menu",
|
||||
contextMenuItemLabel: "Open Link in New Window",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-window-from-context-menu`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_mouse_click_subframe() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open a subframe link in a new tab using Ctrl-click");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(function() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.querySelector("#test-create-new-tab-from-mouse-click-subframe");
|
||||
}, {ctrlKey: true, metaKey: true}, tab.linkedBrowser);
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-tab-from-mouse-click-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open a subframe link in a new window using Shift-click");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(function() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.querySelector("#test-create-new-window-from-mouse-click-subframe");
|
||||
}, {shiftKey: true}, tab.linkedBrowser);
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-window-from-mouse-click-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_context_menu_subframe() {
|
||||
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("Open a subframe link in a new tab from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.document.querySelector("#test-create-new-tab-from-context-menu-subframe");
|
||||
},
|
||||
contextMenuItemLabel: "Open Link in New Tab",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
info("Open a subframe link in a new window from the context menu");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
async openNavTarget() {
|
||||
await clickContextMenuItem({
|
||||
pageElementSelector() {
|
||||
// This code runs as a framescript in the child process and it returns the
|
||||
// target link in the subframe.
|
||||
return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
|
||||
.document.querySelector("#test-create-new-window-from-context-menu-subframe");
|
||||
},
|
||||
contextMenuItemLabel: "Open Link in New Window",
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
|
@ -0,0 +1,169 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
|
||||
const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
|
||||
const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
|
||||
|
||||
async function background() {
|
||||
const tabs = await browser.tabs.query({active: true, currentWindow: true});
|
||||
const sourceTabId = tabs[0].id;
|
||||
|
||||
const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
|
||||
|
||||
browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
|
||||
browser.test.sendMessage("webNavOnCreated", msg);
|
||||
});
|
||||
|
||||
browser.webNavigation.onCompleted.addListener(async (msg) => {
|
||||
// NOTE: checking the url is currently necessary because of Bug 1252129
|
||||
// ( Filter out webNavigation events related to new window initialization phase).
|
||||
if (msg.tabId !== sourceTabId && msg.url !== "about:blank") {
|
||||
await browser.tabs.remove(msg.tabId);
|
||||
browser.test.sendMessage("webNavOnCompleted", msg);
|
||||
}
|
||||
});
|
||||
|
||||
browser.tabs.onCreated.addListener((tab) => {
|
||||
browser.test.sendMessage("tabsOnCreated", tab.id);
|
||||
});
|
||||
|
||||
browser.test.onMessage.addListener(({type, code}) => {
|
||||
if (type === "execute-contentscript") {
|
||||
browser.tabs.executeScript(sourceTabId, {code: code});
|
||||
}
|
||||
});
|
||||
|
||||
browser.test.sendMessage("expectedSourceTab", {
|
||||
sourceTabId, sourceTabFrames,
|
||||
});
|
||||
}
|
||||
|
||||
async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
|
||||
await openNavTarget();
|
||||
|
||||
const webNavMsg = await extension.awaitMessage("webNavOnCreated");
|
||||
const createdTabId = await extension.awaitMessage("tabsOnCreated");
|
||||
const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
|
||||
|
||||
let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
|
||||
|
||||
is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
|
||||
is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
|
||||
is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
|
||||
is(webNavMsg.url, url, "Got the expected url property");
|
||||
|
||||
is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
|
||||
is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
|
||||
}
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_window_open() {
|
||||
const tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation", "tabs", "<all_urls>"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("open an url in a new tab from a window.open call");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
extension.sendMessage({
|
||||
type: "execute-contentscript",
|
||||
code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-tab-from-window-open`,
|
||||
},
|
||||
});
|
||||
|
||||
info("open an url in a new window from a window.open call");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
extension.sendMessage({
|
||||
type: "execute-contentscript",
|
||||
code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: 0,
|
||||
url: `${OPENED_PAGE}#new-win-from-window-open`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* test_on_created_navigation_target_from_window_open_subframe() {
|
||||
const tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["webNavigation", "tabs", "<all_urls>"],
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
|
||||
|
||||
info("open an url in a new tab from subframe window.open call");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
extension.sendMessage({
|
||||
type: "execute-contentscript",
|
||||
code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
info("open an url in a new window from subframe window.open call");
|
||||
|
||||
yield runTestCase({
|
||||
extension,
|
||||
openNavTarget() {
|
||||
extension.sendMessage({
|
||||
type: "execute-contentscript",
|
||||
code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
|
||||
});
|
||||
},
|
||||
expectedWebNavProps: {
|
||||
sourceTabId: expectedSourceTab.sourceTabId,
|
||||
sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
|
||||
url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
|
||||
},
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
|
@ -225,19 +225,19 @@ function closeBrowserAction(extension, win = window) {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function* openContextMenu(selector = "#img1") {
|
||||
async function openContextMenu(selector = "#img1") {
|
||||
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "contextmenu"}, gBrowser.selectedBrowser);
|
||||
yield popupShownPromise;
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(selector, {type: "contextmenu"}, gBrowser.selectedBrowser);
|
||||
await popupShownPromise;
|
||||
return contentAreaContextMenu;
|
||||
}
|
||||
|
||||
function* closeContextMenu() {
|
||||
async function closeContextMenu() {
|
||||
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
|
||||
contentAreaContextMenu.hidePopup();
|
||||
yield popupHiddenPromise;
|
||||
await popupHiddenPromise;
|
||||
}
|
||||
|
||||
function* openExtensionContextMenu(selector = "#img1") {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebNavigatio onCreatedNavigationTarget target</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<a id="other-link" href="webNav_createdTarget_source.html">Go back to the source page</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebNavigatio onCreatedNavigationTarget source</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
<a id="test-create-new-tab-from-context-menu"
|
||||
href="webNav_createdTarget.html#new-tab-from-context-menu">
|
||||
Open a target page in a new tab from the context menu
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="test-create-new-window-from-context-menu"
|
||||
href="webNav_createdTarget.html#new-window-from-context-menu">
|
||||
Open a target page in a new window from the context menu
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="test-create-new-tab-from-mouse-click"
|
||||
href="webNav_createdTarget.html#new-tab-from-mouse-click">
|
||||
Open a target page in a new tab from mouse click
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="test-create-new-window-from-mouse-click"
|
||||
href="webNav_createdTarget.html#new-window-from-mouse-click">
|
||||
Open a target page in a new window from mouse click
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<iframe src="webNav_createdTargetSource_subframe.html" style="width: 100%; height: 100%;">
|
||||
</iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebNavigatio onCreatedNavigationTarget source subframe</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
<a id="test-create-new-tab-from-context-menu-subframe"
|
||||
href="webNav_createdTarget.html#new-tab-from-context-menu-subframe">
|
||||
Open a target page in a new tab from the context menu (subframe)
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="test-create-new-window-from-context-menu-subframe"
|
||||
href="webNav_createdTarget.html#new-window-from-context-menu-subframe">
|
||||
Open a target page in a new window from the context menu (subframe)
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="test-create-new-tab-from-mouse-click-subframe"
|
||||
href="webNav_createdTarget.html#new-tab-from-mouse-click-subframe">
|
||||
Open a target page in a new tab from mouse click (subframe)
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="test-create-new-window-from-mouse-click-subframe"
|
||||
href="webNav_createdTarget.html#new-window-from-mouse-click-subframe">
|
||||
Open a target page in a new window from mouse click (subframe)
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -1114,6 +1114,21 @@
|
|||
this._isHiding = false;
|
||||
}, 0);
|
||||
]]></handler>
|
||||
|
||||
<!-- This handles clicks on the topmost "Foo Search" header in the
|
||||
popup (hbox[anonid="searchbar-engine"]). -->
|
||||
<handler event="click"><![CDATA[
|
||||
if (event.button == 2) {
|
||||
// Ignore right clicks.
|
||||
return;
|
||||
}
|
||||
let button = event.originalTarget;
|
||||
let engine = button.parentNode.engine;
|
||||
if (!engine) {
|
||||
return;
|
||||
}
|
||||
this.oneOffButtons.handleSearchCommand(event, engine);
|
||||
]]></handler>
|
||||
</handlers>
|
||||
|
||||
</binding>
|
||||
|
@ -2223,7 +2238,7 @@
|
|||
return; // ignore right clicks.
|
||||
|
||||
let button = event.originalTarget;
|
||||
let engine = button.engine || button.parentNode.engine;
|
||||
let engine = button.engine;
|
||||
|
||||
if (!engine)
|
||||
return;
|
||||
|
|
|
@ -122,10 +122,19 @@ add_task(function* test_text() {
|
|||
is(getHeaderText(), "Search for foo with:",
|
||||
"Header has the correct text when search terms have been entered and the Change Search Settings button is selected.");
|
||||
|
||||
promise = promiseEvent(searchPopup, "popuphidden");
|
||||
info("Closing search panel");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield promise;
|
||||
// Click the "Foo Search" header at the top of the popup and make sure it
|
||||
// loads the search results.
|
||||
let searchbarEngine =
|
||||
document.getAnonymousElementByAttribute(searchPopup, "anonid",
|
||||
"searchbar-engine");
|
||||
|
||||
yield synthesizeNativeMouseMove(searchbarEngine);
|
||||
SimpleTest.executeSoon(() => {
|
||||
EventUtils.synthesizeMouseAtCenter(searchbarEngine, {});
|
||||
});
|
||||
|
||||
let url = Services.search.currentEngine.getSubmission(textbox.value).uri.spec;
|
||||
yield promiseTabLoadEvent(gBrowser.selectedTab, url);
|
||||
|
||||
// Move the cursor out of the panel area to avoid messing with other tests.
|
||||
yield synthesizeNativeMouseMove(searchbar);
|
||||
|
|
|
@ -85,6 +85,7 @@ var ContentClick = {
|
|||
allowMixedContent: json.allowMixedContent,
|
||||
isContentWindowPrivate: json.isContentWindowPrivate,
|
||||
originPrincipal: json.originPrincipal,
|
||||
frameOuterWindowID: json.frameOuterWindowID,
|
||||
};
|
||||
|
||||
// The new tab/window must use the same userContextId.
|
||||
|
|
|
@ -13,9 +13,40 @@
|
|||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
transition: height 0.5s;
|
||||
/* From javascript side we use animation delay from 0s to -100s to show
|
||||
* corresponding frames needed for progress.
|
||||
* animation-delay is set to a positive value to make nothing shown.
|
||||
*/
|
||||
animation-play-state: paused;
|
||||
animation-delay: 1s;
|
||||
animation-duration: 100s;
|
||||
animation-timing-function: linear;
|
||||
animation-name: indicatorArrowProgress;
|
||||
}
|
||||
|
||||
toolbar[brighttext] #downloads-indicator-progress-icon {
|
||||
background-image: var(--downloads-indicator-image-attention-inverted);
|
||||
animation-name: indicatorArrowProgressDark;
|
||||
}
|
||||
|
||||
@keyframes indicatorArrowProgress {
|
||||
0% {
|
||||
height: 35%;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
100% {
|
||||
height: 87%;
|
||||
filter: brightness(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes indicatorArrowProgressDark {
|
||||
0% {
|
||||
height: 35%;
|
||||
filter: brightness(0.7);
|
||||
}
|
||||
100% {
|
||||
height: 87%;
|
||||
filter: brightness(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const FileSaver = require("devtools/client/shared/file-saver");
|
||||
const JSZip = require("devtools/client/shared/vendor/jszip");
|
||||
const clipboardHelper = require("devtools/shared/platform/clipboard");
|
||||
const { HarUtils } = require("./har-utils.js");
|
||||
const { HarBuilder } = require("./har-builder.js");
|
||||
const { HarBuilder } = require("./har-builder");
|
||||
|
||||
var uid = 1;
|
||||
|
||||
|
@ -61,31 +62,48 @@ const HarExporter = {
|
|||
* - forceExport {Boolean}: The result HAR file is created even if
|
||||
* there are no HTTP entries.
|
||||
*/
|
||||
save: function (options) {
|
||||
async save(options) {
|
||||
// Set default options related to save operation.
|
||||
options.defaultFileName = Services.prefs.getCharPref(
|
||||
let defaultFileName = Services.prefs.getCharPref(
|
||||
"devtools.netmonitor.har.defaultFileName");
|
||||
options.compress = Services.prefs.getBoolPref(
|
||||
let compress = Services.prefs.getBoolPref(
|
||||
"devtools.netmonitor.har.compress");
|
||||
|
||||
// Get target file for exported data. Bail out, if the user
|
||||
// presses cancel.
|
||||
let file = HarUtils.getTargetFile(options.defaultFileName,
|
||||
options.jsonp, options.compress);
|
||||
trace.log("HarExporter.save; " + defaultFileName, options);
|
||||
|
||||
if (!file) {
|
||||
return Promise.resolve();
|
||||
let data = await this.fetchHarData(options);
|
||||
let fileName = this.getHarFileName(defaultFileName, options.jsonp, compress);
|
||||
|
||||
if (compress) {
|
||||
data = await JSZip().file(fileName, data).generateAsync({
|
||||
compression: "DEFLATE",
|
||||
platform: Services.appinfo.OS === "WINNT" ? "DOS" : "UNIX",
|
||||
type: "blob",
|
||||
});
|
||||
}
|
||||
|
||||
trace.log("HarExporter.save; " + options.defaultFileName, options);
|
||||
fileName = `${fileName}${compress ? ".zip" : ""}`;
|
||||
let blob = compress ? data : new Blob([data], { type: "application/json" });
|
||||
|
||||
return this.fetchHarData(options).then(jsonString => {
|
||||
if (!HarUtils.saveToFile(file, jsonString, options.compress)) {
|
||||
let msg = "Failed to save HAR file at: " + options.defaultFileName;
|
||||
console.error(msg);
|
||||
}
|
||||
return jsonString;
|
||||
});
|
||||
FileSaver.saveAs(blob, fileName, document);
|
||||
},
|
||||
|
||||
formatDate(date) {
|
||||
let year = String(date.getFullYear() % 100).padStart(2, "0");
|
||||
let month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
let day = String(date.getDate()).padStart(2, "0");
|
||||
let hour = String(date.getHours()).padStart(2, "0");
|
||||
let minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
let seconds = String(date.getSeconds()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day} ${hour}-${minutes}-${seconds}`;
|
||||
},
|
||||
|
||||
getHarFileName(defaultFileName, jsonp, compress) {
|
||||
let name = defaultFileName.replace(/%date/g, this.formatDate(new Date()));
|
||||
name = name.replace(/\:/gm, "-", "");
|
||||
name = name.replace(/\//gm, "_", "");
|
||||
return `${name}.${jsonp ? "harp" : "har"}`;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -326,16 +326,14 @@ RequestListContextMenu.prototype = {
|
|||
* Copy HAR from the network panel content to the clipboard.
|
||||
*/
|
||||
copyAllAsHar() {
|
||||
let options = this.getDefaultHarOptions();
|
||||
return HarExporter.copy(options);
|
||||
return HarExporter.copy(this.getDefaultHarOptions());
|
||||
},
|
||||
|
||||
/**
|
||||
* Save HAR from the network panel content to a file.
|
||||
*/
|
||||
saveAllAsHar() {
|
||||
let options = this.getDefaultHarOptions();
|
||||
return HarExporter.save(options);
|
||||
return HarExporter.save(this.getDefaultHarOptions());
|
||||
},
|
||||
|
||||
getDefaultHarOptions() {
|
||||
|
|
|
@ -57,31 +57,13 @@ function ParamsPanel({
|
|||
|
||||
// Query String section
|
||||
if (query) {
|
||||
object[PARAMS_QUERY_STRING] =
|
||||
parseQueryString(query)
|
||||
.reduce((acc, { name, value }) =>
|
||||
name ? Object.assign(acc, { [name]: value }) : acc
|
||||
, {});
|
||||
object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
|
||||
}
|
||||
|
||||
// Form Data section
|
||||
if (formDataSections && formDataSections.length > 0) {
|
||||
let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
|
||||
object[PARAMS_FORM_DATA] =
|
||||
parseQueryString(sections)
|
||||
.reduce((map, obj) => {
|
||||
let value = map[obj.name];
|
||||
// Deal with duplicate key case (ex: multiple selection)
|
||||
if (value) {
|
||||
if (typeof value !== "object") {
|
||||
map[obj.name] = [value];
|
||||
}
|
||||
map[obj.name].push(obj.value);
|
||||
} else {
|
||||
map[obj.name] = obj.value;
|
||||
}
|
||||
return map;
|
||||
}, {});
|
||||
object[PARAMS_FORM_DATA] = getProperties(parseQueryString(sections));
|
||||
}
|
||||
|
||||
// Request payload section
|
||||
|
@ -123,4 +105,28 @@ ParamsPanel.propTypes = {
|
|||
request: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapping array to dict for TreeView usage.
|
||||
* Since TreeView only support Object(dict) format.
|
||||
* This function also deal with duplicate key case
|
||||
* (for multiple selection and query params with same keys)
|
||||
*
|
||||
* @param {Object[]} arr - key-value pair array like query or form params
|
||||
* @returns {Object} Rep compatible object
|
||||
*/
|
||||
function getProperties(arr) {
|
||||
return arr.reduce((map, obj) => {
|
||||
let value = map[obj.name];
|
||||
if (value) {
|
||||
if (typeof value !== "object") {
|
||||
map[obj.name] = [value];
|
||||
}
|
||||
map[obj.name].push(obj.value);
|
||||
} else {
|
||||
map[obj.name] = obj.value;
|
||||
}
|
||||
return map;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = ParamsPanel;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* eslint-env browser */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* HTML5 file saver to provide a standard download interface with a "Save As"
|
||||
* dialog
|
||||
*
|
||||
* @param {object} blob - A blob object will be downloaded
|
||||
* @param {string} filename - Given a file name which will display in "Save As" dialog
|
||||
* @param {object} document - Optional. A HTML document for creating a temporary anchor
|
||||
* for triggering a file download.
|
||||
*/
|
||||
function saveAs(blob, filename = "", doc = document) {
|
||||
let url = URL.createObjectURL(blob);
|
||||
let a = doc.createElement("a");
|
||||
doc.body.appendChild(a);
|
||||
a.style = "display: none";
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
a.remove();
|
||||
}
|
||||
|
||||
exports.saveAs = saveAs;
|
|
@ -29,6 +29,7 @@ DevToolsModules(
|
|||
'DOMHelpers.jsm',
|
||||
'doorhanger.js',
|
||||
'enum.js',
|
||||
'file-saver.js',
|
||||
'file-watcher-worker.js',
|
||||
'file-watcher.js',
|
||||
'getjson.js',
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -7,6 +7,7 @@ modules = []
|
|||
modules += [
|
||||
'immutable.js',
|
||||
'jsol.js',
|
||||
'jszip.js',
|
||||
'react-addons-shallow-compare.js',
|
||||
]
|
||||
|
||||
|
|
|
@ -1065,9 +1065,6 @@ class Protocol(ipdl.ast.Protocol):
|
|||
return Type(_actorName(self._ipdlmgrtype().name(), side),
|
||||
ptr=ptr)
|
||||
|
||||
def stateMethod(self):
|
||||
return ExprVar('state');
|
||||
|
||||
def registerMethod(self):
|
||||
return ExprVar('Register')
|
||||
|
||||
|
@ -2718,12 +2715,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
|
||||
self.cls.addstmts([ meth, refmeth, Whitespace.NL ])
|
||||
|
||||
statemethod = MethodDefn(MethodDecl(
|
||||
p.stateMethod().name,
|
||||
ret=p.fqStateType()))
|
||||
statemethod.addstmt(StmtReturn(p.stateVar()))
|
||||
self.cls.addstmts([ statemethod, Whitespace.NL ])
|
||||
|
||||
## OnMessageReceived()/OnCallReceived()
|
||||
|
||||
# save these away for use in message handler case stmts
|
||||
|
|
|
@ -753,7 +753,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
|
|||
case FOURCC('m', 'o', 'o', 'f'):
|
||||
case FOURCC('t', 'r', 'a', 'f'):
|
||||
case FOURCC('m', 'f', 'r', 'a'):
|
||||
case FOURCC('u', 'd', 't', 'a'):
|
||||
case FOURCC('i', 'l', 's', 't'):
|
||||
case FOURCC('s', 'i', 'n', 'f'):
|
||||
case FOURCC('s', 'c', 'h', 'i'):
|
||||
|
|
|
@ -4791,6 +4791,8 @@ pref("extensions.webextensions.keepStorageOnUninstall", false);
|
|||
pref("extensions.webextensions.keepUuidOnUninstall", false);
|
||||
// Redirect basedomain used by identity api
|
||||
pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org");
|
||||
// Whether or not webextension themes are supported.
|
||||
pref("extensions.webextensions.themes.enabled", false);
|
||||
pref("extensions.webextensions.remote", false);
|
||||
|
||||
// Report Site Issue button
|
||||
|
|
|
@ -880,10 +880,6 @@ mozilla::PrintfTarget::vprint(const char* fmt, va_list ap)
|
|||
}
|
||||
}
|
||||
|
||||
// Stuff trailing NUL
|
||||
if (!emit("\0", 1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ private:
|
|||
|
||||
// Used in the implementation of Smprintf et al.
|
||||
template<typename AllocPolicy>
|
||||
class MOZ_STACK_CLASS SprintfState final : public mozilla::PrintfTarget, private AllocPolicy
|
||||
class MOZ_STACK_CLASS SprintfState final : private mozilla::PrintfTarget, private AllocPolicy
|
||||
{
|
||||
public:
|
||||
explicit SprintfState(char* base)
|
||||
|
@ -119,6 +119,12 @@ class MOZ_STACK_CLASS SprintfState final : public mozilla::PrintfTarget, private
|
|||
this->free_(mBase);
|
||||
}
|
||||
|
||||
bool vprint(const char* format, va_list ap_list) {
|
||||
// The "" here has a single \0 character, which is what we're
|
||||
// trying to append.
|
||||
return mozilla::PrintfTarget::vprint(format, ap_list) && append("", 1);
|
||||
}
|
||||
|
||||
char* release() {
|
||||
char* result = mBase;
|
||||
mBase = nullptr;
|
||||
|
|
|
@ -1240,11 +1240,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "immeta"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1676,7 +1676,7 @@ dependencies = [
|
|||
"flate2 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper_serde 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"immeta 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"immeta 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1692,6 +1692,7 @@ dependencies = [
|
|||
"serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-websocket 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo_config 0.0.1",
|
||||
"servo_url 0.0.1",
|
||||
"threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1701,7 +1702,6 @@ dependencies = [
|
|||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_traits 0.22.0 (git+https://github.com/servo/webrender)",
|
||||
"websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1755,12 +1755,12 @@ dependencies = [
|
|||
"num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-websocket 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo_config 0.0.1",
|
||||
"servo_url 0.0.1",
|
||||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_traits 0.22.0 (git+https://github.com/servo/webrender)",
|
||||
"websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2285,6 +2285,7 @@ dependencies = [
|
|||
"selectors 0.18.0",
|
||||
"serde 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo-websocket 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"servo_atoms 0.0.1",
|
||||
"servo_config 0.0.1",
|
||||
"servo_geometry 0.0.1",
|
||||
|
@ -2298,7 +2299,6 @@ dependencies = [
|
|||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_traits 0.22.0 (git+https://github.com/servo/webrender)",
|
||||
"websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webvr 0.0.1",
|
||||
"webvr_traits 0.0.1",
|
||||
"xml5ever 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2530,6 +2530,22 @@ dependencies = [
|
|||
"x11 2.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-websocket"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo_atoms"
|
||||
version = "0.0.1"
|
||||
|
@ -3203,22 +3219,6 @@ dependencies = [
|
|||
"serde_derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "websocket"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webvr"
|
||||
version = "0.0.1"
|
||||
|
@ -3420,7 +3420,7 @@ dependencies = [
|
|||
"checksum hyper_serde 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d602a93073c250f49b2e2d931cc1755a5f447824154dc3c711716dee29bd7486"
|
||||
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
|
||||
"checksum image 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "979bad0502082fd60053a490282e87d6c89650942e3a270e0d4c83569c7f5899"
|
||||
"checksum immeta 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3e76ecb1d64979a91c7fc5b7c0495ef1467e3cbff759044f2b88878a5a845ef7"
|
||||
"checksum immeta 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0b9260463a221bfe3f02100c56e2d14c050d5ffe7e44a43d0a1b2b1f2b523502"
|
||||
"checksum inflate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e0062d2dc2f17d2f13750d95316ae8a2ff909af0fda957084f5defd87c43bb"
|
||||
"checksum io-surface 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10d25285115b9d34be1328fdc5af15d34174472a9f23d1994d2d14a7ec8c537a"
|
||||
"checksum ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc12beb3f43e226410d7f26a77aec73efbf0c11875a8131adc09f30a8219f22e"
|
||||
|
@ -3513,6 +3513,7 @@ dependencies = [
|
|||
"checksum servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9232032c2e85118c0282c6562c84cab12316e655491ba0a5d1905b2320060d1b"
|
||||
"checksum servo-glutin 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b682e1eef598db6048b64face7ea79fd55fe70d171cb92d2a44a89db7bdf34"
|
||||
"checksum servo-skia 0.30000003.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7107296909e71f69a7e8b95becf3efe3e1838e556430b3efc9dc91aea65ddf2"
|
||||
"checksum servo-websocket 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7445fde9aacb9a1f493652ab02ac0fb7a8bfe1e6cd762f7bd44b839a5d5e4c"
|
||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||
"checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a"
|
||||
"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
|
||||
|
@ -3569,7 +3570,6 @@ dependencies = [
|
|||
"checksum webdriver 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdc28802daddee94267a657ffeac2593a33881fb7a3a44fedd320b1319efcaf6"
|
||||
"checksum webrender 0.21.0 (git+https://github.com/servo/webrender)" = "<none>"
|
||||
"checksum webrender_traits 0.22.0 (git+https://github.com/servo/webrender)" = "<none>"
|
||||
"checksum websocket 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a1a6ea5ed0367f32eb3d94dcc58859ef4294b5f75ba983dbf56ac314af45d"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
"checksum ws 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "04614a58714f3fd4a8b1da4bcae9f031c532d35988c3d39627619248113f8be8"
|
||||
|
|
|
@ -35,13 +35,13 @@ serde_derive = "0.9"
|
|||
serde_json = "0.9"
|
||||
servo_config = {path = "../config"}
|
||||
servo_url = {path = "../url"}
|
||||
servo-websocket = "0.18"
|
||||
threadpool = "1.0"
|
||||
time = "0.1.17"
|
||||
unicase = "1.4.0"
|
||||
url = {version = "1.2", features = ["heap_size"]}
|
||||
uuid = {version = "0.4", features = ["v4"]}
|
||||
webrender_traits = {git = "https://github.com/servo/webrender", features = ["ipc"]}
|
||||
websocket = "0.17"
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
|
||||
tinyfiledialogs = "2.5.9"
|
||||
|
|
|
@ -25,7 +25,7 @@ serde = "0.9"
|
|||
serde_derive = "0.9"
|
||||
servo_config = {path = "../config", features = ["servo"]}
|
||||
servo_url = {path = "../url", features = ["servo"]}
|
||||
servo-websocket = "0.18"
|
||||
url = {version = "1.2", features = ["heap_size"]}
|
||||
uuid = {version = "0.4", features = ["v4", "serde"]}
|
||||
webrender_traits = {git = "https://github.com/servo/webrender", features = ["ipc"]}
|
||||
websocket = "0.17"
|
||||
|
|
|
@ -80,13 +80,13 @@ servo_config = {path = "../config", features = ["servo"] }
|
|||
servo_geometry = {path = "../geometry" }
|
||||
servo_rand = {path = "../rand"}
|
||||
servo_url = {path = "../url", features = ["servo"] }
|
||||
servo-websocket = "0.18"
|
||||
smallvec = "0.1"
|
||||
style = {path = "../style"}
|
||||
style_traits = {path = "../style_traits"}
|
||||
time = "0.1.12"
|
||||
url = {version = "1.2", features = ["heap_size", "query_encoding"]}
|
||||
uuid = {version = "0.4", features = ["v4"]}
|
||||
websocket = "0.17"
|
||||
xml5ever = {version = "0.4", features = ["unstable"]}
|
||||
webrender_traits = {git = "https://github.com/servo/webrender", features = ["ipc"]}
|
||||
webvr = {path = "../webvr"}
|
||||
|
|
|
@ -21,10 +21,10 @@ rand = [
|
|||
"phf_generator",
|
||||
"rayon",
|
||||
"servo_rand",
|
||||
"servo-websocket",
|
||||
"tempdir",
|
||||
"tempfile",
|
||||
"uuid",
|
||||
"websocket",
|
||||
"ws",
|
||||
]
|
||||
num = []
|
||||
|
|
|
@ -16,6 +16,7 @@ config = {
|
|||
"MOZ_MAKE_COMPLETE_MAR": "1",
|
||||
'TOOLTOOL_CACHE': '/builds/tooltool_cache',
|
||||
'TOOLTOOL_HOME': '/builds',
|
||||
'EN_US_PACKAGE_NAME': 'target.tar.bz2',
|
||||
},
|
||||
"ssh_key_dir": "/home/mock_mozilla/.ssh",
|
||||
"log_name": "single_locale",
|
||||
|
|
|
@ -16,6 +16,7 @@ config = {
|
|||
"MOZ_MAKE_COMPLETE_MAR": "1",
|
||||
'TOOLTOOL_CACHE': '/builds/tooltool_cache',
|
||||
'TOOLTOOL_HOME': '/builds',
|
||||
'EN_US_PACKAGE_NAME': 'target.tar.bz2',
|
||||
},
|
||||
"ssh_key_dir": "/home/mock_mozilla/.ssh",
|
||||
"log_name": "single_locale",
|
||||
|
|
|
@ -113,20 +113,31 @@ function WebNavigationEventManager(context, eventName) {
|
|||
let data2 = {
|
||||
url: data.url,
|
||||
timeStamp: Date.now(),
|
||||
frameId: ExtensionManagement.getFrameId(data.windowId),
|
||||
parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId),
|
||||
};
|
||||
|
||||
if (eventName == "onErrorOccurred") {
|
||||
data2.error = data.error;
|
||||
}
|
||||
|
||||
if (data.windowId) {
|
||||
data2.frameId = ExtensionManagement.getFrameId(data.windowId);
|
||||
data2.parentFrameId = ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId);
|
||||
}
|
||||
|
||||
if (data.sourceWindowId) {
|
||||
data2.sourceFrameId = ExtensionManagement.getFrameId(data.sourceWindowId);
|
||||
}
|
||||
|
||||
// Fills in tabId typically.
|
||||
Object.assign(data2, tabTracker.getBrowserData(data.browser));
|
||||
if (data2.tabId < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.sourceTabBrowser) {
|
||||
data2.sourceTabId = tabTracker.getBrowserData(data.sourceTabBrowser).tabId;
|
||||
}
|
||||
|
||||
fillTransitionProperties(eventName, data, data2);
|
||||
|
||||
fire.async(data2);
|
||||
|
@ -166,7 +177,7 @@ extensions.registerSchemaAPI("webNavigation", "addon_parent", context => {
|
|||
onErrorOccurred: new WebNavigationEventManager(context, "onErrorOccurred").api(),
|
||||
onReferenceFragmentUpdated: new WebNavigationEventManager(context, "onReferenceFragmentUpdated").api(),
|
||||
onHistoryStateUpdated: new WebNavigationEventManager(context, "onHistoryStateUpdated").api(),
|
||||
onCreatedNavigationTarget: ignoreEvent(context, "webNavigation.onCreatedNavigationTarget"),
|
||||
onCreatedNavigationTarget: new WebNavigationEventManager(context, "onCreatedNavigationTarget").api(),
|
||||
getAllFrames(details) {
|
||||
let tab = tabManager.get(details.tabId);
|
||||
|
||||
|
|
|
@ -4,33 +4,34 @@ category webextension-scripts backgroundPage chrome://extensions/content/ext-bac
|
|||
category webextension-scripts contextualIdentities chrome://extensions/content/ext-contextualIdentities.js
|
||||
category webextension-scripts cookies chrome://extensions/content/ext-cookies.js
|
||||
category webextension-scripts downloads chrome://extensions/content/ext-downloads.js
|
||||
category webextension-scripts extension chrome://extensions/content/ext-extension.js
|
||||
category webextension-scripts geolocation chrome://extensions/content/ext-geolocation.js
|
||||
category webextension-scripts management chrome://extensions/content/ext-management.js
|
||||
category webextension-scripts notifications chrome://extensions/content/ext-notifications.js
|
||||
category webextension-scripts handlers chrome://extensions/content/ext-protocolHandlers.js
|
||||
category webextension-scripts i18n chrome://extensions/content/ext-i18n.js
|
||||
category webextension-scripts idle chrome://extensions/content/ext-idle.js
|
||||
category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
|
||||
category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
|
||||
category webextension-scripts handlers chrome://extensions/content/ext-protocolHandlers.js
|
||||
category webextension-scripts runtime chrome://extensions/content/ext-runtime.js
|
||||
category webextension-scripts extension chrome://extensions/content/ext-extension.js
|
||||
category webextension-scripts storage chrome://extensions/content/ext-storage.js
|
||||
category webextension-scripts topSites chrome://extensions/content/ext-topSites.js
|
||||
category webextension-scripts management chrome://extensions/content/ext-management.js
|
||||
category webextension-scripts notifications chrome://extensions/content/ext-notifications.js
|
||||
category webextension-scripts privacy chrome://extensions/content/ext-privacy.js
|
||||
category webextension-scripts runtime chrome://extensions/content/ext-runtime.js
|
||||
category webextension-scripts storage chrome://extensions/content/ext-storage.js
|
||||
category webextension-scripts theme chrome://extensions/content/ext-theme.js
|
||||
category webextension-scripts topSites chrome://extensions/content/ext-topSites.js
|
||||
category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
|
||||
category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
|
||||
|
||||
# scripts specific for content process.
|
||||
category webextension-scripts-content extension chrome://extensions/content/ext-c-extension.js
|
||||
category webextension-scripts-content i18n chrome://extensions/content/ext-i18n.js
|
||||
category webextension-scripts-content runtime chrome://extensions/content/ext-c-runtime.js
|
||||
category webextension-scripts-content test chrome://extensions/content/ext-c-test.js
|
||||
category webextension-scripts-content storage chrome://extensions/content/ext-c-storage.js
|
||||
category webextension-scripts-content test chrome://extensions/content/ext-c-test.js
|
||||
|
||||
# scripts specific for devtools extension contexts.
|
||||
category webextension-scripts-devtools extension chrome://extensions/content/ext-c-extension.js
|
||||
category webextension-scripts-devtools i18n chrome://extensions/content/ext-i18n.js
|
||||
category webextension-scripts-devtools runtime chrome://extensions/content/ext-c-runtime.js
|
||||
category webextension-scripts-devtools test chrome://extensions/content/ext-c-test.js
|
||||
category webextension-scripts-devtools storage chrome://extensions/content/ext-c-storage.js
|
||||
category webextension-scripts-devtools test chrome://extensions/content/ext-c-test.js
|
||||
|
||||
# scripts that must run in the same process as addon code.
|
||||
category webextension-scripts-addon backgroundPage chrome://extensions/content/ext-c-backgroundPage.js
|
||||
|
@ -40,8 +41,8 @@ category webextension-scripts-addon i18n chrome://extensions/content/ext-i18n.js
|
|||
category webextension-scripts-addon identity chrome://extensions/content/ext-c-identity.js
|
||||
#endif
|
||||
category webextension-scripts-addon runtime chrome://extensions/content/ext-c-runtime.js
|
||||
category webextension-scripts-addon test chrome://extensions/content/ext-c-test.js
|
||||
category webextension-scripts-addon storage chrome://extensions/content/ext-c-storage.js
|
||||
category webextension-scripts-addon test chrome://extensions/content/ext-c-test.js
|
||||
|
||||
# schemas
|
||||
category webextension-schemas alarms chrome://extensions/content/schemas/alarms.json
|
||||
|
@ -64,6 +65,7 @@ category webextension-schemas privacy chrome://extensions/content/schemas/privac
|
|||
category webextension-schemas runtime chrome://extensions/content/schemas/runtime.json
|
||||
category webextension-schemas storage chrome://extensions/content/schemas/storage.json
|
||||
category webextension-schemas test chrome://extensions/content/schemas/test.json
|
||||
category webextension-schemas theme chrome://extensions/content/schemas/theme.json
|
||||
category webextension-schemas top_sites chrome://extensions/content/schemas/top_sites.json
|
||||
category webextension-schemas types chrome://extensions/content/schemas/types.json
|
||||
category webextension-schemas web_navigation chrome://extensions/content/schemas/web_navigation.json
|
||||
|
|
|
@ -10,19 +10,22 @@ toolkit.jar:
|
|||
content/extensions/ext-contextualIdentities.js
|
||||
content/extensions/ext-cookies.js
|
||||
content/extensions/ext-downloads.js
|
||||
content/extensions/ext-extension.js
|
||||
content/extensions/ext-geolocation.js
|
||||
content/extensions/ext-management.js
|
||||
content/extensions/ext-notifications.js
|
||||
content/extensions/ext-i18n.js
|
||||
content/extensions/ext-idle.js
|
||||
content/extensions/ext-webRequest.js
|
||||
content/extensions/ext-webNavigation.js
|
||||
content/extensions/ext-management.js
|
||||
content/extensions/ext-notifications.js
|
||||
content/extensions/ext-privacy.js
|
||||
content/extensions/ext-protocolHandlers.js
|
||||
content/extensions/ext-runtime.js
|
||||
content/extensions/ext-extension.js
|
||||
content/extensions/ext-storage.js
|
||||
content/extensions/ext-theme.js
|
||||
content/extensions/ext-topSites.js
|
||||
content/extensions/ext-privacy.js
|
||||
content/extensions/ext-webRequest.js
|
||||
content/extensions/ext-webNavigation.js
|
||||
# Below is a separate group using the naming convention ext-c-*.js that run
|
||||
# in the child process.
|
||||
content/extensions/ext-c-backgroundPage.js
|
||||
content/extensions/ext-c-extension.js
|
||||
#ifndef ANDROID
|
||||
|
|
|
@ -37,6 +37,10 @@ DIRS += ['schemas']
|
|||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser/browser.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest/mochitest-remote.ini',
|
||||
'test/mochitest/mochitest.ini'
|
||||
|
|
|
@ -26,6 +26,7 @@ toolkit.jar:
|
|||
content/extensions/schemas/runtime.json
|
||||
content/extensions/schemas/storage.json
|
||||
content/extensions/schemas/test.json
|
||||
content/extensions/schemas/theme.json
|
||||
content/extensions/schemas/top_sites.json
|
||||
content/extensions/schemas/types.json
|
||||
content/extensions/schemas/web_navigation.json
|
||||
|
|
|
@ -284,7 +284,6 @@
|
|||
},
|
||||
{
|
||||
"name": "onCreatedNavigationTarget",
|
||||
"unsupported": true,
|
||||
"type": "function",
|
||||
"description": "Fired when a new window, or a new tab in an existing window, is created to host a navigation.",
|
||||
"parameters": [
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = { // eslint-disable-line no-undef
|
||||
"extends": "../../../../../testing/mochitest/mochitest.eslintrc.js",
|
||||
|
||||
"env": {
|
||||
"webextensions": true,
|
||||
},
|
||||
|
||||
"globals": {
|
||||
"ExtensionTestUtils": false,
|
||||
"XPCOMUtils": true,
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"no-shadow": 0,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
[browser_ext_themes_chromeparity.js]
|
||||
[browser_ext_themes_dynamic_updates.js]
|
||||
[browser_ext_themes_lwtsupport.js]
|
|
@ -377,8 +377,9 @@ this.ReaderMode = {
|
|||
},
|
||||
|
||||
_blockedHosts: [
|
||||
"mail.google.com",
|
||||
"amazon.com",
|
||||
"github.com",
|
||||
"mail.google.com",
|
||||
"pinterest.com",
|
||||
"reddit.com",
|
||||
"twitter.com",
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "nsDocShell.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
#include "nsIDocShell.h"
|
||||
|
@ -1209,6 +1210,29 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
|
|||
// userContextId.
|
||||
MOZ_ASSERT(CheckUserContextCompatibility(newDocShell));
|
||||
|
||||
// If this tab or window has been opened by a window.open call, we have to provide
|
||||
// all the data needed to send a webNavigation.onCreatedNavigationTarget event.
|
||||
if (aCalledFromJS && parentDocShell && newDocShellItem) {
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
if (obsSvc) {
|
||||
RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
|
||||
|
||||
if (uriToLoad) {
|
||||
// The url notified in the webNavigation.onCreatedNavigationTarget event.
|
||||
props->SetPropertyAsACString(NS_LITERAL_STRING("url"),
|
||||
uriToLoad->GetSpecOrDefault());
|
||||
}
|
||||
|
||||
props->SetPropertyAsInterface(NS_LITERAL_STRING("sourceTabDocShell"), parentDocShell);
|
||||
props->SetPropertyAsInterface(NS_LITERAL_STRING("createdTabDocShell"), newDocShellItem);
|
||||
|
||||
obsSvc->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
|
||||
"webNavigation-createdNavigationTarget-from-js", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (uriToLoad && aNavigate) {
|
||||
newDocShell->LoadURI(
|
||||
uriToLoad,
|
||||
|
|
|
@ -1,19 +1,5 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html";
|
||||
|
||||
function* wait_for_tab_playing_event(tab, expectPlaying) {
|
||||
if (tab.soundPlaying == expectPlaying) {
|
||||
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
} else {
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function play_audio_from_invisible_tab() {
|
||||
return new Promise(resolve => {
|
||||
var autoPlay = content.document.getElementById("autoplay");
|
||||
|
@ -73,13 +59,13 @@ add_task(function* cross_tabs_audio_competing() {
|
|||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
tab1.linkedBrowser.loadURI(PAGE);
|
||||
yield wait_for_tab_playing_event(tab1, true);
|
||||
yield waitForTabPlayingEvent(tab1, true);
|
||||
|
||||
info("- open tab 2 in foreground -");
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
tab2.linkedBrowser.loadURI(PAGE);
|
||||
yield wait_for_tab_playing_event(tab1, false);
|
||||
yield waitForTabPlayingEvent(tab1, false);
|
||||
|
||||
info("- open tab 3 in foreground -");
|
||||
let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
|
@ -102,7 +88,7 @@ add_task(function* within_one_tab_audio_competing() {
|
|||
let tab = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
tab.linkedBrowser.loadURI(PAGE);
|
||||
yield wait_for_tab_playing_event(tab, true);
|
||||
yield waitForTabPlayingEvent(tab, true);
|
||||
|
||||
info("- play audio2 in the same tab -");
|
||||
yield ContentTask.spawn(tab.linkedBrowser, null,
|
||||
|
|
|
@ -7,20 +7,6 @@ var SuspendedType = {
|
|||
SUSPENDED_PAUSE_DISPOSABLE : 3
|
||||
};
|
||||
|
||||
function* wait_for_tab_playing_event(tab, expectPlaying) {
|
||||
if (tab.soundPlaying == expectPlaying) {
|
||||
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
} else {
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function check_audio_suspended(suspendedType) {
|
||||
var autoPlay = content.document.getElementById("autoplay");
|
||||
if (!autoPlay) {
|
||||
|
@ -61,7 +47,7 @@ add_task(function* block_autoplay_media() {
|
|||
yield BrowserTestUtils.switchTab(window.gBrowser, tab1);
|
||||
|
||||
info("- media should be unblocked because the tab was visited -");
|
||||
yield wait_for_tab_playing_event(tab1, true);
|
||||
yield waitForTabPlayingEvent(tab1, true);
|
||||
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
|
@ -69,12 +55,12 @@ add_task(function* block_autoplay_media() {
|
|||
let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
info("- should still play media from tab1 -");
|
||||
yield wait_for_tab_playing_event(tab1, true);
|
||||
yield waitForTabPlayingEvent(tab1, true);
|
||||
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- should still block media from tab2 -");
|
||||
yield wait_for_tab_playing_event(tab2, false);
|
||||
yield waitForTabPlayingEvent(tab2, false);
|
||||
yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
|
||||
check_audio_suspended);
|
||||
|
||||
|
|
|
@ -64,14 +64,15 @@ add_task(function* block_autoplay_media() {
|
|||
info("- the tab1 should not be blocked -");
|
||||
yield waitForTabBlockEvent(tab1, false);
|
||||
|
||||
info("- select tab2 as foreground tab, and tab2's media should be playing -");
|
||||
info("- select tab2 as foreground tab, and the tab2 should not be blocked -");
|
||||
yield BrowserTestUtils.switchTab(window.gBrowser, tab2);
|
||||
yield waitForTabBlockEvent(tab2, false);
|
||||
|
||||
info("- tab2's media should be playing -");
|
||||
yield waitForTabPlayingEvent(tab2, true);
|
||||
yield ContentTask.spawn(tab2.linkedBrowser, false,
|
||||
check_audio_pause_state);
|
||||
|
||||
info("- the tab2 should not be blocked -");
|
||||
yield waitForTabBlockEvent(tab2, false);
|
||||
|
||||
info("- check tab2's media suspend type -");
|
||||
yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
|
|
@ -7,21 +7,6 @@ var SuspendedType = {
|
|||
SUSPENDED_PAUSE_DISPOSABLE : 3
|
||||
};
|
||||
|
||||
function* wait_for_tab_playing_event(tab, expectPlaying) {
|
||||
if (tab.soundPlaying == expectPlaying) {
|
||||
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
} else {
|
||||
info("Playing state doens't match, wait for attributes changes.");
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function disable_non_test_mouse(disable) {
|
||||
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
@ -97,7 +82,7 @@ add_task(function* unblock_icon_should_disapear_after_resume_tab() {
|
|||
yield waitForTabBlockEvent(tab, false);
|
||||
|
||||
info("- should not display sound indicator icon -");
|
||||
yield wait_for_tab_playing_event(tab, false);
|
||||
yield waitForTabPlayingEvent(tab, false);
|
||||
|
||||
info("- remove tab -");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
@ -127,7 +112,7 @@ add_task(function* should_not_show_sound_indicator_after_resume_tab() {
|
|||
yield waitForTabBlockEvent(tab, false);
|
||||
|
||||
info("- should not display sound indicator icon -");
|
||||
yield wait_for_tab_playing_event(tab, false);
|
||||
yield waitForTabPlayingEvent(tab, false);
|
||||
|
||||
info("- remove tab -");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta content="utf-8" http-equiv="encoding">
|
||||
</head>
|
||||
<body>
|
||||
<audio id="testAudio" src="audio.ogg"></audio>
|
||||
<audio id="testAudio" src="audio.ogg" loop></audio>
|
||||
<script type="text/javascript">
|
||||
|
||||
var audio = document.getElementById("testAudio");
|
||||
|
|
|
@ -49,3 +49,21 @@ function* waitForTabBlockEvent(tab, expectBlocked) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check whether the tab has soundplaying attribute.
|
||||
*/
|
||||
function* waitForTabPlayingEvent(tab, expectPlaying) {
|
||||
if (tab.soundPlaying == expectPlaying) {
|
||||
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
} else {
|
||||
info("Playing state doens't match, wait for attributes changes.");
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
|||
// e.g. nsNavHistory::CheckIsRecentEvent, but with a lower threshold value).
|
||||
const RECENT_DATA_THRESHOLD = 5 * 1000000;
|
||||
|
||||
// TODO:
|
||||
// onCreatedNavigationTarget
|
||||
|
||||
var Manager = {
|
||||
// Map[string -> Map[listener -> URLFilter]]
|
||||
listeners: new Map(),
|
||||
|
@ -32,13 +29,22 @@ var Manager = {
|
|||
// Collect recent tab transition data in a WeakMap:
|
||||
// browser -> tabTransitionData
|
||||
this.recentTabTransitionData = new WeakMap();
|
||||
|
||||
// Collect the pending created navigation target events that still have to
|
||||
// pair the message received from the source tab to the one received from
|
||||
// the new tab.
|
||||
this.createdNavigationTargetByOuterWindowId = new Map();
|
||||
|
||||
Services.obs.addObserver(this, "autocomplete-did-enter-text", true);
|
||||
|
||||
Services.obs.addObserver(this, "webNavigation-createdNavigationTarget", false);
|
||||
|
||||
Services.mm.addMessageListener("Content:Click", this);
|
||||
Services.mm.addMessageListener("Extension:DOMContentLoaded", this);
|
||||
Services.mm.addMessageListener("Extension:StateChange", this);
|
||||
Services.mm.addMessageListener("Extension:DocumentChange", this);
|
||||
Services.mm.addMessageListener("Extension:HistoryChange", this);
|
||||
Services.mm.addMessageListener("Extension:CreatedNavigationTarget", this);
|
||||
|
||||
Services.mm.loadFrameScript("resource://gre/modules/WebNavigationContent.js", true);
|
||||
},
|
||||
|
@ -46,16 +52,20 @@ var Manager = {
|
|||
uninit() {
|
||||
// Stop collecting recent tab transition data and reset the WeakMap.
|
||||
Services.obs.removeObserver(this, "autocomplete-did-enter-text");
|
||||
this.recentTabTransitionData = new WeakMap();
|
||||
Services.obs.removeObserver(this, "webNavigation-createdNavigationTarget");
|
||||
|
||||
Services.mm.removeMessageListener("Content:Click", this);
|
||||
Services.mm.removeMessageListener("Extension:StateChange", this);
|
||||
Services.mm.removeMessageListener("Extension:DocumentChange", this);
|
||||
Services.mm.removeMessageListener("Extension:HistoryChange", this);
|
||||
Services.mm.removeMessageListener("Extension:DOMContentLoaded", this);
|
||||
Services.mm.removeMessageListener("Extension:CreatedNavigationTarget", this);
|
||||
|
||||
Services.mm.removeDelayedFrameScript("resource://gre/modules/WebNavigationContent.js");
|
||||
Services.mm.broadcastAsyncMessage("Extension:DisableWebNavigation");
|
||||
|
||||
this.recentTabTransitionData = new WeakMap();
|
||||
this.createdNavigationTargetByOuterWindowId.clear();
|
||||
},
|
||||
|
||||
addListener(type, listener, filters) {
|
||||
|
@ -92,16 +102,33 @@ var Manager = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
/**
|
||||
* Observe autocomplete-did-enter-text topic to track the user interaction with
|
||||
* the awesome bar.
|
||||
* Observe autocomplete-did-enter-text (to track the user interaction with the awesomebar)
|
||||
* and webNavigation-createdNavigationTarget (to fire the onCreatedNavigationTarget
|
||||
* related to windows or tabs opened from the main process) topics.
|
||||
*
|
||||
* @param {nsIAutoCompleteInput} subject
|
||||
* @param {nsIAutoCompleteInput|Object} subject
|
||||
* @param {string} topic
|
||||
* @param {string} data
|
||||
* @param {string|undefined} data
|
||||
*/
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic == "autocomplete-did-enter-text") {
|
||||
this.onURLBarAutoCompletion(subject);
|
||||
} else if (topic == "webNavigation-createdNavigationTarget") {
|
||||
// The observed notification is coming from privileged JavaScript components running
|
||||
// in the main process (e.g. when a new tab or window is opened using the context menu
|
||||
// or Ctrl/Shift + click on a link).
|
||||
const {
|
||||
createdTabBrowser,
|
||||
url,
|
||||
sourceFrameOuterWindowID,
|
||||
sourceTabBrowser,
|
||||
} = subject.wrappedJSObject;
|
||||
|
||||
this.fire("onCreatedNavigationTarget", createdTabBrowser, {}, {
|
||||
sourceTabBrowser,
|
||||
sourceWindowId: sourceFrameOuterWindowID,
|
||||
url,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -260,6 +287,10 @@ var Manager = {
|
|||
case "Content:Click":
|
||||
this.onContentClick(target, data);
|
||||
break;
|
||||
|
||||
case "Extension:CreatedNavigationTarget":
|
||||
this.onCreatedNavigationTarget(target, data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -274,6 +305,37 @@ var Manager = {
|
|||
}
|
||||
},
|
||||
|
||||
onCreatedNavigationTarget(browser, data) {
|
||||
const {isSourceTab, createdWindowId, sourceWindowId, url} = data;
|
||||
|
||||
// We are going to potentially received two message manager messages for a single
|
||||
// onCreatedNavigationTarget event that is happening in the child process,
|
||||
// we are going to use the generate uuid to pair them together.
|
||||
const pairedMessage = this.createdNavigationTargetByOuterWindowId.get(createdWindowId);
|
||||
|
||||
if (!pairedMessage) {
|
||||
this.createdNavigationTargetByOuterWindowId.set(createdWindowId, {browser, data});
|
||||
return;
|
||||
}
|
||||
|
||||
this.createdNavigationTargetByOuterWindowId.delete(createdWindowId);
|
||||
|
||||
let sourceTabBrowser;
|
||||
let createdTabBrowser;
|
||||
|
||||
if (isSourceTab) {
|
||||
sourceTabBrowser = browser;
|
||||
createdTabBrowser = pairedMessage.browser;
|
||||
} else {
|
||||
sourceTabBrowser = pairedMessage.browser;
|
||||
createdTabBrowser = browser;
|
||||
}
|
||||
|
||||
this.fire("onCreatedNavigationTarget", createdTabBrowser, {}, {
|
||||
sourceTabBrowser, sourceWindowId, url,
|
||||
});
|
||||
},
|
||||
|
||||
onStateChange(browser, data) {
|
||||
let stateFlags = data.stateFlags;
|
||||
if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
|
||||
|
@ -357,7 +419,7 @@ const EVENTS = [
|
|||
"onErrorOccurred",
|
||||
"onReferenceFragmentUpdated",
|
||||
"onHistoryStateUpdated",
|
||||
// "onCreatedNavigationTarget",
|
||||
"onCreatedNavigationTarget",
|
||||
];
|
||||
|
||||
var WebNavigation = {};
|
||||
|
|
|
@ -23,6 +23,55 @@ addMessageListener("Extension:DisableWebNavigation", () => {
|
|||
removeEventListener("DOMContentLoaded", loadListener);
|
||||
});
|
||||
|
||||
var CreatedNavigationTargetListener = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
init() {
|
||||
Services.obs.addObserver(this, "webNavigation-createdNavigationTarget-from-js", false);
|
||||
},
|
||||
uninit() {
|
||||
Services.obs.removeObserver(this, "webNavigation-createdNavigationTarget-from-js");
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
if (!(subject instanceof Ci.nsIPropertyBag2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let props = subject.QueryInterface(Ci.nsIPropertyBag2);
|
||||
|
||||
const createdDocShell = props.getPropertyAsInterface("createdTabDocShell", Ci.nsIDocShell);
|
||||
const sourceDocShell = props.getPropertyAsInterface("sourceTabDocShell", Ci.nsIDocShell);
|
||||
|
||||
const isSourceTabDescendant = WebNavigationFrames.isDescendantDocShell(sourceDocShell, docShell);
|
||||
|
||||
if (docShell !== createdDocShell && docShell !== sourceDocShell &&
|
||||
!isSourceTabDescendant) {
|
||||
// if the createdNavigationTarget is not related to this docShell
|
||||
// (this docShell is not the newly created docShell, it is not the source docShell,
|
||||
// and the source docShell is not a descendant of it)
|
||||
// there is nothing to do here and return early.
|
||||
return;
|
||||
}
|
||||
|
||||
const isSourceTab = docShell === sourceDocShell || isSourceTabDescendant;
|
||||
const sourceWindowId = WebNavigationFrames.getDocShellWindowId(sourceDocShell);
|
||||
const createdWindowId = WebNavigationFrames.getDocShellWindowId(createdDocShell);
|
||||
|
||||
let url;
|
||||
if (props.hasKey("url")) {
|
||||
url = props.getPropertyAsACString("url");
|
||||
}
|
||||
|
||||
sendAsyncMessage("Extension:CreatedNavigationTarget", {
|
||||
url,
|
||||
sourceWindowId,
|
||||
createdWindowId,
|
||||
isSourceTab,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
var FormSubmitListener = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsIFormSubmitObserver,
|
||||
|
@ -256,11 +305,13 @@ var WebProgressListener = {
|
|||
var disabled = false;
|
||||
WebProgressListener.init();
|
||||
FormSubmitListener.init();
|
||||
CreatedNavigationTargetListener.init();
|
||||
addEventListener("unload", () => {
|
||||
if (!disabled) {
|
||||
disabled = true;
|
||||
WebProgressListener.uninit();
|
||||
FormSubmitListener.uninit();
|
||||
CreatedNavigationTargetListener.uninit();
|
||||
}
|
||||
});
|
||||
addMessageListener("Extension:DisableWebNavigation", () => {
|
||||
|
@ -268,5 +319,6 @@ addMessageListener("Extension:DisableWebNavigation", () => {
|
|||
disabled = true;
|
||||
WebProgressListener.uninit();
|
||||
FormSubmitListener.uninit();
|
||||
CreatedNavigationTargetListener.uninit();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -20,6 +20,17 @@ function getParentWindowId(window) {
|
|||
return getWindowId(window.parent);
|
||||
}
|
||||
|
||||
function getDocShellWindowId(docShell) {
|
||||
if (!docShell) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the DOMWindow associated to the docShell passed as parameter.
|
||||
*
|
||||
|
@ -118,8 +129,14 @@ function findDocShell(frameId, rootDocShell) {
|
|||
return null;
|
||||
}
|
||||
|
||||
function isDescendantDocShell(targetDocShell, rootDocShell) {
|
||||
return (rootDocShell === targetDocShell.sameTypeRootTreeItem
|
||||
.QueryInterface(Ci.nsIDocShell));
|
||||
}
|
||||
|
||||
var WebNavigationFrames = {
|
||||
iterateDocShellTree,
|
||||
isDescendantDocShell,
|
||||
|
||||
findDocShell,
|
||||
|
||||
|
@ -139,4 +156,5 @@ var WebNavigationFrames = {
|
|||
|
||||
getWindowId,
|
||||
getParentWindowId,
|
||||
getDocShellWindowId,
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ const PREF_LWTHEME_TO_SELECT = "extensions.lwThemeToSelect";
|
|||
const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
|
||||
const PREF_EM_DSS_ENABLED = "extensions.dss.enabled";
|
||||
const ADDON_TYPE = "theme";
|
||||
const ADDON_TYPE_WEBEXT = "webextension-theme";
|
||||
|
||||
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
|
||||
|
||||
|
@ -354,7 +355,7 @@ this.LightweightThemeManager = {
|
|||
* restart
|
||||
*/
|
||||
addonChanged(aId, aType, aPendingRestart) {
|
||||
if (aType != ADDON_TYPE)
|
||||
if (aType != ADDON_TYPE && aType != ADDON_TYPE_WEBEXT)
|
||||
return;
|
||||
|
||||
let id = _getInternalID(aId);
|
||||
|
|
|
@ -951,7 +951,7 @@ Object.defineProperty(this, "isAddonPartOfE10SRollout", {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (policy.webextensions && aAddon.type == "webextension") {
|
||||
if (policy.webextensions && (aAddon.type == "webextension" || aAddon.type == "webextension-theme")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -242,8 +242,9 @@ if (!AppConstants.RELEASE_OR_BETA)
|
|||
// Some add-on types that we track internally are presented as other types
|
||||
// externally
|
||||
const TYPE_ALIASES = {
|
||||
"webextension": "extension",
|
||||
"apiextension": "extension",
|
||||
"webextension": "extension",
|
||||
"webextension-theme": "theme",
|
||||
};
|
||||
|
||||
const CHROME_TYPES = new Set([
|
||||
|
@ -253,18 +254,20 @@ const CHROME_TYPES = new Set([
|
|||
]);
|
||||
|
||||
const RESTARTLESS_TYPES = new Set([
|
||||
"webextension",
|
||||
"apiextension",
|
||||
"dictionary",
|
||||
"experiment",
|
||||
"locale",
|
||||
"apiextension",
|
||||
"webextension",
|
||||
"webextension-theme",
|
||||
]);
|
||||
|
||||
const SIGNED_TYPES = new Set([
|
||||
"webextension",
|
||||
"apiextension",
|
||||
"extension",
|
||||
"experiment",
|
||||
"apiextension",
|
||||
"webextension",
|
||||
"webextension-theme",
|
||||
]);
|
||||
|
||||
// This is a random number array that can be used as "salt" when generating
|
||||
|
@ -400,6 +403,31 @@ function addonMap(addons) {
|
|||
return new Map(addons.map(a => [a.id, a]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that determines whether an addon of a certain type is a
|
||||
* WebExtension.
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isWebExtension(type) {
|
||||
return type == "webextension" || type == "webextension-theme";
|
||||
}
|
||||
|
||||
var gThemeAliases = null;
|
||||
/**
|
||||
* Helper function that determines whether an addon of a certain type is a
|
||||
* theme.
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isTheme(type) {
|
||||
if (!gThemeAliases)
|
||||
gThemeAliases = getAllAliasesForTypes(["theme"]);
|
||||
return gThemeAliases.includes(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets permissions on a file
|
||||
*
|
||||
|
@ -736,6 +764,9 @@ function isUsableAddon(aAddon) {
|
|||
|
||||
if (mustSign(aAddon.type) && !aAddon.isCorrectlySigned) {
|
||||
logger.warn(`Add-on ${aAddon.id} is not correctly signed.`);
|
||||
if (Preferences.get(PREF_XPI_SIGNATURES_DEV_ROOT, false)) {
|
||||
logger.warn(`Preference ${PREF_XPI_SIGNATURES_DEV_ROOT} is set.`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -933,6 +964,7 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) {
|
|||
let extension = new ExtensionData(uri);
|
||||
|
||||
let manifest = yield extension.readManifest();
|
||||
let theme = !!manifest.theme;
|
||||
|
||||
// Read the list of available locales, and pre-load messages for
|
||||
// all locales.
|
||||
|
@ -956,7 +988,7 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) {
|
|||
let addon = new AddonInternal();
|
||||
addon.id = bss.id;
|
||||
addon.version = manifest.version;
|
||||
addon.type = "webextension";
|
||||
addon.type = "webextension" + (theme ? "-theme" : "");
|
||||
addon.unpack = false;
|
||||
addon.strictCompatibility = true;
|
||||
addon.bootstrap = true;
|
||||
|
@ -1033,7 +1065,8 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) {
|
|||
}];
|
||||
|
||||
addon.targetPlatforms = [];
|
||||
addon.userDisabled = false;
|
||||
// Themes are disabled by default, except when they're installed from a web page.
|
||||
addon.userDisabled = theme;
|
||||
addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;
|
||||
|
||||
return addon;
|
||||
|
@ -1295,7 +1328,7 @@ let loadManifestFromRDF = Task.async(function*(aUri, aStream) {
|
|||
// A theme's userDisabled value is true if the theme is not the selected skin
|
||||
// or if there is an active lightweight theme. We ignore whether softblocking
|
||||
// is in effect since it would change the active theme.
|
||||
if (addon.type == "theme") {
|
||||
if (isTheme(addon.type)) {
|
||||
addon.userDisabled = !!LightweightThemeManager.currentTheme ||
|
||||
addon.internalName != XPIProvider.selectedSkin;
|
||||
} else if (addon.type == "experiment") {
|
||||
|
@ -4095,6 +4128,10 @@ this.XPIProvider = {
|
|||
false);
|
||||
AddonManagerPrivate.callAddonListeners("onInstalled", addon.wrapper);
|
||||
|
||||
// Notify providers that a new theme has been enabled.
|
||||
if (isTheme(addon.type))
|
||||
AddonManagerPrivate.notifyAddonChanged(addon.id, addon.type, false);
|
||||
|
||||
return addon.wrapper;
|
||||
}),
|
||||
|
||||
|
@ -4258,7 +4295,7 @@ this.XPIProvider = {
|
|||
*/
|
||||
addonChanged(aId, aType, aPendingRestart) {
|
||||
// We only care about themes in this provider
|
||||
if (aType != "theme")
|
||||
if (!isTheme(aType))
|
||||
return;
|
||||
|
||||
if (!aId) {
|
||||
|
@ -4271,14 +4308,19 @@ this.XPIProvider = {
|
|||
// currently selected theme
|
||||
let previousTheme = null;
|
||||
let newSkin = this.defaultSkin;
|
||||
let addons = XPIDatabase.getAddonsByType("theme");
|
||||
let addons = XPIDatabase.getAddonsByType("theme", "webextension-theme");
|
||||
for (let theme of addons) {
|
||||
if (!theme.visible)
|
||||
return;
|
||||
if (theme.id == aId)
|
||||
let isChangedAddon = (theme.id == aId);
|
||||
if (isWebExtension(theme.type)) {
|
||||
if (!isChangedAddon)
|
||||
this.updateAddonDisabledState(theme, true, undefined, aPendingRestart);
|
||||
} else if (isChangedAddon) {
|
||||
newSkin = theme.internalName;
|
||||
else if (theme.userDisabled == false && !theme.pendingUninstall)
|
||||
} else if (theme.userDisabled == false && !theme.pendingUninstall) {
|
||||
previousTheme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
if (aPendingRestart) {
|
||||
|
@ -4304,7 +4346,7 @@ this.XPIProvider = {
|
|||
// Mark the previous theme as disabled. This won't cause recursion since
|
||||
// only enabled calls notifyAddonChanged.
|
||||
if (previousTheme)
|
||||
this.updateAddonDisabledState(previousTheme, true);
|
||||
this.updateAddonDisabledState(previousTheme, true, undefined, aPendingRestart);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -4440,8 +4482,9 @@ this.XPIProvider = {
|
|||
*/
|
||||
isBlockingE10s(aAddon) {
|
||||
if (aAddon.type != "extension" &&
|
||||
aAddon.type != "theme" &&
|
||||
aAddon.type != "webextension" &&
|
||||
aAddon.type != "theme")
|
||||
aAddon.type != "webextension-theme")
|
||||
return false;
|
||||
|
||||
// The hotfix is exempt
|
||||
|
@ -4513,7 +4556,13 @@ this.XPIProvider = {
|
|||
if (aAddon.active)
|
||||
return false;
|
||||
|
||||
if (aAddon.type == "theme") {
|
||||
if (isTheme(aAddon.type)) {
|
||||
if (isWebExtension(aAddon.type)) {
|
||||
// Enabling a WebExtension type theme requires a restart ONLY when the
|
||||
// theme-to-be-disabled requires a restart.
|
||||
let theme = XPIDatabase.getVisibleAddonForInternalName(this.currentSkin);
|
||||
return !theme || this.disableRequiresRestart(theme);
|
||||
}
|
||||
// If dynamic theme switching is enabled then switching themes does not
|
||||
// require a restart
|
||||
if (Preferences.get(PREF_EM_DSS_ENABLED))
|
||||
|
@ -4728,7 +4777,7 @@ this.XPIProvider = {
|
|||
let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
|
||||
if (aType == "dictionary")
|
||||
uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
|
||||
else if (aType == "webextension")
|
||||
else if (isWebExtension(aType))
|
||||
uri = "resource://gre/modules/addons/WebExtensionBootstrap.js"
|
||||
else if (aType == "apiextension")
|
||||
uri = "resource://gre/modules/addons/APIExtensionBootstrap.js"
|
||||
|
@ -4942,13 +4991,16 @@ this.XPIProvider = {
|
|||
* @param aSoftDisabled
|
||||
* Value for the softDisabled property. If undefined the value will
|
||||
* not change. If true this will force userDisabled to be true
|
||||
* @param aPendingRestart
|
||||
* If the addon is updated whilst the disabled state of another non-
|
||||
* restartless addon is also set, we need to carry that forward.
|
||||
* @return a tri-state indicating the action taken for the add-on:
|
||||
* - undefined: The add-on did not change state
|
||||
* - true: The add-on because disabled
|
||||
* - false: The add-on became enabled
|
||||
* @throws if addon is not a DBAddonInternal
|
||||
*/
|
||||
updateAddonDisabledState(aAddon, aUserDisabled, aSoftDisabled) {
|
||||
updateAddonDisabledState(aAddon, aUserDisabled, aSoftDisabled, aPendingRestart = false) {
|
||||
if (!(aAddon.inDatabase))
|
||||
throw new Error("Can only update addon states for installed addons.");
|
||||
if (aUserDisabled !== undefined && aSoftDisabled !== undefined) {
|
||||
|
@ -5010,7 +5062,7 @@ this.XPIProvider = {
|
|||
AddonManagerPrivate.callAddonListeners("onOperationCancelled", wrapper);
|
||||
} else {
|
||||
if (isDisabled) {
|
||||
var needsRestart = this.disableRequiresRestart(aAddon);
|
||||
var needsRestart = aPendingRestart || this.disableRequiresRestart(aAddon);
|
||||
AddonManagerPrivate.callAddonListeners("onDisabling", wrapper,
|
||||
needsRestart);
|
||||
} else {
|
||||
|
@ -5067,7 +5119,7 @@ this.XPIProvider = {
|
|||
}
|
||||
|
||||
// Notify any other providers that a new theme has been enabled
|
||||
if (aAddon.type == "theme" && !isDisabled)
|
||||
if (isTheme(aAddon.type) && !isDisabled)
|
||||
AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, needsRestart);
|
||||
|
||||
return isDisabled;
|
||||
|
@ -5215,7 +5267,7 @@ this.XPIProvider = {
|
|||
}
|
||||
|
||||
// Notify any other providers that a new theme has been enabled
|
||||
if (aAddon.type == "theme" && aAddon.active)
|
||||
if (isTheme(aAddon.type) && aAddon.active)
|
||||
AddonManagerPrivate.notifyAddonChanged(null, aAddon.type, requiresRestart);
|
||||
},
|
||||
|
||||
|
@ -5254,7 +5306,7 @@ this.XPIProvider = {
|
|||
}
|
||||
|
||||
// Notify any other providers that this theme is now enabled again.
|
||||
if (aAddon.type == "theme" && aAddon.active)
|
||||
if (isTheme(aAddon.type) && aAddon.active)
|
||||
AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, false);
|
||||
}
|
||||
};
|
||||
|
@ -5528,7 +5580,7 @@ class AddonInstall {
|
|||
`Refusing to upgrade addon ${this.existingAddon.id} to different ID ${this.addon.id}`]);
|
||||
}
|
||||
|
||||
if (this.existingAddon.type == "webextension" && this.addon.type != "webextension") {
|
||||
if (isWebExtension(this.existingAddon.type) && !isWebExtension(this.addon.type)) {
|
||||
zipreader.close();
|
||||
return Promise.reject([AddonManager.ERROR_UNEXPECTED_ADDON_TYPE,
|
||||
"WebExtensions may not be upated to other extension types"]);
|
||||
|
@ -5820,6 +5872,10 @@ class AddonInstall {
|
|||
}
|
||||
XPIProvider.setTelemetry(this.addon.id, "unpacked", installedUnpacked);
|
||||
recordAddonTelemetry(this.addon);
|
||||
|
||||
// Notify providers that a new theme has been enabled.
|
||||
if (isTheme(this.addon.type) && this.addon.active)
|
||||
AddonManagerPrivate.notifyAddonChanged(this.addon.id, this.addon.type, requiresRestart);
|
||||
}
|
||||
}).bind(this)).then(null, (e) => {
|
||||
logger.warn(`Failed to install ${this.file.path} from ${this.sourceURI.spec} to ${stagedAddon.path}`, e);
|
||||
|
@ -7211,7 +7267,7 @@ AddonWrapper.prototype = {
|
|||
},
|
||||
|
||||
get isWebExtension() {
|
||||
return addonFor(this).type == "webextension";
|
||||
return isWebExtension(addonFor(this).type);
|
||||
},
|
||||
|
||||
get temporarilyInstalled() {
|
||||
|
@ -7335,7 +7391,7 @@ AddonWrapper.prototype = {
|
|||
return repositoryScreenshots;
|
||||
}
|
||||
|
||||
if (addon.type == "theme" && this.hasResource("preview.png")) {
|
||||
if (isTheme(addon.type) && this.hasResource("preview.png")) {
|
||||
let url = this.getResourceURI("preview.png").spec;
|
||||
return [new AddonManagerPrivate.AddonScreenshot(url)];
|
||||
}
|
||||
|
@ -7475,11 +7531,13 @@ AddonWrapper.prototype = {
|
|||
}
|
||||
|
||||
if (addon.inDatabase) {
|
||||
if (addon.type == "theme" && val) {
|
||||
let theme = isTheme(addon.type)
|
||||
if (theme && val) {
|
||||
if (addon.internalName == XPIProvider.defaultSkin)
|
||||
throw new Error("Cannot disable the default theme");
|
||||
XPIProvider.enableDefaultTheme();
|
||||
} else {
|
||||
}
|
||||
if (!(theme && val) || isWebExtension(addon.type)) {
|
||||
// hidden and system add-ons should not be user disasbled,
|
||||
// as there is no UI to re-enable them.
|
||||
if (this.hidden) {
|
||||
|
@ -7504,10 +7562,12 @@ AddonWrapper.prototype = {
|
|||
|
||||
if (addon.inDatabase) {
|
||||
// When softDisabling a theme just enable the active theme
|
||||
if (addon.type == "theme" && val && !addon.userDisabled) {
|
||||
if (isTheme(addon.type) && val && !addon.userDisabled) {
|
||||
if (addon.internalName == XPIProvider.defaultSkin)
|
||||
throw new Error("Cannot disable the default theme");
|
||||
XPIProvider.enableDefaultTheme();
|
||||
if (isWebExtension(addon.type))
|
||||
XPIProvider.updateAddonDisabledState(addon, undefined, val);
|
||||
} else {
|
||||
XPIProvider.updateAddonDisabledState(addon, undefined, val);
|
||||
}
|
||||
|
|
|
@ -1133,22 +1133,24 @@ this.XPIDatabase = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Synchronously gets all add-ons of a particular type.
|
||||
* Synchronously gets all add-ons of a particular type(s).
|
||||
*
|
||||
* @param aType
|
||||
* The type of add-on to retrieve
|
||||
* @param aType, aType2, ...
|
||||
* The type(s) of add-on to retrieve
|
||||
* @return an array of DBAddonInternals
|
||||
*/
|
||||
getAddonsByType(aType) {
|
||||
getAddonsByType(...aTypes) {
|
||||
if (!this.addonDB) {
|
||||
// jank-tastic! Must synchronously load DB if the theme switches from
|
||||
// an XPI theme to a lightweight theme before the DB has loaded,
|
||||
// because we're called from sync XPIProvider.addonChanged
|
||||
logger.warn("Synchronous load of XPI database due to getAddonsByType(" + aType + ")");
|
||||
logger.warn("Synchronous load of XPI database due to getAddonsByType([" +
|
||||
aTypes.join(", ") + "])");
|
||||
AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_byType", XPIProvider.runPhase);
|
||||
this.syncLoadDB(true);
|
||||
}
|
||||
return _filterDB(this.addonDB, aAddon => (aAddon.type == aType));
|
||||
|
||||
return _filterDB(this.addonDB, aAddon => aTypes.includes(aAddon.type));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -1054,6 +1054,30 @@ const EXTENSIONS_DB = "extensions.json";
|
|||
var gExtensionsJSON = gProfD.clone();
|
||||
gExtensionsJSON.append(EXTENSIONS_DB);
|
||||
|
||||
function promiseWebExtensionStartup() {
|
||||
const {Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
return new Promise(resolve => {
|
||||
let listener = (evt, extension) => {
|
||||
Management.off("ready", listener);
|
||||
resolve(extension);
|
||||
};
|
||||
|
||||
Management.on("ready", listener);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseInstallWebExtension(aData) {
|
||||
let addonFile = createTempWebExtensionFile(aData);
|
||||
|
||||
return promiseInstallAllFiles([addonFile]).then(installs => {
|
||||
Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
|
||||
// Since themes are disabled by default, it won't start up.
|
||||
if ("theme" in aData.manifest)
|
||||
return installs[0].addon;
|
||||
return promiseWebExtensionStartup();
|
||||
});
|
||||
}
|
||||
|
||||
// By default use strict compatibility
|
||||
Services.prefs.setBoolPref("extensions.strictCompatibility", true);
|
||||
|
|
|
@ -384,8 +384,7 @@ function run_test_5() {
|
|||
"onEnabling"
|
||||
],
|
||||
"theme2@tests.mozilla.org": [
|
||||
["onDisabling", false],
|
||||
"onDisabled"
|
||||
"onDisabling"
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -395,11 +394,10 @@ function run_test_5() {
|
|||
|
||||
prepare_test({
|
||||
"2@personas.mozilla.org": [
|
||||
["onOperationCancelled", true]
|
||||
"onOperationCancelled"
|
||||
],
|
||||
"theme2@tests.mozilla.org": [
|
||||
["onEnabling", false],
|
||||
"onEnabled"
|
||||
"onOperationCancelled"
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -446,8 +444,7 @@ function run_test_6() {
|
|||
"onEnabling",
|
||||
],
|
||||
"theme2@tests.mozilla.org": [
|
||||
["onDisabling", false],
|
||||
"onDisabled"
|
||||
"onDisabling"
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -462,8 +459,7 @@ function run_test_6() {
|
|||
"onOperationCancelled",
|
||||
],
|
||||
"theme2@tests.mozilla.org": [
|
||||
["onEnabling", false],
|
||||
"onEnabled"
|
||||
"onOperationCancelled"
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -476,8 +472,7 @@ function run_test_6() {
|
|||
"onEnabling",
|
||||
],
|
||||
"theme2@tests.mozilla.org": [
|
||||
["onDisabling", false],
|
||||
"onDisabled"
|
||||
"onDisabling"
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -488,9 +483,9 @@ function run_test_6() {
|
|||
do_check_false(p2.isActive);
|
||||
do_check_false(p2.userDisabled);
|
||||
do_check_true(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
|
||||
do_check_false(t2.isActive);
|
||||
do_check_true(t2.isActive);
|
||||
do_check_true(t2.userDisabled);
|
||||
do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
|
||||
do_check_true(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
|
||||
do_check_false(gLWThemeChanged);
|
||||
|
||||
do_execute_soon(check_test_6);
|
||||
|
|
|
@ -307,8 +307,40 @@ add_task(function*() {
|
|||
do_check_false(addon.appDisabled);
|
||||
do_check_true(addon.isActive);
|
||||
do_check_eq(addon.type, "extension");
|
||||
do_check_true(addon.isWebExtension);
|
||||
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
|
||||
|
||||
// test reloading a webextension with the same name, but a different type.
|
||||
webext.remove(false);
|
||||
webext = createTempWebExtensionFile({
|
||||
manifest: {
|
||||
version: "6.0",
|
||||
name: "Test WebExtension 1 (temporary)",
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } }
|
||||
}
|
||||
});
|
||||
|
||||
yield Promise.all([
|
||||
AddonManager.installTemporaryAddon(webext),
|
||||
promiseAddonStartup(),
|
||||
]);
|
||||
addon = yield promiseAddonByID(ID);
|
||||
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.version, "6.0");
|
||||
do_check_eq(addon.name, "Test WebExtension 1 (temporary)");
|
||||
do_check_true(addon.isCompatible);
|
||||
do_check_false(addon.appDisabled);
|
||||
do_check_true(addon.isActive);
|
||||
// This is what we're really interested in:
|
||||
do_check_eq(addon.type, "theme");
|
||||
do_check_true(addon.isWebExtension);
|
||||
|
||||
restartManager();
|
||||
|
||||
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
|
||||
|
|
|
@ -351,6 +351,73 @@ add_task(function* canUndoUninstallDisabled() {
|
|||
yield promiseRestartManager();
|
||||
});
|
||||
|
||||
add_task(function* uninstallWebExtensionOffersUndo() {
|
||||
let { id: addonId } = yield promiseInstallWebExtension({
|
||||
manifest: {
|
||||
"author": "Some author",
|
||||
manifest_version: 2,
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } },
|
||||
}
|
||||
});
|
||||
|
||||
let [ t1, d ] = yield promiseAddonsByIDs([addonId, "default@tests.mozilla.org"]);
|
||||
|
||||
Assert.ok(t1, "Addon should be there");
|
||||
Assert.ok(!t1.isActive);
|
||||
Assert.ok(t1.userDisabled);
|
||||
Assert.equal(t1.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
Assert.ok(d, "Addon should be there");
|
||||
Assert.ok(d.isActive);
|
||||
Assert.ok(!d.userDisabled);
|
||||
Assert.equal(d.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
Assert.equal(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
|
||||
|
||||
prepare_test({ [addonId]: [ "onUninstalling" ] });
|
||||
t1.uninstall(true);
|
||||
ensure_test_completed();
|
||||
|
||||
Assert.ok(!t1.isActive);
|
||||
Assert.ok(t1.userDisabled);
|
||||
Assert.ok(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
|
||||
|
||||
Assert.equal(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
|
||||
|
||||
prepare_test({
|
||||
[addonId]: [
|
||||
"onOperationCancelled"
|
||||
]
|
||||
});
|
||||
t1.cancelUninstall();
|
||||
ensure_test_completed();
|
||||
|
||||
Assert.ok(!t1.isActive);
|
||||
Assert.ok(t1.userDisabled);
|
||||
Assert.equal(t1.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
yield promiseRestartManager();
|
||||
|
||||
[ t1, d ] = yield promiseAddonsByIDs([addonId, "default@tests.mozilla.org"]);
|
||||
|
||||
Assert.ok(d);
|
||||
Assert.ok(d.isActive);
|
||||
Assert.ok(!d.userDisabled);
|
||||
Assert.equal(d.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
Assert.ok(t1);
|
||||
Assert.ok(!t1.isActive);
|
||||
Assert.ok(t1.userDisabled);
|
||||
Assert.equal(t1.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
Assert.equal(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
|
||||
|
||||
t1.uninstall();
|
||||
yield promiseRestartManager();
|
||||
});
|
||||
|
||||
// Tests that uninstalling an enabled lightweight theme offers the option to undo
|
||||
add_task(function* uninstallLWTOffersUndo() {
|
||||
// skipped since lightweight themes don't support undoable uninstall yet
|
||||
|
|
|
@ -14,34 +14,14 @@ profileDir.append("extensions");
|
|||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
|
||||
startupManager();
|
||||
|
||||
const { GlobalManager, Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
function promiseAddonStartup() {
|
||||
return new Promise(resolve => {
|
||||
let listener = (evt, extension) => {
|
||||
Management.off("ready", listener);
|
||||
resolve(extension);
|
||||
};
|
||||
|
||||
Management.on("ready", listener);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseInstallWebExtension(aData) {
|
||||
let addonFile = createTempWebExtensionFile(aData);
|
||||
|
||||
return promiseInstallAllFiles([addonFile]).then(() => {
|
||||
Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
|
||||
return promiseAddonStartup();
|
||||
});
|
||||
}
|
||||
const { GlobalManager } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
add_task(function*() {
|
||||
equal(GlobalManager.extensionMap.size, 0);
|
||||
|
||||
yield Promise.all([
|
||||
promiseInstallAllFiles([do_get_addon("webextension_1")], true),
|
||||
promiseAddonStartup()
|
||||
promiseWebExtensionStartup()
|
||||
]);
|
||||
|
||||
equal(GlobalManager.extensionMap.size, 1);
|
||||
|
@ -79,7 +59,7 @@ add_task(function*() {
|
|||
equal(GlobalManager.extensionMap.size, 0);
|
||||
|
||||
startupManager();
|
||||
yield promiseAddonStartup();
|
||||
yield promiseWebExtensionStartup();
|
||||
|
||||
equal(GlobalManager.extensionMap.size, 1);
|
||||
ok(GlobalManager.extensionMap.has(ID));
|
||||
|
@ -108,7 +88,7 @@ add_task(function*() {
|
|||
equal(GlobalManager.extensionMap.size, 0);
|
||||
|
||||
addon.userDisabled = false;
|
||||
yield promiseAddonStartup();
|
||||
yield promiseWebExtensionStartup();
|
||||
|
||||
equal(GlobalManager.extensionMap.size, 1);
|
||||
ok(GlobalManager.extensionMap.has(ID));
|
||||
|
@ -135,7 +115,7 @@ add_task(function*() {
|
|||
}, profileDir);
|
||||
|
||||
startupManager();
|
||||
yield promiseAddonStartup();
|
||||
yield promiseWebExtensionStartup();
|
||||
|
||||
let addon = yield promiseAddonByID(ID);
|
||||
do_check_neq(addon, null);
|
||||
|
@ -160,7 +140,7 @@ add_task(function* test_manifest_localization() {
|
|||
const extensionId = "webextension3@tests.mozilla.org";
|
||||
|
||||
yield promiseInstallAllFiles([do_get_addon("webextension_3")], true);
|
||||
yield promiseAddonStartup();
|
||||
yield promiseWebExtensionStartup();
|
||||
|
||||
let addon = yield promiseAddonByID(extensionId);
|
||||
addon.userDisabled = true;
|
||||
|
@ -418,3 +398,48 @@ add_task(function* authorNotString() {
|
|||
addon.uninstall();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* testThemeExtension() {
|
||||
let addon = yield promiseInstallWebExtension({
|
||||
manifest: {
|
||||
"author": "Some author",
|
||||
manifest_version: 2,
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } },
|
||||
}
|
||||
});
|
||||
|
||||
addon = yield promiseAddonByID(addon.id);
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.creator, "Some author");
|
||||
do_check_eq(addon.version, "1.0");
|
||||
do_check_eq(addon.name, "Web Extension Name");
|
||||
do_check_true(addon.isCompatible);
|
||||
do_check_false(addon.appDisabled);
|
||||
do_check_false(addon.isActive);
|
||||
do_check_true(addon.userDisabled);
|
||||
do_check_false(addon.isSystem);
|
||||
do_check_eq(addon.type, "theme");
|
||||
do_check_true(addon.isWebExtension);
|
||||
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
|
||||
|
||||
addon.uninstall();
|
||||
|
||||
// Also test one without a proper 'theme' section.
|
||||
addon = yield promiseInstallWebExtension({
|
||||
manifest: {
|
||||
"author": "Some author",
|
||||
manifest_version: 2,
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
theme: null,
|
||||
}
|
||||
});
|
||||
|
||||
addon = yield promiseAddonByID(addon.id);
|
||||
do_check_eq(addon.type, "extension");
|
||||
do_check_true(addon.isWebExtension);
|
||||
|
||||
addon.uninstall();
|
||||
});
|
||||
|
|
|
@ -24,27 +24,12 @@ function promiseAddonStartup() {
|
|||
});
|
||||
}
|
||||
|
||||
// Test simple icon set parsing
|
||||
add_task(function*() {
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
16: "icon16.png",
|
||||
32: "icon32.png",
|
||||
48: "icon48.png",
|
||||
64: "icon64.png"
|
||||
}
|
||||
}, profileDir);
|
||||
function* testSimpleIconsetParsing(manifest) {
|
||||
yield promiseWriteWebManifestForExtension(manifest, profileDir);
|
||||
|
||||
yield promiseRestartManager();
|
||||
yield promiseAddonStartup();
|
||||
if (!manifest.theme)
|
||||
yield promiseAddonStartup();
|
||||
|
||||
let uri = do_get_addon_root_uri(profileDir, ID);
|
||||
|
||||
|
@ -76,7 +61,8 @@ add_task(function*() {
|
|||
|
||||
// check if icons are persisted through a restart
|
||||
yield promiseRestartManager();
|
||||
yield promiseAddonStartup();
|
||||
if (!manifest.theme)
|
||||
yield promiseAddonStartup();
|
||||
|
||||
addon = yield promiseAddonByID(ID);
|
||||
do_check_neq(addon, null);
|
||||
|
@ -86,30 +72,14 @@ add_task(function*() {
|
|||
addon.uninstall();
|
||||
|
||||
yield promiseRestartManager();
|
||||
});
|
||||
}
|
||||
|
||||
// Test AddonManager.getPreferredIconURL for retina screen sizes
|
||||
add_task(function*() {
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
32: "icon32.png",
|
||||
48: "icon48.png",
|
||||
64: "icon64.png",
|
||||
128: "icon128.png",
|
||||
256: "icon256.png"
|
||||
}
|
||||
}, profileDir);
|
||||
function* testRetinaIconsetParsing(manifest) {
|
||||
yield promiseWriteWebManifestForExtension(manifest, profileDir);
|
||||
|
||||
yield promiseRestartManager();
|
||||
yield promiseAddonStartup();
|
||||
if (!manifest.theme)
|
||||
yield promiseAddonStartup();
|
||||
|
||||
let addon = yield promiseAddonByID(ID);
|
||||
do_check_neq(addon, null);
|
||||
|
@ -132,23 +102,14 @@ add_task(function*() {
|
|||
addon.uninstall();
|
||||
|
||||
yield promiseRestartManager();
|
||||
});
|
||||
}
|
||||
|
||||
// Handles no icons gracefully
|
||||
add_task(function*() {
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
}
|
||||
}, profileDir);
|
||||
function* testNoIconsParsing(manifest) {
|
||||
yield promiseWriteWebManifestForExtension(manifest, profileDir);
|
||||
|
||||
yield promiseRestartManager();
|
||||
yield promiseAddonStartup();
|
||||
if (!manifest.theme)
|
||||
yield promiseAddonStartup();
|
||||
|
||||
let addon = yield promiseAddonByID(ID);
|
||||
do_check_neq(addon, null);
|
||||
|
@ -163,4 +124,109 @@ add_task(function*() {
|
|||
addon.uninstall();
|
||||
|
||||
yield promiseRestartManager();
|
||||
}
|
||||
|
||||
// Test simple icon set parsing
|
||||
add_task(function*() {
|
||||
yield* testSimpleIconsetParsing({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
16: "icon16.png",
|
||||
32: "icon32.png",
|
||||
48: "icon48.png",
|
||||
64: "icon64.png"
|
||||
}
|
||||
});
|
||||
|
||||
// Now for theme-type extensions too.
|
||||
yield* testSimpleIconsetParsing({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
16: "icon16.png",
|
||||
32: "icon32.png",
|
||||
48: "icon48.png",
|
||||
64: "icon64.png"
|
||||
},
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } }
|
||||
});
|
||||
});
|
||||
|
||||
// Test AddonManager.getPreferredIconURL for retina screen sizes
|
||||
add_task(function*() {
|
||||
yield* testRetinaIconsetParsing({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
32: "icon32.png",
|
||||
48: "icon48.png",
|
||||
64: "icon64.png",
|
||||
128: "icon128.png",
|
||||
256: "icon256.png"
|
||||
}
|
||||
});
|
||||
|
||||
yield* testRetinaIconsetParsing({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
32: "icon32.png",
|
||||
48: "icon48.png",
|
||||
64: "icon64.png",
|
||||
128: "icon128.png",
|
||||
256: "icon256.png"
|
||||
},
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } }
|
||||
});
|
||||
});
|
||||
|
||||
// Handles no icons gracefully
|
||||
add_task(function*() {
|
||||
yield* testNoIconsParsing({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
yield* testNoIconsParsing({
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
manifest_version: 2,
|
||||
applications: {
|
||||
gecko: {
|
||||
id: ID
|
||||
}
|
||||
},
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } }
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* This file contains test for 'theme' type WebExtension addons. Tests focus mostly
|
||||
* on interoperability between the different theme formats (XUL and LWT) and
|
||||
* Addon Manager integration.
|
||||
*
|
||||
* Coverage may overlap with other tests in this folder.
|
||||
*/
|
||||
|
||||
const {LightweightThemeManager} = AM_Cu.import("resource://gre/modules/LightweightThemeManager.jsm", {});
|
||||
const THEME_IDS = ["theme1@tests.mozilla.org", "theme3@tests.mozilla.org",
|
||||
"theme2@personas.mozilla.org", "default@tests.mozilla.org"];
|
||||
const REQUIRE_RESTART = { [THEME_IDS[0]]: 1 };
|
||||
const DEFAULT_THEME = THEME_IDS[3];
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
profileDir.append("extensions");
|
||||
|
||||
// We remember the last/ currently active theme for tracking events.
|
||||
var gActiveTheme = null;
|
||||
|
||||
add_task(function* setup_to_default_browserish_state() {
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: THEME_IDS[0],
|
||||
version: "1.0",
|
||||
name: "Test 1",
|
||||
type: 4,
|
||||
skinnable: true,
|
||||
internalName: "theme1/1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "2"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
yield promiseWriteWebManifestForExtension({
|
||||
author: "Some author",
|
||||
manifest_version: 2,
|
||||
name: "Web Extension Name",
|
||||
version: "1.0",
|
||||
theme: { images: { headerURL: "https://example.com/example.png" } },
|
||||
applications: {
|
||||
gecko: {
|
||||
id: THEME_IDS[1]
|
||||
}
|
||||
}
|
||||
}, profileDir);
|
||||
|
||||
// We need a default theme for some of these things to work but we have hidden
|
||||
// the one in the application directory.
|
||||
writeInstallRDFForExtension({
|
||||
id: DEFAULT_THEME,
|
||||
version: "1.0",
|
||||
name: "Default",
|
||||
internalName: "classic/1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "2"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
startupManager();
|
||||
|
||||
// We can add an LWT only after the Addon Manager was started.
|
||||
LightweightThemeManager.currentTheme = {
|
||||
id: THEME_IDS[2].substr(0, THEME_IDS[2].indexOf("@")),
|
||||
version: "1",
|
||||
name: "Bling",
|
||||
description: "SO MUCH BLING!",
|
||||
author: "Pixel Pusher",
|
||||
homepageURL: "http://mochi.test:8888/data/index.html",
|
||||
headerURL: "http://mochi.test:8888/data/header.png",
|
||||
previewURL: "http://mochi.test:8888/data/preview.png",
|
||||
iconURL: "http://mochi.test:8888/data/icon.png",
|
||||
textcolor: Math.random().toString(),
|
||||
accentcolor: Math.random().toString()
|
||||
};
|
||||
|
||||
let [ t1, t2, t3, d ] = yield promiseAddonsByIDs(THEME_IDS);
|
||||
Assert.ok(t1, "Theme addon should exist");
|
||||
Assert.ok(t2, "Theme addon should exist");
|
||||
Assert.ok(t3, "Theme addon should exist");
|
||||
Assert.ok(d, "Theme addon should exist");
|
||||
|
||||
t1.userDisabled = t2.userDisabled = t3.userDisabled = true;
|
||||
Assert.ok(!t1.isActive, "Theme should be disabled");
|
||||
Assert.ok(!t2.isActive, "Theme should be disabled");
|
||||
Assert.ok(!t3.isActive, "Theme should be disabled");
|
||||
Assert.ok(d.isActive, "Default theme should be active");
|
||||
|
||||
yield promiseRestartManager();
|
||||
|
||||
[ t1, t2, t3, d ] = yield promiseAddonsByIDs(THEME_IDS);
|
||||
Assert.ok(!t1.isActive, "Theme should still be disabled");
|
||||
Assert.ok(!t2.isActive, "Theme should still be disabled");
|
||||
Assert.ok(!t3.isActive, "Theme should still be disabled");
|
||||
Assert.ok(d.isActive, "Default theme should still be active");
|
||||
|
||||
gActiveTheme = d.id;
|
||||
});
|
||||
|
||||
/**
|
||||
* Set the `userDisabled` property of one specific theme and check if the theme
|
||||
* switching works as expected by checking the state of all installed themes.
|
||||
*
|
||||
* @param {String} which ID of the addon to set the `userDisabled` property on
|
||||
* @param {Boolean} disabled Flag value to switch to
|
||||
*/
|
||||
function* setDisabledStateAndCheck(which, disabled = false) {
|
||||
if (disabled)
|
||||
Assert.equal(which, gActiveTheme, "Only the active theme can be disabled");
|
||||
|
||||
let themeToDisable = disabled ? which : gActiveTheme;
|
||||
let themeToEnable = disabled ? DEFAULT_THEME : which;
|
||||
let expectRestart = !!(REQUIRE_RESTART[themeToDisable] || REQUIRE_RESTART[themeToEnable]);
|
||||
|
||||
let expectedStates = {
|
||||
[themeToDisable]: true,
|
||||
[themeToEnable]: false
|
||||
};
|
||||
let expectedEvents = {
|
||||
[themeToDisable]: [ [ "onDisabling", expectRestart ] ],
|
||||
[themeToEnable]: [ [ "onEnabling", expectRestart ] ]
|
||||
};
|
||||
if (!expectRestart) {
|
||||
expectedEvents[themeToDisable].push([ "onDisabled", false ]);
|
||||
expectedEvents[themeToEnable].push([ "onEnabled", false ]);
|
||||
}
|
||||
|
||||
// Set the state of the theme to change.
|
||||
let theme = yield promiseAddonByID(which);
|
||||
prepare_test(expectedEvents);
|
||||
theme.userDisabled = disabled;
|
||||
|
||||
let isDisabled;
|
||||
for (theme of yield promiseAddonsByIDs(THEME_IDS)) {
|
||||
isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
|
||||
Assert.equal(theme.userDisabled, isDisabled,
|
||||
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
||||
// Some themes need a restart to get their act together.
|
||||
if (expectRestart && (theme.id == themeToEnable || theme.id == themeToDisable)) {
|
||||
let expectedFlag = theme.id == themeToEnable ? AddonManager.PENDING_ENABLE : AddonManager.PENDING_DISABLE;
|
||||
Assert.ok(hasFlag(theme.pendingOperations, expectedFlag),
|
||||
"When expecting a restart, the pending operation flags should match");
|
||||
} else {
|
||||
Assert.equal(theme.pendingOperations, AddonManager.PENDING_NONE,
|
||||
"There should be no pending operations when no restart is expected");
|
||||
Assert.equal(theme.isActive, !isDisabled,
|
||||
`Theme '${theme.id} should be ${isDisabled ? "in" : ""}active`);
|
||||
}
|
||||
}
|
||||
|
||||
yield promiseRestartManager();
|
||||
|
||||
// All should still be good after a restart of the Addon Manager.
|
||||
for (theme of yield promiseAddonsByIDs(THEME_IDS)) {
|
||||
isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
|
||||
Assert.equal(theme.userDisabled, isDisabled,
|
||||
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
||||
Assert.equal(theme.isActive, !isDisabled,
|
||||
`Theme '${theme.id}' should be ${isDisabled ? "in" : ""}active`);
|
||||
Assert.equal(theme.pendingOperations, AddonManager.PENDING_NONE,
|
||||
"There should be no pending operations left");
|
||||
if (!isDisabled)
|
||||
gActiveTheme = theme.id;
|
||||
}
|
||||
|
||||
ensure_test_completed();
|
||||
}
|
||||
|
||||
add_task(function* test_dss_themes() {
|
||||
// Enable the complete theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[0]);
|
||||
|
||||
// Disabling the complete theme should revert to the default theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[0], true);
|
||||
|
||||
// Enable it again.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[0]);
|
||||
|
||||
// Enabling a WebExtension theme should disable the active theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Switching back should disable the WebExtension theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[0]);
|
||||
});
|
||||
|
||||
add_task(function* test_WebExtension_themes() {
|
||||
// Enable the WebExtension theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Disabling WebExtension should revert to the default theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1], true);
|
||||
|
||||
// Enable it again.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Enabling an LWT should disable the active theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[2]);
|
||||
|
||||
// Switching back should disable the LWT.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
});
|
||||
|
||||
add_task(function* test_LWTs() {
|
||||
// Start with enabling an LWT.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[2]);
|
||||
|
||||
// Disabling LWT should revert to the default theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[2], true);
|
||||
|
||||
// Enable it again.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[2]);
|
||||
|
||||
// Enabling a WebExtension theme should disable the active theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Switching back should disable the LWT.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[2]);
|
||||
});
|
||||
|
||||
add_task(function* test_default_theme() {
|
||||
// Explicitly enable the default theme.
|
||||
yield* setDisabledStateAndCheck(DEFAULT_THEME);
|
||||
|
||||
// Swith to the WebExtension theme.
|
||||
yield* setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Enable it again.
|
||||
yield* setDisabledStateAndCheck(DEFAULT_THEME);
|
||||
});
|
|
@ -8,5 +8,7 @@ tags = addons
|
|||
|
||||
[test_webextension_paths.js]
|
||||
tags = webextensions
|
||||
[test_webextension_theme.js]
|
||||
tags = webextensions
|
||||
|
||||
[include:xpcshell-shared.ini]
|
||||
|
|
|
@ -47,7 +47,6 @@ toolbaritem > menubar {
|
|||
toolbarseparator {
|
||||
-moz-appearance: separator;
|
||||
margin : 0;
|
||||
border: 0;
|
||||
min-width: 2px;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
|
||||
toolbox {
|
||||
-moz-appearance: toolbox;
|
||||
background-color: -moz-Dialog;
|
||||
border-top: 2px solid;
|
||||
-moz-border-top-colors: ThreeDShadow ThreeDHighlight;
|
||||
}
|
||||
|
||||
/* ::::: toolbar & menubar ::::: */
|
||||
|
@ -26,14 +23,10 @@ toolbar, menubar {
|
|||
toolbar {
|
||||
min-width: 1px;
|
||||
min-height: 19px;
|
||||
border-top: 1px solid ThreeDHighlight;
|
||||
border-bottom: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
toolbar:first-child, menubar {
|
||||
min-width: 1px;
|
||||
border-bottom: 1px solid ThreeDShadow;
|
||||
border-top: 0px !important;
|
||||
}
|
||||
|
||||
/* ::::: lightweight theme ::::: */
|
||||
|
@ -42,20 +35,12 @@ menubar:-moz-lwtheme,
|
|||
toolbox:-moz-lwtheme,
|
||||
toolbar:-moz-lwtheme {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* ::::: toolbar decorations ::::: */
|
||||
|
||||
toolbarseparator {
|
||||
-moz-appearance: separator;
|
||||
border-top: 2px solid transparent;
|
||||
border-bottom: 2px solid transparent;
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
-moz-border-left-colors : transparent transparent ThreeDShadow;
|
||||
-moz-border-right-colors : transparent transparent ThreeDHighlight;
|
||||
}
|
||||
|
||||
toolbarspacer {
|
||||
|
|
|
@ -3131,6 +3131,23 @@ NativeKey::GetFollowingCharMessage(MSG& aCharMsg)
|
|||
return true;
|
||||
}
|
||||
|
||||
// When found message's wParam is 0 and its scancode is 0xFF, we may remove
|
||||
// usual char message actually. In such case, we should use the removed
|
||||
// char message.
|
||||
if (IsCharMessage(removedMsg) && !nextKeyMsg.wParam &&
|
||||
WinUtils::GetScanCode(nextKeyMsg.lParam) == 0xFF) {
|
||||
aCharMsg = removedMsg;
|
||||
MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
|
||||
("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
|
||||
"remove a char message, but the removed message was changed from "
|
||||
"the found message but the found message was odd, so, ignoring the "
|
||||
"odd found message and respecting the removed message, aCharMsg=%s, "
|
||||
"nextKeyMsg=%s, kFoundCharMsg=%s",
|
||||
this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
|
||||
ToString(kFoundCharMsg).get()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Although, we don't know when this case occurs, the scan code value
|
||||
// in lParam may be changed from 0 to something. The changed value
|
||||
// is different from the scan code of handling keydown message.
|
||||
|
|
|
@ -265,7 +265,7 @@ GetAssertBehavior()
|
|||
return gAssertBehavior;
|
||||
}
|
||||
|
||||
struct FixedBuffer : public mozilla::PrintfTarget
|
||||
struct FixedBuffer final : public mozilla::PrintfTarget
|
||||
{
|
||||
FixedBuffer() : curlen(0)
|
||||
{
|
||||
|
@ -285,11 +285,6 @@ FixedBuffer::append(const char* aBuf, size_t aLen)
|
|||
return true;
|
||||
}
|
||||
|
||||
// strip the trailing null, we add it again later
|
||||
if (aBuf[aLen - 1] == '\0') {
|
||||
--aLen;
|
||||
}
|
||||
|
||||
if (curlen + aLen >= sizeof(buffer)) {
|
||||
aLen = sizeof(buffer) - curlen - 1;
|
||||
}
|
||||
|
|
|
@ -931,11 +931,6 @@ struct MOZ_STACK_CLASS PrintfAppend_CharT : public mozilla::PrintfTarget
|
|||
return true;
|
||||
}
|
||||
|
||||
// Printf sends us the final null terminator even though we don't want it
|
||||
if (aStr[aLen - 1] == '\0') {
|
||||
--aLen;
|
||||
}
|
||||
|
||||
mString->AppendASCII(aStr, aLen);
|
||||
return true;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче