merge autoland to mozilla-central. r=merge a=merge

MozReview-Commit-ID: Fsg3B4nCNMh
This commit is contained in:
Sebastian Hengst 2017-04-08 22:45:04 +02:00
Родитель c4fdb67bca a12e4b891a
Коммит f324d0cfb7
325 изменённых файлов: 6447 добавлений и 7171 удалений

Просмотреть файл

@ -88,6 +88,7 @@ MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_A
.PHONY: repackage
tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME)
rm -rf $(dist_dest)
$(MKDIR) -p $(dist_dest)/Contents/MacOS
$(MKDIR) -p $(dist_dest)/$(LPROJ)
rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj

Просмотреть файл

@ -115,6 +115,17 @@ pref("app.update.backgroundMaxErrors", 10);
// Whether or not app updates are enabled
pref("app.update.enabled", true);
// Whether or not to use the doorhanger application update UI.
pref("app.update.doorhanger", true);
// How many times we should let downloads fail before prompting the user to
// download a fresh installer.
pref("app.update.download.promptMaxAttempts", 2);
// How many times we should let an elevation prompt fail before prompting the user to
// download a fresh installer.
pref("app.update.elevation.promptMaxAttempts", 2);
// If set to true, the Update Service will automatically download updates when
// app updates are enabled per the app.update.enabled preference and if the user
// can apply updates.
@ -123,12 +134,6 @@ pref("app.update.auto", true);
// If set to true, the Update Service will present no UI for any event.
pref("app.update.silent", false);
// If set to true, the hamburger button will show badges for update events.
#ifndef RELEASE_OR_BETA
pref("app.update.badge", true);
#else
pref("app.update.badge", false);
#endif
// app.update.badgeWaitTime is in branding section
// If set to true, the Update Service will apply updates in the background

Просмотреть файл

@ -504,10 +504,9 @@ const gExtensionsNotifications = {
let sideloaded = ExtensionsUI.sideloaded;
let updates = ExtensionsUI.updates;
if (sideloaded.size + updates.size == 0) {
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_ADDONS);
PanelUI.removeNotification("addon-alert");
} else {
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_ADDONS,
"addon-alert");
PanelUI.showBadgeOnlyNotification("addon-alert");
}
let container = document.getElementById("PanelUI-footer-addons");

Просмотреть файл

@ -537,7 +537,8 @@ var FullScreen = {
// e.g. we wouldn't want the autoscroll icon firing this event, so when the user
// toggles chrome when moving mouse to the top, it doesn't go away again.
if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
aEvent.target.localName != "tooltip" && aEvent.target.localName != "window" &&
aEvent.target.getAttribute("nopreventnavboxhide") != "true")
FullScreen._isPopupOpen = true;
else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
aEvent.target.localName != "window") {
@ -547,6 +548,10 @@ var FullScreen = {
}
},
get navToolboxHidden() {
return this._isChromeCollapsed;
},
// Autohide helpers for the context menu item
getAutohide(aItem) {
aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
@ -579,6 +584,7 @@ var FullScreen = {
}
this._isChromeCollapsed = false;
Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
},
hideNavToolbox(aAnimate = false) {
@ -602,6 +608,8 @@ var FullScreen = {
gNavToolbox.style.marginTop =
-gNavToolbox.getBoundingClientRect().height + "px";
this._isChromeCollapsed = true;
Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");
MousePosTracker.removeListener(this);
},

Просмотреть файл

@ -217,9 +217,9 @@ var gFxAccounts = {
}
}
if (showErrorBadge) {
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
} else {
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
PanelUI.removeNotification("fxa-needs-authentication");
}
}

Просмотреть файл

@ -1183,7 +1183,7 @@ toolbarpaletteitem[place="palette"][hidden] {
max-width: 10em;
}
#main-window[customizing=true] #PanelUI-update-status {
#main-window[customizing=true] .PanelUI-notification-menu-item {
display: none;
}

Просмотреть файл

@ -1515,8 +1515,6 @@ var gBrowserInit = {
gBrowserThumbnails.init();
gMenuButtonBadgeManager.init();
gMenuButtonUpdateBadge.init();
gExtensionsNotifications.init();
@ -1693,8 +1691,6 @@ var gBrowserInit = {
gMenuButtonUpdateBadge.uninit();
gMenuButtonBadgeManager.uninit();
SidebarUI.uninit();
// Now either cancel delayedStartup, or clean up the services initialized from
@ -2749,185 +2745,217 @@ function PageProxyClickHandler(aEvent) {
middleMousePaste(aEvent);
}
var gMenuButtonBadgeManager = {
BADGEID_APPUPDATE: "update",
BADGEID_DOWNLOAD: "download",
BADGEID_FXA: "fxa",
BADGEID_ADDONS: "addons",
fxaBadge: null,
downloadBadge: null,
appUpdateBadge: null,
addonsBadge: null,
init() {
PanelUI.panel.addEventListener("popupshowing", this, true);
},
uninit() {
PanelUI.panel.removeEventListener("popupshowing", this, true);
},
handleEvent(e) {
if (e.type === "popupshowing") {
this.clearBadges();
}
},
_showBadge() {
let badgeToShow = this.downloadBadge || this.appUpdateBadge || this.fxaBadge || this.addonsBadge;
if (badgeToShow) {
PanelUI.menuButton.setAttribute("badge-status", badgeToShow);
} else {
PanelUI.menuButton.removeAttribute("badge-status");
}
},
_changeBadge(badgeId, badgeStatus = null) {
if (badgeId == this.BADGEID_APPUPDATE) {
this.appUpdateBadge = badgeStatus;
} else if (badgeId == this.BADGEID_DOWNLOAD) {
this.downloadBadge = badgeStatus;
} else if (badgeId == this.BADGEID_FXA) {
this.fxaBadge = badgeStatus;
} else if (badgeId == this.BADGEID_ADDONS) {
this.addonsBadge = badgeStatus;
} else {
Cu.reportError("The badge ID '" + badgeId + "' is unknown!");
}
this._showBadge();
},
addBadge(badgeId, badgeStatus) {
if (!badgeStatus) {
Cu.reportError("badgeStatus must be defined");
return;
}
this._changeBadge(badgeId, badgeStatus);
},
removeBadge(badgeId) {
this._changeBadge(badgeId);
},
clearBadges() {
this.appUpdateBadge = null;
this.downloadBadge = null;
this.fxaBadge = null;
this._showBadge();
}
};
// Setup the hamburger button badges for updates, if enabled.
var gMenuButtonUpdateBadge = {
enabled: false,
badgeWaitTime: 0,
timer: null,
cancelObserverRegistered: false,
kTopics: [
"update-staged",
"update-downloaded",
"update-available",
"update-error",
],
timeouts: [],
get enabled() {
return Services.prefs.getBoolPref("app.update.doorhanger", false);
},
get badgeWaitTime() {
return Services.prefs.getIntPref("app.update.badgeWaitTime", 4 * 24 * 3600); // 4 days
},
init() {
this.enabled = Services.prefs.getBoolPref("app.update.badge", false);
if (this.enabled) {
this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime",
345600); // 4 days
Services.obs.addObserver(this, "update-staged", false);
Services.obs.addObserver(this, "update-downloaded", false);
this.kTopics.forEach(t => {
Services.obs.addObserver(this, t, false);
});
}
},
uninit() {
if (this.timer)
this.timer.cancel();
if (this.enabled) {
Services.obs.removeObserver(this, "update-staged");
Services.obs.removeObserver(this, "update-downloaded");
this.enabled = false;
this.kTopics.forEach(t => {
Services.obs.removeObserver(this, t);
});
}
if (this.cancelObserverRegistered) {
Services.obs.removeObserver(this, "update-canceled");
this.cancelObserverRegistered = false;
this.reset();
},
reset() {
PanelUI.removeNotification(/^update-/);
this.clearCallbacks();
},
clearCallbacks() {
this.timeouts.forEach(t => clearTimeout(t));
this.timeouts = [];
},
addTimeout(time, callback) {
this.timeouts.push(setTimeout(() => {
this.clearCallbacks();
callback();
}, time));
},
replaceReleaseNotes(update, whatsNewId) {
let whatsNewLink = document.getElementById(whatsNewId);
if (update && update.detailsURL) {
whatsNewLink.href = update.detailsURL;
} else {
whatsNewLink.href = Services.urlFormatter.formatURLPref("app.update.url.details");
}
},
onMenuPanelCommand(event) {
if (event.originalTarget.getAttribute("update-status") === "succeeded") {
// restart the app
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
requestRestart() {
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
if (!cancelQuit.data) {
Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
if (!cancelQuit.data) {
Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
}
},
openManualUpdateUrl() {
let manualUpdateUrl = Services.urlFormatter.formatURLPref("app.update.url.manual");
openUILinkIn(manualUpdateUrl, "tab");
},
showUpdateNotification(type, dismissed, mainAction) {
let action = {
callback(fromDoorhanger) {
if (fromDoorhanger) {
Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_MAIN_ACTION_DOORHANGER").add(type);
} else {
Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_MAIN_ACTION_MENU").add(type);
}
mainAction();
}
} else {
// open the page for manual update
let url = Services.urlFormatter.formatURLPref("app.update.url.manual");
openUILinkIn(url, "tab");
};
let secondaryAction = {
callback() {
Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_DISMISSED").add(type);
},
dismiss: true
};
PanelUI.showNotification("update-" + type, action, [secondaryAction], { dismissed });
Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_SHOWN").add(type);
},
showRestartNotification(dismissed) {
this.showUpdateNotification("restart", dismissed, () => gMenuButtonUpdateBadge.requestRestart());
},
showUpdateAvailableNotification(update, dismissed) {
this.replaceReleaseNotes(update, "update-available-whats-new");
this.showUpdateNotification("available", dismissed, () => {
let updateService = Cc["@mozilla.org/updates/update-service;1"]
.getService(Ci.nsIApplicationUpdateService);
updateService.downloadUpdate(update, true);
});
},
showManualUpdateNotification(update, dismissed) {
this.replaceReleaseNotes(update, "update-manual-whats-new");
this.showUpdateNotification("manual", dismissed, () => gMenuButtonUpdateBadge.openManualUpdateUrl());
},
handleUpdateError(update, status) {
switch (status) {
case "download-attempt-failed":
this.clearCallbacks();
this.showUpdateAvailableNotification(update, false);
break;
case "download-attempts-exceeded":
this.clearCallbacks();
this.showManualUpdateNotification(update, false);
break;
case "elevation-attempt-failed":
this.clearCallbacks();
this.showRestartNotification(update, false);
break;
case "elevation-attempts-exceeded":
this.clearCallbacks();
this.showManualUpdateNotification(update, false);
break;
case "check-attempts-exceeded":
case "unknown":
// Background update has failed, let's show the UI responsible for
// prompting the user to update manually.
this.clearCallbacks();
this.showManualUpdateNotification(update, false);
break;
}
},
handleUpdateStagedOrDownloaded(update, status) {
switch (status) {
case "applied":
case "pending":
case "applied-service":
case "pending-service":
case "success":
this.clearCallbacks();
let badgeWaitTimeMs = this.badgeWaitTime * 1000;
let doorhangerWaitTimeMs = update.promptWaitTime * 1000;
if (badgeWaitTimeMs < doorhangerWaitTimeMs) {
this.addTimeout(badgeWaitTimeMs, () => {
this.showRestartNotification(true);
// doorhangerWaitTimeMs is relative to when we initially received
// the event. Since we've already waited badgeWaitTimeMs, subtract
// that from doorhangerWaitTimeMs.
let remainingTime = doorhangerWaitTimeMs - badgeWaitTimeMs;
this.addTimeout(remainingTime, () => {
this.showRestartNotification(false);
});
});
} else {
this.addTimeout(doorhangerWaitTimeMs, () => {
this.showRestartNotification(false);
});
}
break;
}
},
handleUpdateAvailable(update, status) {
switch (status) {
case "show-prompt":
// If an update is available and had the showPrompt flag set, then
// show an update available doorhanger.
this.clearCallbacks();
this.showUpdateAvailableNotification(update, false);
break;
}
},
observe(subject, topic, status) {
if (topic == "update-canceled") {
this.reset();
return;
}
if (status == "failed") {
// Background update has failed, let's show the UI responsible for
// prompting the user to update manually.
this.uninit();
this.displayBadge(false);
if (!this.enabled) {
return;
}
// Give the user badgeWaitTime seconds to react before prompting.
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback(this, this.badgeWaitTime * 1000,
this.timer.TYPE_ONE_SHOT);
// The timer callback will call uninit() when it completes.
},
let update = subject && subject.QueryInterface(Ci.nsIUpdate);
notify() {
// If the update is successfully applied, or if the updater has fallen back
// to non-staged updates, add a badge to the hamburger menu to indicate an
// update will be applied once the browser restarts.
this.uninit();
this.displayBadge(true);
},
displayBadge(succeeded) {
let status = succeeded ? "succeeded" : "failed";
let badgeStatus = "update-" + status;
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, badgeStatus);
let stringId;
let updateButtonText;
if (succeeded) {
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
stringId = "appmenu.restartNeeded.description";
updateButtonText = gNavigatorBundle.getFormattedString(stringId,
[brandShortName]);
Services.obs.addObserver(this, "update-canceled", false);
this.cancelObserverRegistered = true;
} else {
stringId = "appmenu.updateFailed.description";
updateButtonText = gNavigatorBundle.getString(stringId);
switch (topic) {
case "update-available":
this.handleUpdateAvailable(update, status);
break;
case "update-staged":
case "update-downloaded":
this.handleUpdateStagedOrDownloaded(update, status);
break;
case "update-error":
this.handleUpdateError(update, status);
break;
}
let updateButton = document.getElementById("PanelUI-update-status");
updateButton.setAttribute("label", updateButtonText);
updateButton.setAttribute("update-status", status);
updateButton.hidden = false;
},
reset() {
gMenuButtonBadgeManager.removeBadge(
gMenuButtonBadgeManager.BADGEID_APPUPDATE);
let updateButton = document.getElementById("PanelUI-update-status");
updateButton.hidden = true;
this.uninit();
this.init();
}
};

Просмотреть файл

@ -12,9 +12,6 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/ContentWebRTC.jsm");
Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
@ -23,6 +20,12 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
"resource:///modules/ContentLinkHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentWebRTC",
"resource:///modules/ContentWebRTC.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SpellCheckHelper",
"resource://gre/modules/InlineSpellChecker.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "InlineSpellCheckerContent",
"resource://gre/modules/InlineSpellCheckerContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
"resource://gre/modules/LoginManagerContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginFormFactory",
@ -702,19 +705,15 @@ addEventListener("DOMWindowFocus", function(event) {
sendAsyncMessage("DOMWindowFocus", {});
}, false);
ContentWebRTC.init();
addMessageListener("rtcpeer:Allow", ContentWebRTC);
addMessageListener("rtcpeer:Deny", ContentWebRTC);
addMessageListener("webrtc:Allow", ContentWebRTC);
addMessageListener("webrtc:Deny", ContentWebRTC);
addMessageListener("webrtc:StopSharing", ContentWebRTC);
addMessageListener("webrtc:StartBrowserSharing", () => {
let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
sendAsyncMessage("webrtc:response:StartBrowserSharing", {
windowID
});
});
// We use this shim so that ContentWebRTC.jsm will not be loaded until
// it is actually needed.
var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
addMessageListener("webrtc:Allow", ContentWebRTCShim);
addMessageListener("webrtc:Deny", ContentWebRTCShim);
addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
addEventListener("pageshow", function(event) {
if (event.target == content.document) {

Просмотреть файл

@ -5614,11 +5614,12 @@
<handlers>
<handler event="underflow" phase="capturing"><![CDATA[
if (event.target != this)
if (event.originalTarget != this._scrollbox)
return;
// Ignore vertical events
if (event.detail == 0)
return; // Ignore vertical events
return;
var tabs = document.getBindingParent(this);
tabs.removeAttribute("overflow");
@ -5632,11 +5633,12 @@
tabs._positionPinnedTabs();
]]></handler>
<handler event="overflow"><![CDATA[
if (event.target != this)
if (event.originalTarget != this._scrollbox)
return;
// Ignore vertical events
if (event.detail == 0)
return; // Ignore vertical events
return;
var tabs = document.getBindingParent(this);
tabs.setAttribute("overflow", "true");
@ -6073,16 +6075,19 @@
let scrollButtonWidth = this.mTabstrip._scrollButtonDown.getBoundingClientRect().width;
let paddingStart = this.mTabstrip.scrollboxPaddingStart;
let pinnedTabWidth;
let width = 0;
for (let i = numPinned - 1; i >= 0; i--) {
let tab = this.childNodes[i];
width += tab.getBoundingClientRect().width;
if (!pinnedTabWidth) {
pinnedTabWidth = tab.getBoundingClientRect().width;
}
width += pinnedTabWidth;
tab.style.marginInlineStart = -(width + scrollButtonWidth + paddingStart) + "px";
}
this.style.paddingInlineStart = width + paddingStart + "px";
} else {
this.removeAttribute("positionpinnedtabs");

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,22 @@
[DEFAULT]
tags = appupdate
support-files =
head.js
downloadPage.html
testConstants.js
[browser_updatesBasicPrompt.js]
skip-if = asan
reason = Bug 1168003
[browser_updatesBasicPromptNoStaging.js]
[browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js]
[browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js]
[browser_updatesCompleteAndPartialPatchesWithBadSizes.js]
[browser_updatesCompletePatchApplyFailure.js]
[browser_updatesCompletePatchWithBadCompleteSize.js]
[browser_updatesDownloadFailures.js]
[browser_updatesMalformedXml.js]
[browser_updatesPartialPatchApplyFailure.js]
[browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js]
[browser_updatesPartialPatchApplyFailureWithCompleteValidationFailure.js]
[browser_updatesPartialPatchWithBadPartialSize.js]

Просмотреть файл

@ -0,0 +1,28 @@
add_task(function* testBasicPrompt() {
SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]]});
let updateParams = "showPrompt=1&promptWaitTime=0";
gUseTestUpdater = true;
// Open a new window to make sure that it doesn't get in the way
// of the notification management.
let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
yield runUpdateTest(updateParams, 1, [
{
notificationId: "update-available",
button: "button",
beforeClick() {
checkWhatsNewLink("update-available-whats-new");
}
},
{
notificationId: "update-restart",
button: "secondarybutton",
*cleanup() {
PanelUI.removeNotification(/.*/);
}
},
]);
yield BrowserTestUtils.closeWindow(extraWindow);
});

Просмотреть файл

@ -0,0 +1,22 @@
add_task(function* testBasicPromptNoStaging() {
SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_STAGING_ENABLED, false]]});
let updateParams = "showPrompt=1&promptWaitTime=0";
yield runUpdateTest(updateParams, 1, [
{
notificationId: "update-available",
button: "button",
beforeClick() {
checkWhatsNewLink("update-available-whats-new");
}
},
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
PanelUI.removeNotification(/.*/);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,13 @@
add_task(function* testCompleteAndPartialPatchesWithBadCompleteSize() {
let updateParams = "invalidCompleteSize=1&promptWaitTime=0";
yield runUpdateTest(updateParams, 1, [
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
PanelUI.removeNotification(/.*/);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,13 @@
add_task(function* testCompleteAndPartialPatchesWithBadPartialSize() {
let updateParams = "invalidPartialSize=1&promptWaitTime=0";
yield runUpdateTest(updateParams, 1, [
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
PanelUI.removeNotification(/.*/);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,33 @@
add_task(function* testCompleteAndPartialPatchesWithBadSizes() {
SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]]});
let updateParams = "invalidPartialSize=1&invalidCompleteSize=1";
yield runUpdateTest(updateParams, 1, [
{
// if we fail maxBackgroundErrors download attempts, then we want to
// first show the user an update available prompt.
notificationId: "update-available",
button: "button"
},
{
notificationId: "update-available",
button: "button"
},
{
// if we have only an invalid patch, then something's wrong and we don't
// have an automatic way to fix it, so show the manual update
// doorhanger.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new");
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,25 @@
add_task(function* testCompletePatchApplyFailure() {
let patches = getLocalPatchString("complete", null, null, null, null, null,
STATE_PENDING);
let updates = getLocalUpdateString(patches, null, null, null,
Services.appinfo.version, null);
yield runUpdateProcessingTest(updates, [
{
// if we have only an invalid patch, then something's wrong and we don't
// have an automatic way to fix it, so show the manual update
// doorhanger.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new");
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,34 @@
add_task(function* testCompletePatchWithBadCompleteSize() {
SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]]});
let updateParams = "completePatchOnly=1&invalidCompleteSize=1";
yield runUpdateTest(updateParams, 1, [
{
// if we fail maxBackgroundErrors download attempts, then we want to
// first show the user an update available prompt.
notificationId: "update-available",
button: "button"
},
{
notificationId: "update-available",
button: "button"
},
{
// if we have only an invalid patch, then something's wrong and we don't
// have an automatic way to fix it, so show the manual update
// doorhanger.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new");
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,37 @@
add_task(function* testDownloadFailures() {
const maxBackgroundErrors = 5;
SpecialPowers.pushPrefEnv({set: [
[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors],
[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]
]});
let updateParams = "badURL=1";
// Open a new window to make sure that our pref management isn't duplicated.
let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
yield runUpdateTest(updateParams, 1, [
{
// if we fail maxBackgroundErrors download attempts, then we want to
// first show the user an update available prompt.
notificationId: "update-available",
button: "button"
},
{
notificationId: "update-available",
button: "button"
},
{
notificationId: "update-manual",
button: "button",
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.");
gBrowser.removeTab(gBrowser.selectedTab);
gMenuButtonUpdateBadge.reset();
}
},
]);
yield BrowserTestUtils.closeWindow(extraWindow);
});

Просмотреть файл

@ -0,0 +1,29 @@
add_task(function* testMalformedXml() {
const updateDetailsUrl = "http://example.com/details";
const maxBackgroundErrors = 10;
SpecialPowers.pushPrefEnv({set: [
[PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors],
[PREF_APP_UPDATE_URL_DETAILS, updateDetailsUrl]
]});
let updateParams = "xmlMalformed=1";
yield runUpdateTest(updateParams, maxBackgroundErrors, [
{
// if we fail 10 check attempts, then we want to just show the user a manual update
// workflow.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new", updateDetailsUrl);
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
gMenuButtonUpdateBadge.reset();
}
},
]);
});

Просмотреть файл

@ -0,0 +1,26 @@
add_task(function* testPartialPatchApplyFailure() {
let patches = getLocalPatchString("partial", null, null, null, null, null,
STATE_PENDING);
let updates = getLocalUpdateString(patches, null, null, null,
Services.appinfo.version, null,
null, null, null, null, "false");
yield runUpdateProcessingTest(updates, [
{
// if we have only an invalid patch, then something's wrong and we don't
// have an automatic way to fix it, so show the manual update
// doorhanger.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new");
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,22 @@
add_task(function* testPartialPatchApplyFailureWithCompleteAvailable() {
let patches = getLocalPatchString("partial", null, null, null, null, null,
STATE_PENDING) +
getLocalPatchString("complete", null, null, null,
null, "false");
let promptWaitTime = "0";
let updates = getLocalUpdateString(patches, null, null, null,
Services.appinfo.version, null,
null, null, null, null, "false",
null, null, null, null, promptWaitTime);
yield runUpdateProcessingTest(updates, [
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
PanelUI.removeNotification(/.*/);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,33 @@
add_task(function* testPartialPatchApplyFailureWithCompleteValidationFailure() {
// because of the way we're simulating failure, we have to just pretend we've already
// retried.
SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 0]]});
let patches = getLocalPatchString("partial", null, null, null, null, null,
STATE_PENDING) +
getLocalPatchString("complete", null, "MD5",
null, "1234",
"false");
let updates = getLocalUpdateString(patches, null, null, null,
Services.appinfo.version, null,
null, null, null, null, "false");
yield runUpdateProcessingTest(updates, [
{
// if we have only an invalid patch, then something's wrong and we don't
// have an automatic way to fix it, so show the manual update
// doorhanger.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new");
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,33 @@
add_task(function* testPartialPatchWithBadPartialSize() {
SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]]});
let updateParams = "partialPatchOnly=1&invalidPartialSize=1";
yield runUpdateTest(updateParams, 1, [
{
// if we fail maxBackgroundErrors download attempts, then we want to
// first show the user an update available prompt.
notificationId: "update-available",
button: "button"
},
{
notificationId: "update-available",
button: "button"
},
{
// if we have only an invalid patch, then something's wrong and we don't
// have an automatic way to fix it, so show the manual update
// doorhanger.
notificationId: "update-manual",
button: "button",
beforeClick() {
checkWhatsNewLink("update-manual-whats-new");
},
*cleanup() {
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
}
},
]);
});

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>Download page</title>
<meta charset="utf-8">
</head>
<body>
<!-- just use simple.mar since we have it available and it will result in a download dialog -->
<a id="download-link" href="http://example.com/browser/browser/base/content/test/appUpdate/simple.mar" data-link-type="download">
Download
</a>
</body>
</html>

Просмотреть файл

@ -0,0 +1,360 @@
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const IS_MACOSX = ("nsILocalFileMac" in Ci);
const IS_WIN = ("@mozilla.org/windows-registry-key;1" in Cc);
const BIN_SUFFIX = (IS_WIN ? ".exe" : "");
const FILE_UPDATER_BIN = "updater" + (IS_MACOSX ? ".app" : BIN_SUFFIX);
const FILE_UPDATER_BIN_BAK = FILE_UPDATER_BIN + ".bak";
let gRembemberedPrefs = [];
const DATA_URI_SPEC = "chrome://mochitests/content/browser/browser/base/content/test/appUpdate/";
var DEBUG_AUS_TEST = true;
var gUseTestUpdater = false;
const LOG_FUNCTION = info;
/* import-globals-from testConstants.js */
Services.scriptloader.loadSubScript(DATA_URI_SPEC + "testConstants.js", this);
/* import-globals-from ../../../../../toolkit/mozapps/update/tests/data/shared.js */
Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this);
var gURLData = URL_HOST + "/" + REL_PATH_DATA;
const URL_MANUAL_UPDATE = gURLData + "downloadPage.html";
const NOTIFICATIONS = [
"update-available",
"update-manual",
"update-restart"
];
/**
* Delay for a very short period. Useful for moving the code after this
* to the back of the event loop.
*
* @return A promise which will resolve after a very short period.
*/
function delay() {
return new Promise(resolve => executeSoon(resolve));
}
/**
* Gets the update version info for the update url parameters to send to
* update.sjs.
*
* @param aAppVersion (optional)
* The application version for the update snippet. If not specified the
* current application version will be used.
* @return The url parameters for the application and platform version to send
* to update.sjs.
*/
function getVersionParams(aAppVersion) {
let appInfo = Services.appinfo;
return "&appVersion=" + (aAppVersion ? aAppVersion : appInfo.version);
}
/**
* Clean up updates list and the updates directory.
*/
function cleanUpUpdates() {
gUpdateManager.activeUpdate = null;
gUpdateManager.saveUpdates();
removeUpdateDirsAndFiles();
}
/**
* Runs a typical update test. Will set various common prefs for using the
* updater doorhanger, runs the provided list of steps, and makes sure
* everything is cleaned up afterwards.
*
* @param updateParams
* URL-encoded params which will be sent to update.sjs.
* @param checkAttempts
* How many times to check for updates. Useful for testing the UI
* for check failures.
* @param steps
* A list of test steps to perform, specifying expected doorhangers
* and additional validation/cleanup callbacks.
* @return A promise which will resolve once all of the steps have been run
* and cleanup has been performed.
*/
function runUpdateTest(updateParams, checkAttempts, steps) {
return Task.spawn(function*() {
registerCleanupFunction(() => {
gMenuButtonUpdateBadge.uninit();
gMenuButtonUpdateBadge.init();
cleanUpUpdates();
});
yield SpecialPowers.pushPrefEnv({
set: [
[PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0],
[PREF_APP_UPDATE_ENABLED, true],
[PREF_APP_UPDATE_IDLETIME, 0],
[PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE],
[PREF_APP_UPDATE_LOG, DEBUG_AUS_TEST],
]});
yield setupTestUpdater();
let url = URL_HTTP_UPDATE_SJS +
"?" + updateParams +
getVersionParams();
setUpdateURL(url);
executeSoon(() => {
Task.spawn(function*() {
gAUS.checkForBackgroundUpdates();
for (var i = 0; i < checkAttempts - 1; i++) {
yield waitForEvent("update-error", "check-attempt-failed");
gAUS.checkForBackgroundUpdates();
}
});
});
for (let step of steps) {
yield processStep(step);
}
yield finishTestRestoreUpdaterBackup();
});
}
/**
* Runs a test which processes an update. Similar to runUpdateTest.
*
* @param updates
* A list of updates to process.
* @param steps
* A list of test steps to perform, specifying expected doorhangers
* and additional validation/cleanup callbacks.
* @return A promise which will resolve once all of the steps have been run
* and cleanup has been performed.
*/
function runUpdateProcessingTest(updates, steps) {
return Task.spawn(function*() {
registerCleanupFunction(() => {
gMenuButtonUpdateBadge.reset();
cleanUpUpdates();
});
SpecialPowers.pushPrefEnv({
set: [
[PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0],
[PREF_APP_UPDATE_ENABLED, true],
[PREF_APP_UPDATE_IDLETIME, 0],
[PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE],
[PREF_APP_UPDATE_LOG, DEBUG_AUS_TEST],
]});
yield setupTestUpdater();
writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
writeStatusFile(STATE_FAILED_CRC_ERROR);
reloadUpdateManagerData();
testPostUpdateProcessing();
for (let step of steps) {
yield processStep(step);
}
yield finishTestRestoreUpdaterBackup();
});
}
function processStep({notificationId, button, beforeClick, cleanup}) {
return Task.spawn(function*() {
yield BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popupshown");
const shownNotification = PanelUI.activeNotification.id;
is(shownNotification, notificationId, "The right notification showed up.");
if (shownNotification != notificationId) {
if (cleanup) {
yield cleanup();
}
return;
}
let notification = document.getElementById(`PanelUI-${notificationId}-notification`);
is(notification.hidden, false, `${notificationId} notification is showing`);
if (beforeClick) {
yield Task.spawn(beforeClick);
}
let buttonEl = document.getAnonymousElementByAttribute(notification, "anonid", button);
buttonEl.click();
if (cleanup) {
yield cleanup();
}
});
}
/**
* Waits for the specified topic and (optionally) status.
* @param topic
* String representing the topic to wait for.
* @param status
* Optional String representing the status on said topic to wait for.
* @return A promise which will resolve the first time an event occurs on the
* specified topic, and (optionally) with the specified status.
*/
function waitForEvent(topic, status = null) {
return new Promise(resolve => Services.obs.addObserver({
observe(subject, innerTopic, innerStatus) {
if (!status || status == innerStatus) {
Services.obs.removeObserver(this, topic);
resolve(innerStatus);
}
}
}, topic, false))
}
/**
* Ensures that the "What's new" link with the provided ID is displayed and
* matches the url parameter provided. If no URL is provided, it will instead
* ensure that the link matches the default link URL.
*
* @param id
* The ID of the "What's new" link element.
* @param url (optional)
* The URL to check against. If none is provided, a default will be used.
*/
function checkWhatsNewLink(id, url) {
let whatsNewLink = document.getElementById(id);
is(whatsNewLink.href,
url || URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS",
"What's new link points to the test_details URL");
is(whatsNewLink.hidden, false, "What's new link is not hidden.");
}
/**
* For tests that use the test updater restores the backed up real updater if
* it exists and tries again on failure since Windows debug builds at times
* leave the file in use. After success moveRealUpdater is called to continue
* the setup of the test updater. For tests that don't use the test updater
* runTest will be called.
*/
function setupTestUpdater() {
return Task.spawn(function*() {
if (gUseTestUpdater) {
try {
restoreUpdaterBackup();
} catch (e) {
logTestInfo("Attempt to restore the backed up updater failed... " +
"will try again, Exception: " + e);
yield delay();
yield setupTestUpdater();
return;
}
yield moveRealUpdater();
}
});
}
/**
* Backs up the real updater and tries again on failure since Windows debug
* builds at times leave the file in use. After success it will call
* copyTestUpdater to continue the setup of the test updater.
*/
function moveRealUpdater() {
return Task.spawn(function*() {
try {
// Move away the real updater
let baseAppDir = getAppBaseDir();
let updater = baseAppDir.clone();
updater.append(FILE_UPDATER_BIN);
updater.moveTo(baseAppDir, FILE_UPDATER_BIN_BAK);
} catch (e) {
logTestInfo("Attempt to move the real updater out of the way failed... " +
"will try again, Exception: " + e);
yield delay();
yield moveRealUpdater();
return;
}
yield copyTestUpdater();
});
}
/**
* Copies the test updater so it can be used by tests and tries again on failure
* since Windows debug builds at times leave the file in use. After success it
* will call runTest to continue the test.
*/
function copyTestUpdater() {
return Task.spawn(function*() {
try {
// Copy the test updater
let baseAppDir = getAppBaseDir();
let testUpdaterDir = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
let relPath = REL_PATH_DATA;
let pathParts = relPath.split("/");
for (let i = 0; i < pathParts.length; ++i) {
testUpdaterDir.append(pathParts[i]);
}
let testUpdater = testUpdaterDir.clone();
testUpdater.append(FILE_UPDATER_BIN);
testUpdater.copyToFollowingLinks(baseAppDir, FILE_UPDATER_BIN);
} catch (e) {
logTestInfo("Attempt to copy the test updater failed... " +
"will try again, Exception: " + e);
yield delay();
yield copyTestUpdater();
}
});
}
/**
* Restores the updater that was backed up. This is called in setupTestUpdater
* before the backup of the real updater is done in case the previous test
* failed to restore the updater, in finishTestDefaultWaitForWindowClosed when
* the test has finished, and in test_9999_cleanup.xul after all tests have
* finished.
*/
function restoreUpdaterBackup() {
let baseAppDir = getAppBaseDir();
let updater = baseAppDir.clone();
let updaterBackup = baseAppDir.clone();
updater.append(FILE_UPDATER_BIN);
updaterBackup.append(FILE_UPDATER_BIN_BAK);
if (updaterBackup.exists()) {
if (updater.exists()) {
updater.remove(true);
}
updaterBackup.moveTo(baseAppDir, FILE_UPDATER_BIN);
}
}
/**
* When a test finishes this will repeatedly attempt to restore the real updater
* for tests that use the test updater and then call
* finishTestDefaultWaitForWindowClosed after the restore is successful.
*/
function finishTestRestoreUpdaterBackup() {
return Task.spawn(function*() {
if (gUseTestUpdater) {
try {
// Windows debug builds keep the updater file in use for a short period of
// time after the updater process exits.
restoreUpdaterBackup();
} catch (e) {
logTestInfo("Attempt to restore the backed up updater failed... " +
"will try again, Exception: " + e);
yield delay();
yield finishTestRestoreUpdaterBackup();
}
}
});
}

Просмотреть файл

@ -0,0 +1,4 @@
const REL_PATH_DATA = "browser/browser/base/content/test/appUpdate/";
const URL_HOST = "http://example.com";
const URL_PATH_UPDATE_XML = "/" + REL_PATH_DATA + "update.sjs";
const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;

Просмотреть файл

@ -638,7 +638,6 @@ tags = psm
[browser_domFullscreen_fullscreenMode.js]
tags = fullscreen
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_menuButtonBadgeManager.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
[browser_newTabDrop.js]
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.

Просмотреть файл

@ -1,46 +0,0 @@
/* 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/. */
var menuButton = document.getElementById("PanelUI-menu-button");
add_task(function* testButtonActivities() {
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
is(menuButton.hasAttribute("badge"), false, "Should not have the badge attribute set");
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-succeeded");
is(menuButton.getAttribute("badge-status"), "update-succeeded", "Should have update-succeeded badge status (update > fxa)");
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-failed");
is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, "download-severe");
is(menuButton.getAttribute("badge-status"), "download-severe", "Should have download-severe badge status");
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, "download-warning");
is(menuButton.getAttribute("badge-status"), "download-warning", "Should have download-warning badge status");
gMenuButtonBadgeManager.addBadge("unknownbadge", "attr");
is(menuButton.getAttribute("badge-status"), "download-warning", "Should not have changed badge status");
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE);
is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
yield PanelUI.show();
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (Hamburger menu opened)");
PanelUI.hide();
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-succeeded");
gMenuButtonBadgeManager.clearBadges();
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (clearBadges called)");
});

Просмотреть файл

@ -4,10 +4,6 @@
requestLongerTimeout(2);
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
const permissionError = "error: NotAllowedError: The request is not allowed " +
"by the user agent or the platform in the current context.";
@ -579,39 +575,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
add_task(async function test() {
await runTests(gTests);
});

Просмотреть файл

@ -1,10 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
var gTests = [
{
@ -76,34 +72,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
add_task(async function test() {
await runTests(gTests);
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
var gTests = [
{
@ -216,38 +212,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
let url = rootDir + "get_user_media.html";
content.location = 'data:text/html,<iframe id="frame1" src="' + url + '"></iframe><iframe id="frame2" src="' + url + '"></iframe>'
}
add_task(async function test() {
await runTests(gTests, { relativeURI: "get_user_media_in_frame.html" });
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
var gTests = [
{
@ -118,36 +114,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
add_task(async function test() {
await runTests(gTests);
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
const permissionError = "error: NotAllowedError: The request is not allowed " +
"by the user agent or the platform in the current context.";
@ -577,39 +573,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
add_task(async function test() {
await runTests(gTests);
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
var gTests = [
{
@ -62,46 +58,12 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, runTest);
}
add_task(async function test() {
await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]});
function runTest() {
// An empty tab where we can load the content script without leaving it
// behind at the end of the test.
gBrowser.addTab();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
await runTests(gTests);
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
const permissionError = "error: NotAllowedError: The request is not allowed " +
"by the user agent or the platform in the current context.";
@ -251,39 +247,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
add_task(async function test() {
await runTests(gTests);
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
const permissionError = "error: NotAllowedError: The request is not allowed " +
"by the user agent or the platform in the current context.";
@ -210,37 +206,6 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media_in_frame.html";
}
add_task(async function test() {
await runTests(gTests, { relativeURI: "get_user_media_in_frame.html" });
});

Просмотреть файл

@ -2,10 +2,6 @@
* 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/. */
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
var gTests = [
{
@ -56,46 +52,12 @@ var gTests = [
];
function test() {
waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, runTest);
}
add_task(async function test() {
await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]});
function runTest() {
// An empty tab where we can load the content script without leaving it
// behind at the end of the test.
gBrowser.addTab();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
}
await runTests(gTests);
});

Просмотреть файл

@ -6,10 +6,6 @@ Cu.import("resource:///modules/webrtcUI.jsm");
const ORIGIN = "https://example.com";
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
function* tryPeerConnection(browser, expectedError = null) {
let errtype = yield ContentTask.spawn(browser, null, function*() {
let pc = new content.RTCPeerConnection();
@ -326,36 +322,11 @@ var gTests = [
},
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.addEventListener("load", function() {
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run(browser);
// Make sure the test cleaned up after itself.
is(webrtcUI.peerConnectionBlockers.size, 0, "Peer connection blockers list is empty");
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, {capture: true, once: true});
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content", ORIGIN);
content.location = rootDir + "get_user_media.html";
}
add_task(async function test() {
await runTests(gTests,
{ cleanup() {
is(webrtcUI.peerConnectionBlockers.size, 0,
"Peer connection blockers list is empty");
}
});
});

Просмотреть файл

@ -520,3 +520,34 @@ function promiseReloadFrame(aFrameId) {
.reload();
});
}
async function runTests(tests, options = {}) {
let leaf = options.relativeURI || "get_user_media.html";
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
let absoluteURI = rootDir + leaf;
let cleanup = options.cleanup || (() => expectNoObserverCalled());
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, absoluteURI);
let browser = tab.linkedBrowser;
browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
await SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of tests) {
info(testCase.desc);
await Task.spawn(testCase.run(browser));
await cleanup();
}
// Some tests destroy the original tab and leave a new one in its place.
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
}

Просмотреть файл

@ -37,6 +37,9 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/webrtc/browser.ini',
]
if CONFIG['MOZ_UPDATER']:
BROWSER_CHROME_MANIFESTS += ['content/test/appUpdate/browser.ini']
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
@ -51,4 +54,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
TEST_HARNESS_FILES.testing.mochitest.browser.browser.base.content.test.appUpdate += [
'/toolkit/mozapps/update/tests/chrome/update.sjs',
'/toolkit/mozapps/update/tests/data/shared.js',
'/toolkit/mozapps/update/tests/data/sharedUpdateXML.js',
'/toolkit/mozapps/update/tests/data/simple.mar',
]
JAR_MANIFESTS += ['jar.mn']

Просмотреть файл

@ -26,8 +26,8 @@ pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes")
pref("app.update.checkInstallTime.days", 63);
// Give the user x seconds to reboot before showing a badge on the hamburger
// button. default=immediately
pref("app.update.badgeWaitTime", 0);
// button. default=4 days
pref("app.update.badgeWaitTime", 345600);
// Number of usages of the web console or scratchpad.
// If this is less than 5, then pasting code into the web console or scratchpad is disabled

Просмотреть файл

@ -17,9 +17,17 @@
<footer id="PanelUI-footer">
<vbox id="PanelUI-footer-addons"></vbox>
<toolbarbutton id="PanelUI-update-status"
oncommand="gMenuButtonUpdateBadge.onMenuPanelCommand(event);"
<toolbarbutton id="PanelUI-update-available-menu-item"
wrap="true"
label="&updateAvailable.panelUI.label;"
hidden="true"/>
<toolbarbutton id="PanelUI-update-manual-menu-item"
wrap="true"
label="&updateManual.panelUI.label;"
hidden="true"/>
<toolbarbutton id="PanelUI-update-restart-menu-item"
wrap="true"
label="&updateRestart.panelUI.label;"
hidden="true"/>
<hbox id="PanelUI-footer-fxa">
<hbox id="PanelUI-fxa-status"
@ -414,3 +422,66 @@
id="panic-button-success-closebutton"
oncommand="PanicButtonNotifier.close()"/>
</panel>
<panel id="PanelUI-notification-popup"
class="popup-notification-panel"
type="arrow"
position="after_start"
hidden="true"
orient="vertical"
noautofocus="true"
noautohide="true"
nopreventnavboxhide="true"
role="alert">
<popupnotification id="PanelUI-update-available-notification"
popupid="update-available"
label="&updateAvailable.header.message;"
buttonlabel="&updateAvailable.acceptButton.label;"
buttonaccesskey="&updateAvailable.acceptButton.accesskey;"
closebuttonhidden="true"
secondarybuttonlabel="&updateAvailable.cancelButton.label;"
secondarybuttonaccesskey="&updateAvailable.cancelButton.accesskey;"
dropmarkerhidden="true"
checkboxhidden="true"
hidden="true">
<popupnotificationcontent id="update-available-notification-content" orient="vertical">
<description id="update-available-description">&updateAvailable.message;
<label id="update-available-whats-new" class="text-link" value="&updateAvailable.whatsnew.label;" />
</description>
</popupnotificationcontent>
</popupnotification>
<popupnotification id="PanelUI-update-manual-notification"
popupid="update-manual"
label="&updateManual.header.message;"
buttonlabel="&updateManual.acceptButton.label;"
buttonaccesskey="&updateManual.acceptButton.accesskey;"
closebuttonhidden="true"
secondarybuttonlabel="&updateManual.cancelButton.label;"
secondarybuttonaccesskey="&updateManual.cancelButton.accesskey;"
dropmarkerhidden="true"
checkboxhidden="true"
hidden="true">
<popupnotificationcontent id="update-manual-notification-content" orient="vertical">
<description id="update-manual-description">&updateManual.message;
<label id="update-manual-whats-new" class="text-link" value="&updateManual.whatsnew.label;" />
</description>
</popupnotificationcontent>
</popupnotification>
<popupnotification id="PanelUI-update-restart-notification"
popupid="update-restart"
label="&updateRestart.header.message;"
buttonlabel="&updateRestart.acceptButton.label;"
buttonaccesskey="&updateRestart.acceptButton.accesskey;"
closebuttonhidden="true"
secondarybuttonlabel="&updateRestart.cancelButton.label;"
secondarybuttonaccesskey="&updateRestart.cancelButton.accesskey;"
dropmarkerhidden="true"
checkboxhidden="true"
hidden="true">
<popupnotificationcontent id="update-restart-notification-content" orient="vertical">
<description id="update-restart-description">&updateRestart.message;</description>
</popupnotificationcontent>
</popupnotification>
</panel>

Просмотреть файл

@ -32,7 +32,9 @@ const PanelUI = {
helpView: "PanelUI-helpView",
menuButton: "PanelUI-menu-button",
panel: "PanelUI-popup",
scroller: "PanelUI-contents-scroller"
notificationPanel: "PanelUI-notification-popup",
scroller: "PanelUI-contents-scroller",
footer: "PanelUI-footer"
};
},
@ -48,11 +50,23 @@ const PanelUI = {
});
}
this.notifications = [];
this.menuButton.addEventListener("mousedown", this);
this.menuButton.addEventListener("keypress", this);
this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);
Services.obs.addObserver(this, "fullscreen-nav-toolbox", false);
Services.obs.addObserver(this, "panelUI-notification-main-action", false);
Services.obs.addObserver(this, "panelUI-notification-dismissed", false);
window.addEventListener("fullscreen", this);
window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
CustomizableUI.addListener(this);
for (let event of this.kEvents) {
this.notificationPanel.addEventListener(event, this);
}
this._initialized = true;
},
@ -75,7 +89,14 @@ const PanelUI = {
uninit() {
for (let event of this.kEvents) {
this.panel.removeEventListener(event, this);
this.notificationPanel.removeEventListener(event, this);
}
Services.obs.removeObserver(this, "fullscreen-nav-toolbox");
Services.obs.removeObserver(this, "panelUI-notification-main-action");
Services.obs.removeObserver(this, "panelUI-notification-dismissed");
window.removeEventListener("fullscreen", this);
this.helpView.removeEventListener("ViewShowing", this._onHelpViewShow);
this.menuButton.removeEventListener("mousedown", this);
this.menuButton.removeEventListener("keypress", this);
@ -156,16 +177,70 @@ const PanelUI = {
resolve();
}, {once: true});
let iconAnchor =
document.getAnonymousElementByAttribute(anchor, "class",
"toolbarbutton-icon");
this.panel.openPopup(iconAnchor || anchor);
anchor = this._getPanelAnchor(anchor);
this.panel.openPopup(anchor);
}, (reason) => {
console.error("Error showing the PanelUI menu", reason);
});
});
},
showNotification(id, mainAction, secondaryActions = [], options = {}) {
let notification = new PanelUINotification(id, mainAction, secondaryActions, options);
let existingIndex = this.notifications.findIndex(n => n.id == id);
if (existingIndex != -1) {
this.notifications.splice(existingIndex, 1);
}
// We don't want to clobber doorhanger notifications just to show a badge,
// so don't dismiss any of them and the badge will show once the doorhanger
// gets resolved.
if (!options.badgeOnly && !options.dismissed) {
this.notifications.forEach(n => { n.dismissed = true; });
}
// Since notifications are generally somewhat pressing, the ideal case is that
// we never have two notifications at once. However, in the event that we do,
// it's more likely that the older notification has been sitting around for a
// bit, and so we don't want to hide the new notification behind it. Thus,
// we want our notifications to behave like a stack instead of a queue.
this.notifications.unshift(notification);
this._updateNotifications();
return notification;
},
showBadgeOnlyNotification(id) {
return this.showNotification(id, null, null, { badgeOnly: true });
},
removeNotification(id) {
let notifications;
if (typeof id == "string") {
notifications = this.notifications.filter(n => n.id == id);
} else {
// If it's not a string, assume RegExp
notifications = this.notifications.filter(n => id.test(n.id));
}
notifications.forEach(n => {
this._removeNotification(n);
});
this._updateNotifications();
},
dismissNotification(id) {
let notifications;
if (typeof id == "string") {
notifications = this.notifications.filter(n => n.id == id);
} else {
// If it's not a string, assume RegExp
notifications = this.notifications.filter(n => id.test(n.id));
}
notifications.forEach(n => n.dismissed = true);
this._updateNotifications();
},
/**
* If the menu panel is being shown, hide it.
*/
@ -177,6 +252,24 @@ const PanelUI = {
this.panel.hidePopup();
},
observe(subject, topic, status) {
switch (topic) {
case "fullscreen-nav-toolbox":
this._updateNotifications();
break;
case "panelUI-notification-main-action":
if (subject != window) {
this.removeNotification(status);
}
break;
case "panelUI-notification-dismissed":
if (subject != window) {
this.dismissNotification(status);
}
break;
}
},
handleEvent(aEvent) {
// Ignore context menus and menu button menus showing and hiding:
if (aEvent.type.startsWith("popup") &&
@ -192,6 +285,7 @@ const PanelUI = {
case "popuphiding":
// Fall through
case "popuphidden":
this._updateNotifications();
this._updatePanelButton(aEvent.target);
break;
case "mousedown":
@ -201,6 +295,9 @@ const PanelUI = {
case "keypress":
this.toggle(aEvent);
break;
case "fullscreen":
this._updateNotifications();
break;
}
},
@ -208,6 +305,22 @@ const PanelUI = {
return !!this._isReady;
},
get isNotificationPanelOpen() {
let panelState = this.notificationPanel.state;
return panelState == "showing" || panelState == "open";
},
get activeNotification() {
if (this.notifications.length > 0) {
const doorhanger =
this.notifications.find(n => !n.dismissed && !n.options.badgeOnly);
return doorhanger || this.notifications[0];
}
return null;
},
/**
* Registering the menu panel is done lazily for performance reasons. This
* method is exposed so that CustomizationMode can force panel-readyness in the
@ -393,14 +506,13 @@ const PanelUI = {
CustomizableUI.addPanelCloseListeners(tempPanel);
tempPanel.addEventListener("popuphidden", panelRemover);
let iconAnchor =
document.getAnonymousElementByAttribute(aAnchor, "class",
"toolbarbutton-icon");
let anchor = this._getPanelAnchor(aAnchor);
if (iconAnchor && aAnchor.id) {
iconAnchor.setAttribute("consumeanchor", aAnchor.id);
if (aAnchor != anchor && aAnchor.id) {
anchor.setAttribute("consumeanchor", aAnchor.id);
}
tempPanel.openPopup(iconAnchor || aAnchor, "bottomcenter topright");
tempPanel.openPopup(anchor, "bottomcenter topright");
}
}),
@ -538,6 +650,212 @@ const PanelUI = {
ScrollbarSampler.resetSystemScrollbarWidth();
this._scrollWidth = null;
},
_hidePopup() {
if (this.isNotificationPanelOpen) {
this.notificationPanel.hidePopup();
}
},
_updateNotifications() {
if (!this.notifications.length) {
this._clearAllNotifications();
this._hidePopup();
return;
}
if (window.fullScreen && FullScreen.navToolboxHidden) {
this._hidePopup();
return;
}
let doorhangers =
this.notifications.filter(n => !n.dismissed && !n.options.badgeOnly);
if (this.panel.state == "showing" || this.panel.state == "open") {
// If the menu is already showing, then we need to dismiss all notifications
// since we don't want their doorhangers competing for attention
doorhangers.forEach(n => { n.dismissed = true; })
this._hidePopup();
this._clearBadge();
if (!this.notifications[0].options.badgeOnly) {
this._showMenuItem(this.notifications[0]);
}
} else if (doorhangers.length > 0) {
this._clearBadge();
this._showNotificationPanel(doorhangers[0]);
} else {
this._hidePopup();
this._showBadge(this.notifications[0]);
this._showMenuItem(this.notifications[0]);
}
},
_showNotificationPanel(notification) {
this._refreshNotificationPanel(notification);
if (this.isNotificationPanelOpen) {
return;
}
let anchor = this._getPanelAnchor(this.menuButton);
this.notificationPanel.hidden = false;
this.notificationPanel.openPopup(anchor, "bottomcenter topright");
},
_clearNotificationPanel() {
for (let popupnotification of this.notificationPanel.children) {
popupnotification.hidden = true;
popupnotification.notification = null;
}
},
_clearAllNotifications() {
this._clearNotificationPanel();
this._clearBadge();
this._clearMenuItems();
},
_refreshNotificationPanel(notification) {
this._clearNotificationPanel();
let popupnotificationID = this._getPopupId(notification);
let popupnotification = document.getElementById(popupnotificationID);
popupnotification.setAttribute("id", popupnotificationID);
popupnotification.setAttribute("buttoncommand", "PanelUI._onNotificationButtonEvent(event, 'buttoncommand');");
popupnotification.setAttribute("secondarybuttoncommand", "PanelUI._onNotificationButtonEvent(event, 'secondarybuttoncommand');");
popupnotification.notification = notification;
popupnotification.hidden = false;
},
_showBadge(notification) {
let badgeStatus = this._getBadgeStatus(notification);
this.menuButton.setAttribute("badge-status", badgeStatus);
},
// "Menu item" here refers to an item in the hamburger panel menu. They will
// typically show up as a colored row near the bottom of the panel.
_showMenuItem(notification) {
this._clearMenuItems();
let menuItemId = this._getMenuItemId(notification);
let menuItem = document.getElementById(menuItemId);
if (menuItem) {
menuItem.notification = notification;
menuItem.setAttribute("oncommand", "PanelUI._onNotificationMenuItemSelected(event)");
menuItem.classList.add("PanelUI-notification-menu-item");
menuItem.hidden = false;
menuItem.fromPanelUINotifications = true;
}
},
_clearBadge() {
this.menuButton.removeAttribute("badge-status");
},
_clearMenuItems() {
for (let child of this.footer.children) {
if (child.fromPanelUINotifications) {
child.notification = null;
child.hidden = true;
}
}
},
_removeNotification(notification) {
// This notification may already be removed, in which case let's just fail
// silently.
let notifications = this.notifications;
if (!notifications)
return;
var index = notifications.indexOf(notification);
if (index == -1)
return;
// Remove the notification
notifications.splice(index, 1);
},
_onNotificationButtonEvent(event, type) {
let notificationEl = getNotificationFromElement(event.originalTarget);
if (!notificationEl)
throw "PanelUI._onNotificationButtonEvent: couldn't find notification element";
if (!notificationEl.notification)
throw "PanelUI._onNotificationButtonEvent: couldn't find notification";
let notification = notificationEl.notification;
let action = notification.mainAction;
if (type == "secondarybuttoncommand") {
action = notification.secondaryActions[0];
}
let dismiss = true;
if (action) {
try {
if (action === notification.mainAction) {
action.callback(true);
this._notify(notification.id, "main-action");
} else {
action.callback();
}
} catch (error) {
Cu.reportError(error);
}
dismiss = action.dismiss;
}
if (dismiss) {
notification.dismissed = true;
this._notify(notification.id, "dismissed");
} else {
this._removeNotification(notification);
}
this._updateNotifications();
},
_onNotificationMenuItemSelected(event) {
let target = event.originalTarget;
if (!target.notification)
throw "menucommand target has no associated action/notification";
event.stopPropagation();
try {
target.notification.mainAction.callback(false);
this._notify(target.notification.id, "main-action");
} catch (error) {
Cu.reportError(error);
}
this._removeNotification(target.notification);
this._updateNotifications();
},
_getPopupId(notification) { return "PanelUI-" + notification.id + "-notification"; },
_getBadgeStatus(notification) { return notification.id; },
_getMenuItemId(notification) { return "PanelUI-" + notification.id + "-menu-item"; },
_getPanelAnchor(candidate) {
let iconAnchor =
document.getAnonymousElementByAttribute(candidate, "class",
"toolbarbutton-icon");
return iconAnchor || candidate;
},
_notify(status, topic) {
Services.obs.notifyObservers(window, "panelUI-notification-" + topic, status);
}
};
XPCOMUtils.defineConstant(this, "PanelUI", PanelUI);
@ -549,3 +867,24 @@ XPCOMUtils.defineConstant(this, "PanelUI", PanelUI);
function getLocale() {
return Services.locale.getAppLocaleAsLangTag();
}
function PanelUINotification(id, mainAction, secondaryActions = [], options = {}) {
this.id = id;
this.mainAction = mainAction;
this.secondaryActions = secondaryActions;
this.options = options;
this.dismissed = this.options.dismissed || false;
}
function getNotificationFromElement(aElement) {
// Need to find the associated notification object, which is a bit tricky
// since it isn't associated with the element directly - this is kind of
// gross and very dependent on the structure of the popupnotification
// binding's content.
let notificationEl;
let parent = aElement;
while (parent && (parent = aElement.ownerDocument.getBindingParent(parent))) {
notificationEl = parent;
}
return notificationEl;
}

Просмотреть файл

@ -150,6 +150,7 @@ skip-if = os == "mac"
[browser_customizemode_contextmenu_menubuttonstate.js]
[browser_exit_background_customize_mode.js]
[browser_panel_toggle.js]
[browser_panelUINotifications.js]
[browser_switch_to_customize_mode.js]
[browser_synced_tabs_menu.js]
[browser_check_tooltips_in_navbar.js]

Просмотреть файл

@ -0,0 +1,305 @@
"use strict";
/**
* Tests that when we click on the main call-to-action of the doorhanger, the provided
* action is called, and the doorhanger removed.
*/
add_task(function* testMainActionCalled() {
let options = {
gBrowser: window.gBrowser,
url: "about:blank"
};
let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
yield BrowserTestUtils.withNewTab(options, function*(browser) {
let doc = browser.ownerDocument;
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
let mainActionCalled = false;
let mainAction = {
callback: () => { mainActionCalled = true; }
};
PanelUI.showNotification("update-manual", mainAction);
let extraMainActionCalled = false;
let extraMainAction = {
callback: () => { extraMainActionCalled = true; }
};
extraWindow.PanelUI.showNotification("update-manual", extraMainAction)
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
mainActionButton.click();
ok(mainActionCalled, "Main action callback was called");
isnot(extraMainActionCalled, true, "Extra window's main action callback was not called");
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(extraWindow.PanelUI.notificationPanel.state, "closed", "Extra window's update-manual doorhanger is closed.");
is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
});
yield BrowserTestUtils.closeWindow(extraWindow);
});
/**
* This tests that when we click the secondary action for a notification,
* it will display the badge for that notification on the PanelUI menu button.
* Once we click on this button, we should see an item in the menu which will
* call our main action.
*/
add_task(function* testSecondaryActionWorkflow() {
let options = {
gBrowser: window.gBrowser,
url: "about:blank"
};
let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
yield BrowserTestUtils.withNewTab(options, function*(browser) {
let doc = browser.ownerDocument;
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
let mainActionCalled = false;
let mainAction = {
callback: () => { mainActionCalled = true; },
};
PanelUI.showNotification("update-manual", mainAction);
let extraMainActionCalled = false;
let extraMainAction = {
callback: () => { extraMainActionCalled = true; }
};
extraWindow.PanelUI.showNotification("update-manual", extraMainAction)
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
secondaryActionButton.click();
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(extraWindow.PanelUI.notificationPanel.state, "closed", "Extra window's update-manual doorhanger is closed.");
is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is displaying on PanelUI button.");
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button.");
let menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
is(menuItem.hidden, false, "update-manual menu item is showing.");
yield PanelUI.hide();
is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is shown on PanelUI button.");
yield PanelUI.show();
menuItem.click();
ok(mainActionCalled, "Main action callback was called");
isnot(extraMainActionCalled, true, "Extra window's main action callback was not called");
PanelUI.removeNotification(/.*/);
});
yield BrowserTestUtils.closeWindow(extraWindow);
});
/**
* We want to ensure a few things with this:
* - Adding a doorhanger will make a badge disappear
* - once the notification for the doorhanger is resolved (removed, not just dismissed),
* then we display any other badges that are remaining.
*/
add_task(function* testInteractionWithBadges() {
yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
let doc = browser.ownerDocument;
PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button.");
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
let mainActionCalled = false;
let mainAction = {
callback: () => { mainActionCalled = true; },
};
PanelUI.showNotification("update-manual", mainAction);
isnot(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is hidden on PanelUI button.");
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
secondaryActionButton.click();
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is displaying on PanelUI button.");
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button.");
let menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
is(menuItem.hidden, false, "update-manual menu item is showing.");
menuItem.click();
ok(mainActionCalled, "Main action callback was called");
is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button.");
PanelUI.removeNotification(/.*/);
is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
});
});
/**
* This tests that adding a badge will not dismiss any existing doorhangers.
*/
add_task(function* testAddingBadgeWhileDoorhangerIsShowing() {
yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
let doc = browser.ownerDocument;
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
let mainActionCalled = false;
let mainAction = {
callback: () => { mainActionCalled = true; }
};
PanelUI.showNotification("update-manual", mainAction);
PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
isnot(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is hidden on PanelUI button.");
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
let doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
mainActionButton.click();
ok(mainActionCalled, "Main action callback was called");
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button.");
PanelUI.removeNotification(/.*/);
is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
});
});
/**
* Tests that badges operate like a stack.
*/
add_task(function* testMultipleBadges() {
yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
let doc = browser.ownerDocument;
let menuButton = doc.getElementById("PanelUI-menu-button");
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
is(menuButton.hasAttribute("badge"), false, "Should not have the badge attribute set");
PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
PanelUI.showBadgeOnlyNotification("update-succeeded");
is(menuButton.getAttribute("badge-status"), "update-succeeded", "Should have update-succeeded badge status (update > fxa)");
PanelUI.showBadgeOnlyNotification("update-failed");
is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
PanelUI.showBadgeOnlyNotification("download-severe");
is(menuButton.getAttribute("badge-status"), "download-severe", "Should have download-severe badge status");
PanelUI.showBadgeOnlyNotification("download-warning");
is(menuButton.getAttribute("badge-status"), "download-warning", "Should have download-warning badge status");
PanelUI.removeNotification(/^download-/);
is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
PanelUI.removeNotification(/^update-/);
is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
PanelUI.removeNotification(/^fxa-/);
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
yield PanelUI.show();
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (Hamburger menu opened)");
PanelUI.hide();
PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
PanelUI.showBadgeOnlyNotification("update-succeeded");
PanelUI.removeNotification(/.*/);
is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
});
});
/**
* Tests that non-badges also operate like a stack.
*/
add_task(function* testMultipleNonBadges() {
yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
let doc = browser.ownerDocument;
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
let updateManualAction = {
called: false,
callback: () => { updateManualAction.called = true; },
};
let updateRestartAction = {
called: false,
callback: () => { updateRestartAction.called = true; },
};
PanelUI.showNotification("update-manual", updateManualAction);
let notifications;
let doorhanger;
isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing.");
notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
PanelUI.showNotification("update-restart", updateRestartAction);
isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing.");
notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
doorhanger = notifications[0];
is(doorhanger.id, "PanelUI-update-restart-notification", "PanelUI is displaying the update-restart notification.");
let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
secondaryActionButton.click();
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is displaying on PanelUI button.");
let menuItem;
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is hidden on PanelUI button.");
menuItem = doc.getElementById("PanelUI-update-restart-menu-item");
is(menuItem.hidden, false, "update-restart menu item is showing.");
menuItem.click();
ok(updateRestartAction.called, "update-restart main action callback was called");
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is displaying on PanelUI button.");
yield PanelUI.show();
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is hidden on PanelUI button.");
menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
is(menuItem.hidden, false, "update-manual menu item is showing.");
menuItem.click();
ok(updateManualAction.called, "update-manual main action callback was called");
});
});

Просмотреть файл

@ -507,15 +507,13 @@ const DownloadsIndicatorView = {
if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
this.indicator.removeAttribute("attention");
if (inMenu) {
gMenuButtonBadgeManager.removeBadge(
gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
PanelUI.removeNotification(/^download-/);
}
} else {
this.indicator.setAttribute("attention", this._attention);
if (inMenu) {
let badgeClass = "download-" + this._attention;
gMenuButtonBadgeManager.addBadge(
gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
PanelUI.showBadgeOnlyNotification(badgeClass);
}
}
},

Просмотреть файл

@ -12,7 +12,7 @@
"properties": {
"homepage": {
"type": "string",
"format": "url",
"format": "relativeUrl",
"optional": true,
"preprocess": "localize"
}
@ -22,4 +22,4 @@
}
]
}
]
]

Просмотреть файл

@ -182,3 +182,20 @@ add_task(function* test_disable() {
is(Preferences.get("browser.startup.homepage"), defaultHomePage,
"Home url should be the default");
});
add_task(function* test_local() {
let ext1 = ExtensionTestUtils.loadExtension({
manifest: {"chrome_settings_overrides": {"homepage": "home.html"}},
useAddonManager: "temporary",
});
let prefPromise = promisePrefChangeObserved("browser.startup.homepage");
yield ext1.startup();
yield prefPromise;
let homepage = Preferences.get("browser.startup.homepage");
ok((homepage.startsWith("moz-extension") && homepage.endsWith("home.html")),
"Home url should be relative to extension.");
yield ext1.unload();
});

Просмотреть файл

@ -794,6 +794,9 @@ BrowserGlue.prototype = {
// With older versions of the extension installed, this load will fail
// passively.
Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap.js", true);
if (PdfJs.enabled) {
Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap-enabled.js", true);
}
if (AppConstants.platform == "win") {
// For Windows 7, initialize the jump list module.

Просмотреть файл

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.actionTypes = [
"INIT",
"UNINIT",
// The line below creates an object like this:
// {
// INIT: "INIT",
// UNINIT: "UNINIT"
// }
// It prevents accidentally adding a different key/value name.
].reduce((obj, type) => { obj[type] = type; return obj; }, {});
this.EXPORTED_SYMBOLS = [
"actionTypes"
];

Просмотреть файл

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.INITIAL_STATE = {
TopSites: {
rows: [
{
"title": "Facebook",
"url": "https://www.facebook.com/"
},
{
"title": "YouTube",
"url": "https://www.youtube.com/"
},
{
"title": "Amazon",
"url": "http://www.amazon.com/"
},
{
"title": "Yahoo",
"url": "https://www.yahoo.com/"
},
{
"title": "eBay",
"url": "http://www.ebay.com"
},
{
"title": "Twitter",
"url": "https://twitter.com/"
}
]
}
};
// TODO: Handle some real actions here, once we have a TopSites feed working
function TopSites(prevState = INITIAL_STATE.TopSites, action) {
return prevState;
}
this.reducers = {TopSites};
this.EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE"];

Просмотреть файл

@ -5,4 +5,6 @@
[features/activity-stream@mozilla.org] chrome.jar:
% resource activity-stream %content/
content/lib/ (./lib/*)
content/common/ (./common/*)
content/vendor/Redux.jsm (./vendor/Redux.jsm)
content/data/ (./data/*)

Просмотреть файл

@ -3,7 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
class ActivityStream {
const {utils: Cu} = Components;
const {Store} = Cu.import("resource://activity-stream/lib/Store.jsm", {});
this.ActivityStream = class ActivityStream {
/**
* constructor - Initializes an instance of ActivityStream
@ -16,13 +19,16 @@ class ActivityStream {
constructor(options) {
this.initialized = false;
this.options = options;
this.store = new Store();
}
init() {
this.initialized = true;
this.store.init();
}
uninit() {
this.store.uninit();
this.initialized = false;
}
}
};
this.EXPORTED_SYMBOLS = ["ActivityStream"];

Просмотреть файл

@ -0,0 +1,78 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {utils: Cu} = Components;
const {redux} = Cu.import("resource://activity-stream/vendor/Redux.jsm", {});
const {actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
const {reducers} = Cu.import("resource://activity-stream/common/Reducers.jsm", {});
/**
* Store - This has a similar structure to a redux store, but includes some extra
* functionality. It accepts an array of "Feeds" on inititalization, which
* can listen for any action that is dispatched through the store.
*/
this.Store = class Store {
/**
* constructor - The redux store is created here,
* but no listeners are added until "init" is called.
*/
constructor() {
this._middleware = this._middleware.bind(this);
// Bind each redux method so we can call it directly from the Store. E.g.,
// store.dispatch() will call store._store.dispatch();
["dispatch", "getState", "subscribe"].forEach(method => {
this[method] = function(...args) {
return this._store[method](...args);
}.bind(this);
});
this.feeds = new Set();
this._store = redux.createStore(
redux.combineReducers(reducers),
redux.applyMiddleware(this._middleware)
);
}
/**
* _middleware - This is redux middleware consumed by redux.createStore.
* it calls each feed's .onAction method, if one
* is defined.
*/
_middleware(store) {
return next => action => {
next(action);
this.feeds.forEach(s => s.onAction && s.onAction(action));
};
}
/**
* init - Initializes the MessageManager channel, and adds feeds.
* After initialization has finished, an INIT action is dispatched.
*
* @param {array} feeds An array of objects with an optional .onAction method
*/
init(feeds) {
if (feeds) {
feeds.forEach(subscriber => {
subscriber.store = this;
this.feeds.add(subscriber);
});
}
this.dispatch({type: at.INIT});
}
/**
* uninit - Clears all feeds, dispatches an UNINIT action
*
* @return {type} description
*/
uninit() {
this.feeds.clear();
this.dispatch({type: at.UNINIT});
}
};
this.EXPORTED_SYMBOLS = ["Store"];

Просмотреть файл

@ -1720,6 +1720,7 @@ class PPAPIInstance {
let mouseEventInit = {
altkey: event.altkey,
button: event.button,
buttons: event.buttons,
clientX: event.clientX - rect.left,
clientY: event.clientY - rect.top,
ctrlKey: event.ctrlKey,
@ -1820,6 +1821,12 @@ class PPAPIInstance {
// We need permission for showing print dialog to get print settings
this.mm.sendAsyncMessage("ppapipdf.js:getPrintSettings");
break;
case 'openLink':
this.mm.sendAsyncMessage("ppapipdf.js:openLink", {
url: message.url,
disposition: message.disposition
});
break;
case 'viewport':
case 'rotateClockwise':
case 'rotateCounterclockwise':
@ -3126,25 +3133,37 @@ dump(`callFromJSON: < ${JSON.stringify(call)}\n`);
if (event instanceof KeyboardInputEvent) {
if (event.domEvent.location == event.domEvent.DOM_KEY_LOCATION_NUMPAD) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISKEYPAD;
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISKEYPAD;
} else if (event.domEvent.location & event.domEvent.DOM_KEY_LOCATION_LEFT) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISLEFT;
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISLEFT;
} else if (event.domEvent.location & event.domEvent.DOM_KEY_LOCATION_RIGHT) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISRIGHT;
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISRIGHT;
}
if (event.domEvent.repeat) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT;
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT;
}
} else if (event instanceof MouseInputEvent) {
if (event.domEvent.buttons && 0x01) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN;
if (event.domEvent.buttons & 0x01) {
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN;
}
if (event.domEvent.buttons && 0x04) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN;
if (event.domEvent.buttons & 0x04) {
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN;
}
if (event.domEvent.buttons && 0x02) {
modifiers &= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN;
if (event.domEvent.buttons & 0x02) {
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN;
}
if (event.domEvent.type == 'mouseup') {
// mouseup event indicates the key released only in domEvent.button
// rather than domEvent.buttons, but PDFium do use modifiers to
// determine which button is released. So we make it up here.
if (event.domEvent.button == 0) {
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN;
} else if (event.domEvent.button == 1) {
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN;
} else if (event.domEvent.button == 2) {
modifiers |= PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN;
}
}
}

Просмотреть файл

@ -782,6 +782,13 @@ class Viewport {
case 'goToPage':
this.page = message.page;
break;
case 'navigate':
this._doAction({
type: 'openLink',
url: message.url,
disposition: message.disposition
});
break;
}
}
}

Просмотреть файл

@ -180,6 +180,31 @@ mm.addMessageListener("ppapipdf.js:printPDF", ({ data }) => {
});
});
mm.addMessageListener("ppapipdf.js:openLink", ({data}) => {
const PDFIUM_WINDOW_OPEN_DISPOSITION = {
CURRENT_TAB: 1,
NEW_FOREGROUND_TAB: 3,
NEW_BACKGROUND_TAB: 4,
NEW_WINDOW: 6,
};
switch(data.disposition) {
case PDFIUM_WINDOW_OPEN_DISPOSITION.CURRENT_TAB:
containerWindow.location.href = data.url;
break;
// We don't support opening in background tab for now. Just open it in
// foreground tab.
case PDFIUM_WINDOW_OPEN_DISPOSITION.NEW_FOREGROUND_TAB:
case PDFIUM_WINDOW_OPEN_DISPOSITION.NEW_BACKGROUND_TAB:
containerWindow.open(data.url);
break;
case PDFIUM_WINDOW_OPEN_DISPOSITION.NEW_WINDOW:
containerWindow.open(data.url, "",
"noopener=1,menubar=1,toolbar=1," +
"location=1,personalbar=1,status=1,resizable");
break;
}
});
mm.addMessageListener("ppapipdf.js:save", () => {
let url = containerWindow.document.location;
let filename = "document.pdf";

Просмотреть файл

@ -1,3 +1,3 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.7.401
Current extension version is: 1.8.173

Просмотреть файл

@ -190,9 +190,9 @@ var PdfJs = {
updateRegistration: function updateRegistration() {
if (this.enabled) {
this._ensureRegistered();
this.ensureRegistered();
} else {
this._ensureUnregistered();
this.ensureUnregistered();
}
},
@ -205,7 +205,7 @@ var PdfJs = {
Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED);
this._initialized = false;
}
this._ensureUnregistered();
this.ensureUnregistered();
},
_migrate: function migrate() {
@ -324,7 +324,7 @@ var PdfJs = {
return !enabledPluginFound;
},
_ensureRegistered: function _ensureRegistered() {
ensureRegistered: function ensureRegistered() {
if (this._registered) {
return;
}
@ -335,7 +335,7 @@ var PdfJs = {
this._registered = true;
},
_ensureUnregistered: function _ensureUnregistered() {
ensureUnregistered: function ensureUnregistered() {
if (!this._registered) {
return;
}

Просмотреть файл

@ -33,7 +33,9 @@ const MAX_STRING_PREF_LENGTH = 128;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetworkManager",
"resource://pdf.js/PdfJsNetwork.jsm");

Просмотреть файл

@ -1292,6 +1292,16 @@ var CustomStyle = function CustomStyleClosure() {
};
return CustomStyle;
}();
var RenderingCancelledException = function RenderingCancelledException() {
function RenderingCancelledException(msg, type) {
this.message = msg;
this.type = type;
}
RenderingCancelledException.prototype = new Error();
RenderingCancelledException.prototype.name = 'RenderingCancelledException';
RenderingCancelledException.constructor = RenderingCancelledException;
return RenderingCancelledException;
}();
var hasCanvasTypedArrays;
hasCanvasTypedArrays = function () {
return true;
@ -1380,6 +1390,8 @@ function getDefaultSetting(id) {
return globalSettings ? globalSettings.externalLinkRel : DEFAULT_LINK_REL;
case 'enableStats':
return !!(globalSettings && globalSettings.enableStats);
case 'pdfjsNext':
return !!(globalSettings && globalSettings.pdfjsNext);
default:
throw new Error('Unknown default setting: ' + id);
}
@ -1407,6 +1419,7 @@ exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
exports.isValidUrl = isValidUrl;
exports.getFilenameFromUrl = getFilenameFromUrl;
exports.LinkTarget = LinkTarget;
exports.RenderingCancelledException = RenderingCancelledException;
exports.hasCanvasTypedArrays = hasCanvasTypedArrays;
exports.getDefaultSetting = getDefaultSetting;
exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL;
@ -2068,6 +2081,7 @@ var FontFaceObject = displayFontLoader.FontFaceObject;
var FontLoader = displayFontLoader.FontLoader;
var CanvasGraphics = displayCanvas.CanvasGraphics;
var Metadata = displayMetadata.Metadata;
var RenderingCancelledException = displayDOMUtils.RenderingCancelledException;
var getDefaultSetting = displayDOMUtils.getDefaultSetting;
var DOMCanvasFactory = displayDOMUtils.DOMCanvasFactory;
var DOMCMapReaderFactory = displayDOMUtils.DOMCMapReaderFactory;
@ -3298,7 +3312,7 @@ var InternalRenderTask = function InternalRenderTaskClosure() {
cancel: function InternalRenderTask_cancel() {
this.running = false;
this.cancelled = true;
this.callback('cancelled');
this.callback(new RenderingCancelledException('Rendering cancelled, page ' + this.pageNumber, 'canvas'));
},
operatorListChanged: function InternalRenderTask_operatorListChanged() {
if (!this.graphicsReady) {
@ -3363,8 +3377,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() {
}
};
}();
exports.version = '1.7.401';
exports.build = '57d9a64c';
exports.version = '1.8.173';
exports.build = 'c5199d08';
exports.getDocument = getDocument;
exports.PDFDataRangeTransport = PDFDataRangeTransport;
exports.PDFWorker = PDFWorker;
@ -4331,8 +4345,8 @@ if (!globalScope.PDFJS) {
globalScope.PDFJS = {};
}
var PDFJS = globalScope.PDFJS;
PDFJS.version = '1.7.401';
PDFJS.build = '57d9a64c';
PDFJS.version = '1.8.173';
PDFJS.build = 'c5199d08';
PDFJS.pdfBug = false;
if (PDFJS.verbosity !== undefined) {
sharedUtil.setVerbosityLevel(PDFJS.verbosity);
@ -4392,6 +4406,7 @@ PDFJS.disableWebGL = PDFJS.disableWebGL === undefined ? true : PDFJS.disableWebG
PDFJS.externalLinkTarget = PDFJS.externalLinkTarget === undefined ? LinkTarget.NONE : PDFJS.externalLinkTarget;
PDFJS.externalLinkRel = PDFJS.externalLinkRel === undefined ? DEFAULT_LINK_REL : PDFJS.externalLinkRel;
PDFJS.isEvalSupported = PDFJS.isEvalSupported === undefined ? true : PDFJS.isEvalSupported;
PDFJS.pdfjsNext = PDFJS.pdfjsNext === undefined ? false : PDFJS.pdfjsNext;
PDFJS.getDocument = displayAPI.getDocument;
PDFJS.PDFDataRangeTransport = displayAPI.PDFDataRangeTransport;
PDFJS.PDFWorker = displayAPI.PDFWorker;
@ -6670,8 +6685,8 @@ exports.TilingPattern = TilingPattern;
"use strict";
var pdfjsVersion = '1.7.401';
var pdfjsBuild = '57d9a64c';
var pdfjsVersion = '1.8.173';
var pdfjsBuild = 'c5199d08';
var pdfjsSharedUtil = __w_pdfjs_require__(0);
var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
var pdfjsDisplayAPI = __w_pdfjs_require__(3);
@ -6702,6 +6717,7 @@ exports.createObjectURL = pdfjsSharedUtil.createObjectURL;
exports.removeNullCharacters = pdfjsSharedUtil.removeNullCharacters;
exports.shadow = pdfjsSharedUtil.shadow;
exports.createBlob = pdfjsSharedUtil.createBlob;
exports.RenderingCancelledException = pdfjsDisplayDOMUtils.RenderingCancelledException;
exports.getFilenameFromUrl = pdfjsDisplayDOMUtils.getFilenameFromUrl;
exports.addLinkAttributes = pdfjsDisplayDOMUtils.addLinkAttributes;

Просмотреть файл

@ -27676,9 +27676,6 @@ var Font = function FontClosure() {
}
glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
glyphId = glyphId + delta & 0xFFFF;
if (glyphId === 0) {
continue;
}
mappings.push({
charCode: j,
glyphId: glyphId
@ -36373,8 +36370,8 @@ exports.Type1Parser = Type1Parser;
"use strict";
var pdfjsVersion = '1.7.401';
var pdfjsBuild = '57d9a64c';
var pdfjsVersion = '1.8.173';
var pdfjsBuild = 'c5199d08';
var pdfjsCoreWorker = __w_pdfjs_require__(17);
;
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;

Просмотреть файл

@ -0,0 +1,31 @@
/* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals Components, PdfJs, Services */
"use strict";
/*
* pdfjschildbootstrap-enabled.js loads into the content process to
* take care of initializing our built-in version of pdfjs when
* running remote. It will only be run when PdfJs.enable is true.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://pdf.js/PdfJs.jsm");
if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
// register various pdfjs factories that hook us into content loading.
PdfJs.ensureRegistered();
}

Просмотреть файл

@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals Components, PdfjsContentUtils, PdfJs, Services */
/* globals Components, PdfjsContentUtils */
"use strict";
@ -21,14 +21,7 @@
* initializing our built-in version of pdfjs when running remote.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://pdf.js/PdfJs.jsm");
Components.utils.import("resource://pdf.js/PdfjsContentUtils.jsm");
// init content utils shim pdfjs will use to access privileged apis.
PdfjsContentUtils.init();
if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
// register various pdfjs factories that hook us into content loading.
PdfJs.updateRegistration();
}

Просмотреть файл

@ -4944,7 +4944,7 @@ var PDFPageView = function PDFPageViewClosure() {
if (paintTask === self.paintTask) {
self.paintTask = null;
}
if (error === 'cancelled') {
if (error instanceof pdfjsLib.RenderingCancelledException) {
self.error = null;
return Promise.resolve(undefined);
}

Просмотреть файл

@ -898,3 +898,31 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY emeLearnMoreContextMenu.label "Learn more about DRM…">
<!ENTITY emeLearnMoreContextMenu.accesskey "D">
<!ENTITY updateAvailable.message "Update your &brandShorterName; for the latest in speed and privacy.">
<!ENTITY updateAvailable.whatsnew.label "See whats new.">
<!ENTITY updateAvailable.whatsnew.href "http://www.mozilla.org/">
<!ENTITY updateAvailable.header.message "A new &brandShorterName; update is available.">
<!ENTITY updateAvailable.acceptButton.label "Download Update">
<!ENTITY updateAvailable.acceptButton.accesskey "D">
<!ENTITY updateAvailable.cancelButton.label "Not Now">
<!ENTITY updateAvailable.cancelButton.accesskey "N">
<!ENTITY updateAvailable.panelUI.label "Download &brandShorterName; update">
<!ENTITY updateManual.message "Download a fresh copy of &brandShorterName; and well help you to install it.">
<!ENTITY updateManual.whatsnew.label "See whats new.">
<!ENTITY updateManual.whatsnew.href "http://www.mozilla.org/">
<!ENTITY updateManual.header.message "&brandShorterName; cant update to the latest version.">
<!ENTITY updateManual.acceptButton.label "Download &brandShorterName;">
<!ENTITY updateManual.acceptButton.accesskey "D">
<!ENTITY updateManual.cancelButton.label "Not Now">
<!ENTITY updateManual.cancelButton.accesskey "N">
<!ENTITY updateManual.panelUI.label "Download a fresh copy of &brandShorterName;">
<!ENTITY updateRestart.message "After a quick restart, &brandShorterName; will restore all your open tabs and windows.">
<!ENTITY updateRestart.header.message "Restart &brandShorterName; to apply update.">
<!ENTITY updateRestart.acceptButton.label "Restart and Restore">
<!ENTITY updateRestart.acceptButton.accesskey "R">
<!ENTITY updateRestart.cancelButton.label "Not Now">
<!ENTITY updateRestart.cancelButton.accesskey "N">
<!ENTITY updateRestart.panelUI.label "Restart &brandShorterName; to apply update">

Просмотреть файл

@ -761,15 +761,6 @@ customizeTips.tip0.learnMore = Learn more
# LOCALIZATION NOTE (customizeMode.tabTitle): %S is brandShortName
customizeMode.tabTitle = Customize %S
# LOCALIZATION NOTE(appmenu.*.description, appmenu.*.label): these are used for
# the appmenu labels and buttons that appear when an update is staged for
# installation or a background update has failed and a manual download is required.
# %S is brandShortName
appmenu.restartNeeded.description = Restart %S to apply updates
appmenu.updateFailed.description = Background update failed, please download update
appmenu.restartBrowserButton.label = Restart %S
appmenu.downloadUpdateButton.label = Download Update
# LOCALIZATION NOTE : FILE Reader View is a feature name and therefore typically used as a proper noun.
readingList.promo.firstUse.readerView.title = Reader View

Просмотреть файл

@ -17,6 +17,10 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentWebRTC",
"resource:///modules/ContentWebRTC.jsm");
var gEMEUIObserver = function(subject, topic, data) {
let win = subject.top;
@ -52,3 +56,29 @@ function getMessageManagerForWindow(aContentWindow) {
Services.obs.addObserver(gEMEUIObserver, "mediakeys-request", false);
Services.obs.addObserver(gDecoderDoctorObserver, "decoder-doctor-notification", false);
// ContentWebRTC observer registration.
const kWebRTCObserverTopics = ["getUserMedia:request",
"recording-device-stopped",
"PeerConnection:request",
"recording-device-events",
"recording-window-ended"];
function webRTCObserve(aSubject, aTopic, aData) {
ContentWebRTC.observe(aSubject, aTopic, aData);
}
for (let topic of kWebRTCObserverTopics) {
Services.obs.addObserver(webRTCObserve, topic, false);
}
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT)
Services.obs.addObserver(processShutdown, "content-child-shutdown", false);
function processShutdown() {
for (let topic of kWebRTCObserverTopics) {
Services.obs.removeObserver(webRTCObserve, topic);
}
Services.obs.removeObserver(processShutdown, "content-child-shutdown");
}

Просмотреть файл

@ -17,36 +17,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
const kBrowserURL = "chrome://browser/content/browser.xul";
this.ContentWebRTC = {
_initialized: false,
init() {
if (this._initialized)
return;
this._initialized = true;
Services.obs.addObserver(handleGUMRequest, "getUserMedia:request", false);
Services.obs.addObserver(handleGUMStop, "recording-device-stopped", false);
Services.obs.addObserver(handlePCRequest, "PeerConnection:request", false);
Services.obs.addObserver(updateIndicators, "recording-device-events", false);
Services.obs.addObserver(removeBrowserSpecificIndicator, "recording-window-ended", false);
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT)
Services.obs.addObserver(processShutdown, "content-child-shutdown", false);
},
uninit() {
Services.obs.removeObserver(handleGUMRequest, "getUserMedia:request");
Services.obs.removeObserver(handleGUMStop, "recording-device-stopped");
Services.obs.removeObserver(handlePCRequest, "PeerConnection:request");
Services.obs.removeObserver(updateIndicators, "recording-device-events");
Services.obs.removeObserver(removeBrowserSpecificIndicator, "recording-window-ended");
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT)
Services.obs.removeObserver(processShutdown, "content-child-shutdown");
this._initialized = false;
},
// Called only for 'unload' to remove pending gUM prompts in reloaded frames.
handleEvent(aEvent) {
let contentWindow = aEvent.target.defaultView;
@ -59,6 +29,28 @@ this.ContentWebRTC = {
}
},
// This observer is registered in ContentObservers.js to avoid
// loading this .jsm when WebRTC is not in use.
observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "getUserMedia:request":
handleGUMRequest(aSubject, aTopic, aData);
break;
case "recording-device-stopped":
handleGUMStop(aSubject, aTopic, aData);
break;
case "PeerConnection:request":
handlePCRequest(aSubject, aTopic, aData);
break;
case "recording-device-events":
updateIndicators(aSubject, aTopic, aData);
break;
case "recording-window-ended":
removeBrowserSpecificIndicator(aSubject, aTopic, aData);
break;
}
},
receiveMessage(aMessage) {
switch (aMessage.name) {
case "rtcpeer:Allow":
@ -410,7 +402,3 @@ function getMessageManagerForWindow(aContentWindow) {
throw e;
}
}
function processShutdown() {
ContentWebRTC.uninit();
}

Просмотреть файл

@ -110,14 +110,31 @@
display: none;
}
#PanelUI-menu-button[badge-status="update-succeeded"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
#PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
height: 13px;
border-radius: 50%;
box-shadow: none;
border: 1px solid -moz-dialog;
/* "!important" is necessary to override the rule in toolbarbutton.css */
margin: -9px 0 0 !important;
margin-inline-end: -6px !important;
min-width: 16px;
min-height: 16px;
}
#PanelUI-menu-button[badge-status="update-failed"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
background: #D90000 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center;
height: 13px;
#PanelUI-update-restart-menu-item::after,
#PanelUI-update-available-menu-item::after,
#PanelUI-update-manual-menu-item::after {
background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
border-radius: 50%;
}
#PanelUI-update-restart-menu-item,
#PanelUI-update-available-menu-item,
#PanelUI-update-manual-menu-item {
list-style-image: url(chrome://branding/content/icon16.png);
}
#PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
@ -444,11 +461,11 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]),
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-update-status,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > .PanelUI-notification-menu-item,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-label,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-icon,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > toolbarseparator,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-footer-inner > toolbarseparator,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-customize,
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-help:not([panel-multiview-anchor="true"]) {
opacity: .5;
@ -566,28 +583,15 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
top: 25%;
}
#PanelUI-update-status[update-status]::after,
#PanelUI-footer-addons > toolbarbutton::after {
#PanelUI-footer-addons > toolbarbutton::after,
.PanelUI-notification-menu-item::after {
content: "";
width: 14px;
height: 14px;
width: 16px;
height: 16px;
margin-inline-end: 16.5px;
box-shadow: 0px 1px 0px rgba(255,255,255,.2) inset, 0px -1px 0px rgba(0,0,0,.1) inset, 0px 1px 0px rgba(12,27,38,.2);
border-radius: 2px;
background-size: contain;
display: -moz-box;
}
#PanelUI-update-status[update-status="succeeded"]::after {
background-image: url(chrome://browser/skin/update-badge.svg);
background-color: #74BF43;
}
#PanelUI-update-status[update-status="failed"]::after {
background-image: url(chrome://browser/skin/update-badge-failed.svg);
background-color: #D90000;
}
#PanelUI-footer-addons > toolbarbutton {
background-color: #FFEFBF;
/* Force border to override `#PanelUI-footer-addons > toolbarbutton` selector below */
@ -646,7 +650,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
margin: 0;
}
#PanelUI-update-status,
.PanelUI-notification-menu-item,
#PanelUI-help,
#PanelUI-fxa-label,
#PanelUI-fxa-icon,
@ -665,16 +669,13 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
-moz-box-orient: horizontal;
}
#PanelUI-update-status {
.PanelUI-notification-menu-item {
border-top: 1px solid var(--panel-separator-color);
}
#PanelUI-update-status {
border-bottom: 1px solid transparent;
margin-bottom: -1px;
}
#PanelUI-update-status > .toolbarbutton-text {
.PanelUI-notification-menu-item > .toolbarbutton-text {
width: 0; /* Fancy cropping solution for flexbox. */
}
@ -683,7 +684,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
min-width: 46px;
}
#PanelUI-update-status > .toolbarbutton-text,
.PanelUI-notification-menu-item > .toolbarbutton-text,
#PanelUI-fxa-label > .toolbarbutton-text,
#PanelUI-footer-addons > toolbarbutton > .toolbarbutton-text,
#PanelUI-customize > .toolbarbutton-text {
@ -698,7 +699,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
display: none;
}
#PanelUI-update-status > .toolbarbutton-icon,
.PanelUI-notification-menu-item > .toolbarbutton-icon,
#PanelUI-fxa-label > .toolbarbutton-icon,
#PanelUI-fxa-icon > .toolbarbutton-icon,
#PanelUI-customize > .toolbarbutton-icon,
@ -725,16 +726,14 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
padding-inline-start: 0px;
}
#PanelUI-update-status {
/* descend from #PanelUI-footer to add specificity, or else the
padding-inline-start will be overridden */
#PanelUI-footer > .PanelUI-notification-menu-item {
width: calc(@menuPanelWidth@ + 30px);
padding-inline-start: 15px;
border-inline-start-style: none;
}
#PanelUI-update-status {
list-style-image: url(chrome://branding/content/icon16.png);
}
#PanelUI-fxa-label,
#PanelUI-fxa-icon {
list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
@ -980,34 +979,19 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
#PanelUI-update-status {
.PanelUI-notification-menu-item {
color: black;
}
#PanelUI-update-status[update-status="succeeded"] {
background-color: hsla(96,65%,75%,.5);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
.PanelUI-notification-menu-item:not([disabled]):hover {
background-color: hsla(96,65%,75%,.8);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
.PanelUI-notification-menu-item:not([disabled]):hover:active {
background-color: hsl(96,65%,75%);
}
#PanelUI-update-status[update-status="failed"] {
background-color: hsla(359,69%,84%,.5);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
background-color: hsla(359,69%,84%,.8);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
background-color: hsl(359,69%,84%);
}
#PanelUI-quit:not([disabled]):hover {
background-color: #d94141;
outline-color: #c23a3a;
@ -1712,7 +1696,9 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
linear-gradient(rgba(255,255,255,0.3), transparent);
}
#PanelUI-update-status {
#PanelUI-update-restart-menu-item,
#PanelUI-update-available-menu-item,
#PanelUI-update-manual-menu-item {
list-style-image: url(chrome://branding/content/icon32.png);
}
@ -1749,7 +1735,7 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
-moz-image-region: rect(0, 32px, 32px, 0);
}
#PanelUI-update-status > .toolbarbutton-icon,
.PanelUI-notification-menu-item > .toolbarbutton-icon,
#PanelUI-fxa-label > .toolbarbutton-icon,
#PanelUI-fxa-icon > .toolbarbutton-icon,
#PanelUI-customize > .toolbarbutton-icon,

Просмотреть файл

@ -306,3 +306,11 @@ html|*#webRTC-previewVideo {
}
}
%endif
/* UPDATE */
.popup-notification-icon[popupid="update-available"],
.popup-notification-icon[popupid="update-manual"],
.popup-notification-icon[popupid="update-restart"] {
background: #74BF43 url(chrome://browser/skin/notification-icons.svg#update) no-repeat center;
border-radius: 50%;
}

Просмотреть файл

@ -44,6 +44,12 @@
fill: white;
fill-opacity: 1;
}
#update-icon {
stroke: #fff;
stroke-width: 3px;
stroke-linecap: round;
}
</style>
<defs>
@ -62,6 +68,7 @@
<path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z" />
<path id="popup-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
<path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
<path id="update-icon" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14" />
<clipPath id="blocked-clipPath">
<path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
@ -109,6 +116,7 @@
<use id="screen-sharing" xlink:href="#screen-icon"/>
<use id="screen-indicator" xlink:href="#screen-icon"/>
<use id="screen-blocked" class="blocked" xlink:href="#screen-icon" />
<use id="update" xlink:href="#update-icon" />
<path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 7.1 KiB

После

Ширина:  |  Высота:  |  Размер: 7.3 KiB

Просмотреть файл

@ -2,5 +2,7 @@
- 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" width="10px" height="10px">
<polygon points="4,9 4,5 2,5 5,1 8,5 6,5 6,9" fill="#fff"/>
<line x1="5" x2="5" y1="9" y2="2" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
<line x1="5" x2="2" y1="2" y2="5" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
<line x1="5" x2="8" y1="2" y2="5" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 355 B

После

Ширина:  |  Высота:  |  Размер: 577 B

Просмотреть файл

@ -277,7 +277,7 @@ TabTarget.prototype = {
return this._form;
},
// Get a promise of the root form returned by a listTabs request. This promise
// Get a promise of the root form returned by a getRoot request. This promise
// is cached.
get root() {
if (!this._root) {
@ -288,7 +288,7 @@ TabTarget.prototype = {
_getRoot: function () {
return new Promise((resolve, reject) => {
this.client.listTabs(response => {
this.client.mainRoot.getRoot(response => {
if (response.error) {
reject(new Error(response.error + ": " + response.message));
return;

Просмотреть файл

@ -457,10 +457,6 @@ netmonitor.tab.timings=Timings
# in the network details pane identifying the stack-trace tab.
netmonitor.tab.stackTrace=Stack Trace
# LOCALIZATION NOTE (netmonitor.tab.preview): This is the label displayed
# in the network details pane identifying the preview tab.
netmonitor.tab.preview=Preview
# LOCALIZATION NOTE (netmonitor.tab.security): This is the label displayed
# in the network details pane identifying the security tab.
netmonitor.tab.security=Security

Просмотреть файл

@ -2,6 +2,7 @@
* 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/. */
@import "chrome://devtools/skin/widgets.css";
@import "resource://devtools/client/shared/components/splitter/split-box.css";
@import "resource://devtools/client/shared/components/tree/tree-view.css";
@import "resource://devtools/client/shared/components/tabs/tabs.css";

Просмотреть файл

@ -12,7 +12,6 @@ DevToolsModules(
'monitor-panel.js',
'network-details-panel.js',
'params-panel.js',
'preview-panel.js',
'properties-view.js',
'request-list-content.js',
'request-list-empty-notice.js',

Просмотреть файл

@ -1,35 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
const { div, iframe } = DOM;
/*
* Preview panel component
* Display HTML content within a sandbox enabled iframe
*/
function PreviewPanel({ request }) {
const htmlBody = request.responseContent ?
request.responseContent.content.text : "";
return (
div({ className: "panel-container" },
iframe({
sandbox: "",
srcDoc: typeof htmlBody === "string" ? htmlBody : "",
})
)
);
}
PreviewPanel.displayName = "PreviewPanel";
PreviewPanel.propTypes = {
request: PropTypes.object.isRequired,
};
module.exports = PreviewPanel;

Просмотреть файл

@ -10,7 +10,6 @@ const {
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index");
const { Filters } = require("../utils/filter-predicates");
const { L10N } = require("../utils/l10n");
const { getSelectedRequest } = require("../selectors/index");
@ -20,7 +19,6 @@ const TabPanel = createFactory(require("devtools/client/shared/components/tabs/t
const CookiesPanel = createFactory(require("./cookies-panel"));
const HeadersPanel = createFactory(require("./headers-panel"));
const ParamsPanel = createFactory(require("./params-panel"));
const PreviewPanel = createFactory(require("./preview-panel"));
const ResponsePanel = createFactory(require("./response-panel"));
const SecurityPanel = createFactory(require("./security-panel"));
const StackTracePanel = createFactory(require("./stack-trace-panel"));
@ -29,7 +27,6 @@ const TimingsPanel = createFactory(require("./timings-panel"));
const COOKIES_TITLE = L10N.getStr("netmonitor.tab.cookies");
const HEADERS_TITLE = L10N.getStr("netmonitor.tab.headers");
const PARAMS_TITLE = L10N.getStr("netmonitor.tab.params");
const PREVIEW_TITLE = L10N.getStr("netmonitor.tab.preview");
const RESPONSE_TITLE = L10N.getStr("netmonitor.tab.response");
const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security");
const STACK_TRACE_TITLE = L10N.getStr("netmonitor.tab.stackTrace");
@ -100,13 +97,6 @@ function TabboxPanel({
},
SecurityPanel({ request }),
),
Filters.html(request) &&
TabPanel({
id: "preview",
title: PREVIEW_TITLE,
},
PreviewPanel({ request }),
),
)
);
}

Просмотреть файл

@ -12,7 +12,7 @@ const {
} = require("./index");
const { getFormatStr } = require("../utils/l10n");
const { getToplevelWindow } = require("sdk/window/utils");
const { getToplevelWindow } = require("../utils/window");
const { Task: { spawn } } = require("devtools/shared/task");
const e10s = require("../utils/e10s");
const Services = require("Services");

Просмотреть файл

@ -8,7 +8,7 @@
const { Task } = require("devtools/shared/task");
const flags = require("devtools/shared/flags");
const { getToplevelWindow } = require("sdk/window/utils");
const { getToplevelWindow } = require("../utils/window");
const { DOM: dom, createClass, addons, PropTypes } =
require("devtools/client/shared/vendor/react");

Просмотреть файл

@ -15,7 +15,7 @@ const { require } = BrowserLoader({
});
const { Task } = require("devtools/shared/task");
const Telemetry = require("devtools/client/shared/telemetry");
const { loadSheet } = require("sdk/stylesheet/utils");
const { loadAgentSheet } = require("./utils/css");
const { createFactory, createElement } =
require("devtools/client/shared/vendor/react");
@ -42,9 +42,10 @@ let bootstrap = {
init: Task.async(function* () {
// Load a special UA stylesheet to reset certain styles such as dropdown
// lists.
loadSheet(window,
"resource://devtools/client/responsive.html/responsive-ua.css",
"agent");
loadAgentSheet(
window,
"resource://devtools/client/responsive.html/responsive-ua.css"
);
this.telemetry.toolOpened("responsive");
let store = this.store = Store();
let provider = createElement(Provider, { store }, App());

Просмотреть файл

@ -8,8 +8,7 @@ const { Ci } = require("chrome");
const promise = require("promise");
const { Task } = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const { getOwnerWindow } = require("sdk/tabs/utils");
const { startup } = require("sdk/window/helpers");
const { startup } = require("./utils/window");
const message = require("./utils/message");
const { swapToInnerBrowser } = require("./browser/swap");
const { EmulationFront } = require("devtools/shared/fronts/emulation");
@ -145,7 +144,7 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
* @return boolean
*/
isActiveForWindow(window) {
return [...this.activeTabs.keys()].some(t => getOwnerWindow(t) === window);
return [...this.activeTabs.keys()].some(t => t.ownerGlobal === window);
},
/**
@ -209,7 +208,7 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
}
},
setMenuCheckFor: Task.async(function* (tab, window = getOwnerWindow(tab)) {
setMenuCheckFor: Task.async(function* (tab, window = tab.ownerGlobal) {
yield startup(window);
let menu = window.document.getElementById("menu_responsiveUI");

Просмотреть файл

@ -7,18 +7,18 @@
const TEST_URL = "data:text/html;charset=utf-8,";
const tabUtils = require("sdk/tabs/utils");
const { startup } = require("sdk/window/helpers");
const { startup } = require("devtools/client/responsive.html/utils/window");
const activateTab = (tab) => new Promise(resolve => {
let { tabContainer } = tabUtils.getOwnerWindow(tab).gBrowser;
let { gBrowser } = tab.ownerGlobal;
let { tabContainer } = gBrowser;
tabContainer.addEventListener("TabSelect", function listener({type}) {
tabContainer.removeEventListener(type, listener);
resolve();
});
tabUtils.activateTab(tab);
gBrowser.selectedTab = tab;
});
const isMenuChecked = () => {

Просмотреть файл

@ -7,8 +7,6 @@
const TEST_URL = "data:text/html;charset=utf-8,";
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const isMenuCheckedFor = ({document}) => {
let menu = document.getElementById("menu_responsiveUI");
return menu.getAttribute("checked") === "true";
@ -22,7 +20,7 @@ add_task(function* () {
function* (browser) {
let tab = gBrowser.getTabForBrowser(browser);
is(window1, getMostRecentBrowserWindow(),
is(window1, Services.wm.getMostRecentWindow("navigator:browser"),
"The new window is the active one");
ok(!isMenuCheckedFor(window1),
@ -41,7 +39,7 @@ add_task(function* () {
yield BrowserTestUtils.closeWindow(window1);
is(window, getMostRecentBrowserWindow(),
is(window, Services.wm.getMostRecentWindow("navigator:browser"),
"The original window is the active one");
ok(!isMenuCheckedFor(window),

Просмотреть файл

@ -36,7 +36,6 @@ const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.htm
const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
const { getOwnerWindow } = require("sdk/tabs/utils");
const asyncStorage = require("devtools/shared/async-storage");
const { addDevice, removeDevice } = require("devtools/client/shared/devices");
@ -72,7 +71,7 @@ const { ResponsiveUIManager } = require("resource://devtools/client/responsivede
var openRDM = Task.async(function* (tab) {
info("Opening responsive design mode");
let manager = ResponsiveUIManager;
let ui = yield manager.openIfNeeded(getOwnerWindow(tab), tab);
let ui = yield manager.openIfNeeded(tab.ownerGlobal, tab);
info("Responsive design mode opened");
return { ui, manager };
});
@ -83,7 +82,7 @@ var openRDM = Task.async(function* (tab) {
var closeRDM = Task.async(function* (tab, options) {
info("Closing responsive design mode");
let manager = ResponsiveUIManager;
yield manager.closeIfNeeded(getOwnerWindow(tab), tab, options);
yield manager.closeIfNeeded(tab.ownerGlobal, tab, options);
info("Responsive design mode closed");
});

Просмотреть файл

@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { getDOMWindowUtils } = require("./window");
/**
* Synchronously loads an agent style sheet from `uri` and adds it to the list of
* additional style sheets of the document. The sheets added takes effect immediately,
* and only on the document of the `window` given.
*/
function loadAgentSheet(window, url) {
let winUtils = getDOMWindowUtils(window);
winUtils.loadSheetUsingURIString(url, winUtils.AGENT_SHEET);
}
exports.loadAgentSheet = loadAgentSheet;

Просмотреть файл

@ -5,7 +5,9 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'css.js',
'e10s.js',
'l10n.js',
'message.js',
'window.js',
)

Просмотреть файл

@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Ci } = require("chrome");
const Services = require("Services");
/**
* Returns the `nsIDOMWindow` toplevel window for any child/inner window
*/
function getToplevelWindow(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
exports.getToplevelWindow = getToplevelWindow;
function getDOMWindowUtils(window) {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
}
exports.getDOMWindowUtils = getDOMWindowUtils;
/**
* Check if the given browser window has finished the startup.
* @params {nsIDOMWindow} window
*/
const isStartupFinished = (window) =>
window.gBrowserInit &&
window.gBrowserInit.delayedStartupFinished;
function startup(window) {
return new Promise(resolve => {
if (isStartupFinished(window)) {
resolve(window);
return;
}
Services.obs.addObserver(function listener({ subject }) {
if (subject === window) {
Services.obs.removeObserver(listener, "browser-delayed-startup-finished");
resolve(window);
}
}, "browser-delayed-startup-finished", false);
});
}
exports.startup = startup;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Двоичные данные
devtools/docs/resources/thread-states.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 84 KiB

Просмотреть файл

@ -18,7 +18,7 @@ const {getCurrentZoom, getFrameOffsets} = require("devtools/shared/layout/utils"
loader.lazyGetter(this, "clipboardHelper",
() => Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper));
loader.lazyGetter(this, "l10n",
() => Services.strings.createBundle("chrome://devtools/locale/eyedropper.properties"));
() => Services.strings.createBundle("chrome://devtools-shared/locale/eyedropper.properties"));
const ZOOM_LEVEL_PREF = "devtools.eyedropper.zoom";
const FORMAT_PREF = "devtools.defaultColorUnit";

Просмотреть файл

@ -244,84 +244,95 @@ RootActor.prototype = {
this._processActors.clear();
},
/**
* Gets the "root" form, which lists all the global actors that affect the entire
* browser. This can replace usages of `listTabs` that only wanted the global actors
* and didn't actually care about tabs.
*/
onGetRoot: function () {
let reply = {
from: this.actorID,
};
// Create global actors
if (!this._globalActorPool) {
this._globalActorPool = new ActorPool(this.conn);
this.conn.addActorPool(this._globalActorPool);
}
this._createExtraActors(this._parameters.globalActorFactories, this._globalActorPool);
// List the global actors
this._appendExtraActors(reply);
return reply;
},
/* The 'listTabs' request and the 'tabListChanged' notification. */
/**
* Handles the listTabs request. The actors will survive until at least
* the next listTabs request.
*
* WARNING This can be a very expensive operation, especially if there are many
* open tabs. It will cause us to visit every tab, load a frame script, start a
* debugger server, and read some data. With lazy tab support (bug 906076), this
* would trigger any lazy tabs to be loaded, greatly increasing resource usage. Avoid
* this method whenever possible.
*/
onListTabs: function () {
onListTabs: async function () {
let tabList = this._parameters.tabList;
if (!tabList) {
return { from: this.actorID, error: "noTabs",
message: "This root actor has no browser tabs." };
}
/*
* Now that a client has requested the list of tabs, we reattach the onListChanged
* listener in order to be notified if the list of tabs changes again in the future.
*/
// Now that a client has requested the list of tabs, we reattach the onListChanged
// listener in order to be notified if the list of tabs changes again in the future.
tabList.onListChanged = this._onTabListChanged;
/*
* Walk the tab list, accumulating the array of tab actors for the
* reply, and moving all the actors to a new ActorPool. We'll
* replace the old tab actor pool with the one we build here, thus
* retiring any actors that didn't get listed again, and preparing any
* new actors to receive packets.
*/
// Walk the tab list, accumulating the array of tab actors for the reply, and moving
// all the actors to a new ActorPool. We'll replace the old tab actor pool with the
// one we build here, thus retiring any actors that didn't get listed again, and
// preparing any new actors to receive packets.
let newActorPool = new ActorPool(this.conn);
let tabActorList = [];
let selected;
return tabList.getList().then((tabActors) => {
for (let tabActor of tabActors) {
if (tabActor.exited) {
// Tab actor may have exited while we were gathering the list.
continue;
}
if (tabActor.selected) {
selected = tabActorList.length;
}
tabActor.parentID = this.actorID;
newActorPool.addActor(tabActor);
tabActorList.push(tabActor);
}
/* DebuggerServer.addGlobalActor support: create actors. */
if (!this._globalActorPool) {
this._globalActorPool = new ActorPool(this.conn);
this.conn.addActorPool(this._globalActorPool);
}
this._createExtraActors(this._parameters.globalActorFactories,
this._globalActorPool);
/*
* Drop the old actorID -> actor map. Actors that still mattered were
* added to the new map; others will go away.
*/
if (this._tabActorPool) {
this.conn.removeActorPool(this._tabActorPool);
}
this._tabActorPool = newActorPool;
this.conn.addActorPool(this._tabActorPool);
let reply = {
"from": this.actorID,
"selected": selected || 0,
"tabs": tabActorList.map(actor => actor.form())
};
/* If a root window is accessible, include its URL. */
if (this.url) {
reply.url = this.url;
let tabActors = await tabList.getList();
for (let tabActor of tabActors) {
if (tabActor.exited) {
// Tab actor may have exited while we were gathering the list.
continue;
}
if (tabActor.selected) {
selected = tabActorList.length;
}
tabActor.parentID = this.actorID;
newActorPool.addActor(tabActor);
tabActorList.push(tabActor);
}
/* DebuggerServer.addGlobalActor support: name actors in 'listTabs' reply. */
this._appendExtraActors(reply);
// Start with the root reply, which includes the global actors for the whole browser.
let reply = this.onGetRoot();
return reply;
// Drop the old actorID -> actor map. Actors that still mattered were added to the
// new map; others will go away.
if (this._tabActorPool) {
this.conn.removeActorPool(this._tabActorPool);
}
this._tabActorPool = newActorPool;
this.conn.addActorPool(this._tabActorPool);
// We'll extend the reply here to also mention all the tabs.
Object.assign(reply, {
selected: selected || 0,
tabs: tabActorList.map(actor => actor.form()),
});
return reply;
},
onGetTab: function (options) {
onGetTab: async function (options) {
let tabList = this._parameters.tabList;
if (!tabList) {
return { error: "noTabs",
@ -331,22 +342,25 @@ RootActor.prototype = {
this._tabActorPool = new ActorPool(this.conn);
this.conn.addActorPool(this._tabActorPool);
}
return tabList.getTab(options)
.then(tabActor => {
tabActor.parentID = this.actorID;
this._tabActorPool.addActor(tabActor);
return { tab: tabActor.form() };
}, error => {
if (error.error) {
let tabActor;
try {
tabActor = await tabList.getTab(options);
} catch (error) {
if (error.error) {
// Pipe expected errors as-is to the client
return error;
}
return {
error: "noTab",
message: "Unexpected error while calling getTab(): " + error
};
});
return error;
}
return {
error: "noTab",
message: "Unexpected error while calling getTab(): " + error
};
}
tabActor.parentID = this.actorID;
this._tabActorPool.addActor(tabActor);
return { tab: tabActor.form() };
},
onGetWindow: function ({ outerWindowID }) {
@ -583,16 +597,17 @@ RootActor.prototype = {
};
RootActor.prototype.requestTypes = {
"listTabs": RootActor.prototype.onListTabs,
"getTab": RootActor.prototype.onGetTab,
"getWindow": RootActor.prototype.onGetWindow,
"listAddons": RootActor.prototype.onListAddons,
"listWorkers": RootActor.prototype.onListWorkers,
"listServiceWorkerRegistrations": RootActor.prototype.onListServiceWorkerRegistrations,
"listProcesses": RootActor.prototype.onListProcesses,
"getProcess": RootActor.prototype.onGetProcess,
"echo": RootActor.prototype.onEcho,
"protocolDescription": RootActor.prototype.onProtocolDescription
getRoot: RootActor.prototype.onGetRoot,
listTabs: RootActor.prototype.onListTabs,
getTab: RootActor.prototype.onGetTab,
getWindow: RootActor.prototype.onGetWindow,
listAddons: RootActor.prototype.onListAddons,
listWorkers: RootActor.prototype.onListWorkers,
listServiceWorkerRegistrations: RootActor.prototype.onListServiceWorkerRegistrations,
listProcesses: RootActor.prototype.onListProcesses,
getProcess: RootActor.prototype.onGetProcess,
echo: RootActor.prototype.onEcho,
protocolDescription: RootActor.prototype.onProtocolDescription
};
exports.RootActor = RootActor;

Просмотреть файл

@ -5,10 +5,9 @@
/* global XPCNativeWrapper */
const { Cu } = require("chrome");
const { Cu, Cc, Ci } = require("chrome");
const events = require("sdk/event/core");
const { on: systemOn, off: systemOff } = require("sdk/system/events");
const protocol = require("devtools/shared/protocol");
const { CallWatcherActor } = require("devtools/server/actors/call-watcher");
const { createValueGrip } = require("devtools/server/actors/object");
@ -19,6 +18,10 @@ const {
webAudioSpec
} = require("devtools/shared/specs/webaudio");
const { WebAudioFront } = require("devtools/shared/fronts/webaudio");
const observerService = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
const AUDIO_NODE_DEFINITION = require("devtools/server/actors/utils/audionodes.json");
const ENABLE_AUTOMATION = false;
const AUTOMATION_GRANULARITY = 2000;
@ -413,7 +416,6 @@ exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
// to the client in any way.
this._nativeToActorID = new Map();
this._onDestroyNode = this._onDestroyNode.bind(this);
this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
this._onGlobalCreated = this._onGlobalCreated.bind(this);
},
@ -555,7 +557,13 @@ exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
return;
}
this._initialized = false;
systemOff("webaudio-node-demise", this._onDestroyNode);
try {
observerService.removeObserver(this, "webaudio-node-demise");
} catch (e) {
// Maybe we've shutdown already and it's too late to remove the observer. So avoid
// NS_ERROR_FAILURE errors with this silent try/catch.
}
off(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
off(this.tabActor, "window-ready", this._onGlobalCreated);
@ -619,7 +627,7 @@ exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
* Called on first audio node creation, signifying audio context usage
*/
_onStartContext: function () {
systemOn("webaudio-node-demise", this._onDestroyNode);
observerService.addObserver(this, "webaudio-node-demise", false);
emit(this, "start-context");
},
@ -677,20 +685,31 @@ exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
emit(this, "create-node", actor);
},
/** Called when `webaudio-node-demise` is triggered,
* and emits the associated actor to the front if found.
/**
* Called by the ObserverService when webaudio-node-demise events are emitted.
*/
_onDestroyNode: function ({data}) {
// Cast to integer.
let nativeID = ~~data;
observe: function (subject, topic, data) {
switch (topic) {
case "webaudio-node-demise":
// Cast the data to an integer.
this._handleNodeDestroyed(~~data);
break;
}
},
let actor = this._getActorByNativeID(nativeID);
/**
* Handles `webaudio-node-demise` events. Emits the associated actor to the front if
* found.
* @param {Number} nodeNativeID The ID for the audio node.
*/
_handleNodeDestroyed: function (nodeNativeID) {
let actor = this._getActorByNativeID(nodeNativeID);
// If actorID exists, emit; in the case where we get demise
// notifications for a document that no longer exists,
// the mapping should not be found, so we do not emit an event.
if (actor) {
this._nativeToActorID.delete(nativeID);
this._nativeToActorID.delete(nodeNativeID);
emit(this, "destroy-node", actor);
}
},
@ -736,7 +755,7 @@ exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
if (this._nativeToActorID) {
this._nativeToActorID.clear();
}
systemOff("webaudio-node-demise", this._onDestroyNode);
observerService.removeObserver(this, "webaudio-node-demise");
}
});

Просмотреть файл

@ -1652,6 +1652,13 @@ RootClient.prototype = {
constructor: RootClient,
/**
* Gets the "root" form, which lists all the global actors that affect the entire
* browser. This can replace usages of `listTabs` that only wanted the global actors
* and didn't actually care about tabs.
*/
getRoot: DebuggerClient.requester({ type: "getRoot" }),
/**
* List the open tabs.
*
* @param function onResponse

Просмотреть файл

@ -3143,6 +3143,21 @@ ContentChild::DeallocPURLClassifierChild(PURLClassifierChild* aActor)
return true;
}
PURLClassifierLocalChild*
ContentChild::AllocPURLClassifierLocalChild(const URIParams& aUri,
const nsCString& aTables)
{
return new URLClassifierLocalChild();
}
bool
ContentChild::DeallocPURLClassifierLocalChild(PURLClassifierLocalChild* aActor)
{
MOZ_ASSERT(aActor);
delete aActor;
return true;
}
// The IPC code will call this method asking us to assign an event target to new
// actors created by the ContentParent.
already_AddRefed<nsIEventTarget>

Просмотреть файл

@ -610,6 +610,7 @@ public:
return mFontFamilies;
}
// PURLClassifierChild
virtual PURLClassifierChild*
AllocPURLClassifierChild(const Principal& aPrincipal,
const bool& aUseTrackingProtection,
@ -617,6 +618,13 @@ public:
virtual bool
DeallocPURLClassifierChild(PURLClassifierChild* aActor) override;
// PURLClassifierLocalChild
virtual PURLClassifierLocalChild*
AllocPURLClassifierLocalChild(const URIParams& aUri,
const nsCString& aTables) override;
virtual bool
DeallocPURLClassifierLocalChild(PURLClassifierLocalChild* aActor) override;
nsTArray<LookAndFeelInt>&
LookAndFeelCache() {
return mLookAndFeelCache;

Просмотреть файл

@ -5158,6 +5158,9 @@ ContentParent::RecvRecordChildEvents(nsTArray<mozilla::Telemetry::ChildEventData
return IPC_OK();
}
//////////////////////////////////////////////////////////////////
// PURLClassifierParent
PURLClassifierParent*
ContentParent::AllocPURLClassifierParent(const Principal& aPrincipal,
const bool& aUseTrackingProtection,
@ -5200,6 +5203,48 @@ ContentParent::DeallocPURLClassifierParent(PURLClassifierParent* aActor)
return true;
}
//////////////////////////////////////////////////////////////////
// PURLClassifierLocalParent
PURLClassifierLocalParent*
ContentParent::AllocPURLClassifierLocalParent(const URIParams& aURI,
const nsCString& aTables)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<URLClassifierLocalParent> actor = new URLClassifierLocalParent();
return actor.forget().take();
}
mozilla::ipc::IPCResult
ContentParent::RecvPURLClassifierLocalConstructor(PURLClassifierLocalParent* aActor,
const URIParams& aURI,
const nsCString& aTables)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aActor);
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
NS_WARNING("Failed to DeserializeURI");
return IPC_FAIL_NO_REASON(this);
}
auto* actor = static_cast<URLClassifierLocalParent*>(aActor);
return actor->StartClassify(uri, aTables);
}
bool
ContentParent::DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aActor);
RefPtr<URLClassifierLocalParent> actor =
dont_AddRef(static_cast<URLClassifierLocalParent*>(aActor));
return true;
}
mozilla::ipc::IPCResult
ContentParent::RecvClassifyLocal(const URIParams& aURI, const nsCString& aTables,
nsresult *aRv, nsTArray<nsCString>* aResults)

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше