Revision control

Copy as Markdown

Other Tools

<?xml version="1.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/. -->
<!DOCTYPE bindings [
<!ENTITY % commNotificationDTD SYSTEM "chrome://communicator/locale/notification.dtd">
%commNotificationDTD;
<!ENTITY % globalNotificationDTD SYSTEM "chrome://global/locale/notification.dtd">
%globalNotificationDTD;
]>
<bindings id="browserNotificationBindings"
<binding id="browser-notificationbox"
<implementation implements="nsIObserver, nsIFormSubmitObserver, nsIWebProgressListener, nsIWebProgressListener2">
<field name="_stringBundle" readonly="true">
<![CDATA[
Services.strings.createBundle("chrome://communicator/locale/notification.properties");
]]>
</field>
<field name="_brandStringBundle" readonly="true">
<![CDATA[
Services.strings.createBundle("chrome://branding/locale/brand.properties");
]]>
</field>
<field name="_placesBundle" readonly="true">
<![CDATA[
Services.strings.createBundle("chrome://communicator/locale/places/places.properties");
]]>
</field>
<field name="wrappedJSObject">this</field>
<field name="_activeBrowser">null</field>
<property name="activeBrowser" readonly="true">
<getter>
<![CDATA[
if (!this._activeBrowser) {
var browsers = this.getElementsByTagNameNS(XUL_NS, "browser");
for (var i = 0; this._activeBrowser = browsers.item(i); i++)
if (!this._activeBrowser.hidden)
break;
}
return this._activeBrowser;
]]>
</getter>
</property>
<field name="_cwu">null</field>
<property name="contentWindowUtils" readonly="true">
<getter>
<![CDATA[
if (!this._cwu)
this._cwu = this.activeBrowser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return this._cwu;
]]>
</getter>
</property>
<field name="usePrivateBrowsing" readonly="true">
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext)
.usePrivateBrowsing
</field>
<method name="onDocumentChange">
<body>
<![CDATA[
this.crashNotified = false;
if (this.popupCount) {
this.popupCount = 0;
this.notifyPopupCountChanged();
}
this.removeTransientNotifications();
]]>
</body>
</method>
<method name="addProgressListener">
<body>
<![CDATA[
if (this.activeBrowser && !this._addedProgressListener) {
this.activeBrowser.webProgress
.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_SECURITY |
Ci.nsIWebProgress.NOTIFY_LOCATION |
Ci.nsIWebProgress.NOTIFY_REFRESH);
this._addedProgressListener = true;
}
]]>
</body>
</method>
<field name="lastMessage">"EnterInsecureMessage"</field>
<field name="lastState">0</field>
<method name="onSecurityChange">
<parameter name="aWebProgress"/>
<parameter name="aRequest"/>
<parameter name="aState"/>
<body>
<![CDATA[
if (aState < 0)
aState = this.lastState;
const nsIWebProgressListener = Ci.nsIWebProgressListener;
var pref = "security.warn_leaving_secure";
var message = "EnterInsecureMessage";
var priority = this.PRIORITY_WARNING_LOW;
var pane = "ssl_pane";
var buttons = [];
if (aState & nsIWebProgressListener.STATE_IS_SECURE) {
pref = "security.warn_entering_secure";
message = "EnterSecureMessage";
priority = this.PRIORITY_INFO_LOW;
} else if (aState & nsIWebProgressListener.STATE_IS_BROKEN) {
pref = "security.warn_viewing_mixed";
message = "MixedContentMessage";
priority = this.PRIORITY_CRITICAL_LOW;
}
if (aState & nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT &&
Services.prefs.getBoolPref("security.warn_mixed_active_content")) {
pref = "security.warn_mixed_active_content";
message = "MixedActiveContentMessage";
priority = this.PRIORITY_CRITICAL_LOW;
} else if (aState & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT &&
Services.prefs.getBoolPref("security.warn_mixed_active_content")) {
pref = "security.warn_mixed_active_content";
message = "BlockedActiveContentMessage";
priority = this.PRIORITY_INFO_LOW;
this.lastState = aState & ~nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
const nsIWebNavigation = Ci.nsIWebNavigation;
buttons = [{
label: this._stringBundle.GetStringFromName("SecurityKeepBlocking.label"),
accessKey: this._stringBundle.GetStringFromName("SecurityKeepBlocking.accesskey"),
callback: this.onSecurityChange.bind(this, null, null, -1)
}, {
label: this._stringBundle.GetStringFromName("SecurityUnblock.label"),
accessKey: this._stringBundle.GetStringFromName("SecurityUnblock.accesskey"),
callback: this.reloadPage.bind(this,
nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT |
nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE)
}];
} else if (aState & nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT &&
Services.prefs.getBoolPref("privacy.warn_tracking_content")) {
pref = "privacy.warn_tracking_content";
message = "TrackingContentMessage";
priority = this.PRIORITY_WARNING_LOW;
pane = "security_pane";
} else if (aState & nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT &&
Services.prefs.getBoolPref("privacy.warn_tracking_content")) {
pref = "privacy.warn_tracking_content";
message = "BlockedTrackingContentMessage";
priority = this.PRIORITY_INFO_LOW;
pane = "security_pane";
if (!this.usePrivateBrowsing) {
this.lastState = aState & ~nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
buttons = [{
label: this._stringBundle.GetStringFromName("SecurityKeepBlocking.label"),
accessKey: this._stringBundle.GetStringFromName("SecurityKeepBlocking.accesskey"),
callback: this.onSecurityChange.bind(this, null, null, -1)
}, {
label: this._stringBundle.GetStringFromName("SecurityUnblock.label"),
accessKey: this._stringBundle.GetStringFromName("SecurityUnblock.accesskey"),
callback: () => {
Services.perms.add(this.activeBrowser.currentURI,
"trackingprotection",
Ci.nsIPermissionManager.ALLOW_ACTION);
this.reloadPage();
}
}];
}
} else if (aState & nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT &&
Services.prefs.getBoolPref("security.warn_mixed_display_content")) {
pref = "security.warn_mixed_display_content";
message = "MixedDisplayContentMessage";
priority = this.PRIORITY_WARNING_LOW;
} else if (aState & nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT &&
Services.prefs.getBoolPref("security.warn_mixed_display_content")) {
pref = "security.warn_mixed_display_content";
message = "BlockedDisplayContentMessage";
priority = this.PRIORITY_INFO_LOW;
}
if (this.lastMessage == message)
return false;
var box = this.getNotificationWithValue(this.lastMessage);
if (box)
box.close();
this.lastMessage = message;
if (!Services.prefs.getBoolPref(pref))
return true;
if ("goPreferences" in window) {
buttons.push({
label: this._stringBundle.GetStringFromName("SecurityPreferences.label"),
accessKey: this._stringBundle.GetStringFromName("SecurityPreferences.accesskey"),
callback: function() {
goPreferences(pane);
return true;
}
});
}
var text = this._stringBundle.GetStringFromName(message);
box = this.appendNotification(text, message, null, priority, buttons);
box.persistence = 1;
box.timeout = Date.now() + 20000; // 20 seconds
return true;
]]>
</body>
</method>
<method name="onLocationChange">
<parameter name="aWebProgress" />
<parameter name="aRequest" />
<parameter name="aLocation" />
<parameter name="aFlags" />
<body>
<![CDATA[
const nsIWebProgressListener = Ci.nsIWebProgressListener;
if (aWebProgress.DOMWindow == this.activeBrowser.contentWindow &&
!(aFlags & nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT))
this.onDocumentChange();
]]>
</body>
</method>
<method name="onRefreshAttempted">
<parameter name="aWebProgress" />
<parameter name="aURI" />
<parameter name="aDelay" />
<parameter name="aSameURI" />
<body>
<![CDATA[
if (Services.prefs.getBoolPref("accessibility.blockautorefresh")) {
let brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
let refreshButtonText =
this._stringBundle.GetStringFromName("refreshBlocked.goButton");
let refreshButtonAccesskey =
this._stringBundle.GetStringFromName("refreshBlocked.goButton.accesskey");
let message =
this._stringBundle.formatStringFromName(aSameURI ? "refreshBlocked.refreshLabel"
: "refreshBlocked.redirectLabel",
[brandShortName], 1);
let notification = this.getNotificationWithValue("refresh-blocked");
if (notification) {
notification.label = message;
} else {
let buttons = [{
label: refreshButtonText,
accessKey: refreshButtonAccesskey,
callback: function (aNotification, aButton) {
var refreshURI = aNotification.webProgress
.QueryInterface(Ci.nsIRefreshURI);
refreshURI.forceRefreshURI(aNotification.uri, null,
aNotification.delay, true);
}
}];
notification =
this.appendNotification(message, "refresh-blocked", null,
this.PRIORITY_INFO_MEDIUM, buttons);
}
// In the case of a meta refresh, the location has already
// changed. But in the case of an HTTP header refresh, the
// location changes synchronously after this call returns.
// Set the persistence to 1 temporarily to stop this from
// immediately clobbering the location bar (bug 516441),
// but set the persistence back to 0 as soon as possible.
setTimeout(function() { notification.persistence = 0; }, 0);
notification.persistence = 1;
notification.webProgress = aWebProgress;
notification.uri = aURI;
notification.delay = aDelay;
return false;
}
return true;
]]>
</body>
</method>
<method name="notify">
<parameter name="aFormElement"/>
<parameter name="aWindow"/>
<parameter name="aActionURI"/>
<parameter name="aCancel"/>
<body>
<![CDATA[
aCancel.value = false;
if (!aFormElement || !aWindow || !aActionURI)
return;
var browser = this.activeBrowser;
// inactive sidebar panel:
if (!browser || !browser.docShell || !browser.docShell.securityUI)
return;
// not our window:
if (aWindow.top != browser.contentWindow)
return;
// pref disabled:
if (!Services.prefs.getBoolPref("security.warn_submit_insecure"))
return;
// HTTPS uninteresting:
if (aActionURI.schemeIs("https"))
return;
// javascript doesn't hit the network:
if (aActionURI.schemeIs("javascript"))
return;
// PSM handles HTTPS source:
var uri;
try {
uri = aFormElement.nodePrincipal.URI;
} catch (e) {}
if (!uri)
uri = aFormElement.ownerDocument.documentURIObject;
if (uri.schemeIs("https"))
return;
var warn = { value: true };
var prompt = Services.prompt;
aCancel.value = prompt.confirmEx(
aWindow,
this._stringBundle.GetStringFromName("SecurityTitle"),
this._stringBundle.GetStringFromName("PostToInsecureFromInsecureMessage"),
prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1,
this._stringBundle.GetStringFromName("PostToInsecureContinue"),
null, null,
this._stringBundle.GetStringFromName("PostToInsecureFromInsecureShowAgain"),
warn) != 0;
if (!aCancel.value)
Services.prefs.setBoolPref("security.warn_submit_insecure", warn.value);
]]>
</body>
</method>
<method name="observe">
<parameter name="aSubject" />
<parameter name="aTopic" />
<parameter name="aData" />
<body>
<![CDATA[
var browser = this.activeBrowser;
// inactive sidebar panel:
if (!browser || !browser.docShell)
return;
switch (aTopic) {
case "indexedDB-permissions-prompt":
var requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
if (contentWindow.top == browser.contentWindow)
this.promptIndexedDB(requestor, contentWindow, "permissions");
break;
case "indexedDB-quota-prompt":
var requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
if (contentWindow.top == browser.contentWindow)
this.promptIndexedDB(requestor, contentWindow, "quota", aData);
break;
case "indexedDB-quota-cancel":
var requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
if (contentWindow.top == browser.contentWindow)
this.cancelIndexedDB(requestor, contentWindow, "quota", aData);
break;
case "addon-install-blocked":
var installInfo = aSubject.wrappedJSObject;
if (installInfo.browser == browser)
this.addonInstallBlocked(installInfo);
break;
case "addon-install-complete":
var installInfo = aSubject.wrappedJSObject;
if (installInfo.browser == browser)
this.addonInstallComplete(installInfo);
break;
case "addon-install-disabled":
var installInfo = aSubject.wrappedJSObject;
if (installInfo.browser == browser)
this.addonInstallDisabled(installInfo);
break;
case "addon-install-failed":
var installInfo = aSubject.wrappedJSObject;
if (installInfo.browser == browser)
this.addonInstallFailed(installInfo);
break;
case "addon-install-started":
var installInfo = aSubject.wrappedJSObject;
if (installInfo.browser == browser)
this.addonInstallStarted(installInfo);
break;
case "offline-cache-update-completed":
var doc = browser.contentDocument;
if (!doc.documentElement)
break;
var manifest = doc.documentElement.getAttribute("manifest");
if (!manifest)
break;
try {
var manifestURI = Services.io.newURI(manifest,
doc.characterSet,
doc.documentURIObject);
var cacheUpdate =
aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
if (manifestURI.equals(cacheUpdate.manifestURI))
this.checkUsage(manifestURI);
} catch (e) {
alert(e);
}
break;
case "nsPref:changed":
if (aData == "privacy.popups.showBrowserMessage") {
if (Services.prefs.getBoolPref(aData))
return;
var popupNotification = this.getNotificationWithValue("popup-blocked");
if (popupNotification)
this.removeNotification(popupNotification);
}
if (aData == "dom.disable_open_during_load") {
// remove notifications when popup blocking has been turned off
if (Services.prefs.getBoolPref(aData) || !this.popupCount)
return;
var popupNotification = this.getNotificationWithValue("popup-blocked");
if (popupNotification)
this.removeNotification(popupNotification);
this.popupCount = 0;
this.notifyPopupCountChanged();
}
break;
case "perm-changed":
// If all permissions are cleared aSubject is null.
if (!aSubject)
return;
var permission = aSubject.QueryInterface(Ci.nsIPermission);
if (permission.type != "popup" || aData != "added" || !this.popupCount)
return;
if (permission.matchesURI(this.activeBrowser.currentURI, false)) {
var popupNotification = this.getNotificationWithValue("popup-blocked");
if (popupNotification)
this.removeNotification(popupNotification);
this.popupCount = 0;
this.notifyPopupCountChanged();
}
break;
}
]]>
</body>
</method>
<field name="CrashSubmit">null</field>
<field name="crashNotified">false</field>
<method name="openURLPref">
<parameter name="aPref"/>
<body>
<![CDATA[
var url = Services.urlFormatter.formatURLPref(aPref);
var nsIBrowserDOMWindow = Ci.nsIBrowserDOMWindow;
var browserWin;
var whereToOpen = Services.prefs.getIntPref("browser.link.open_external");
if (whereToOpen != nsIBrowserDOMWindow.OPEN_NEWWINDOW) {
browserWin = Services.wm.getMostRecentWindow("navigator:browser");
}
if (!browserWin) {
try {
browserURL = Services.prefs.getCharPref("browser.chromeURL");
} catch (ex) {}
window.openDialog(browserURL, "_blank", "chrome,all,dialog=no", url);
} else {
if (whereToOpen == nsIBrowserDOMWindow.OPEN_CURRENTWINDOW)
browserWin.loadURI(url);
else {
// new tab
var browser = browserWin.getBrowser();
var newTab = browser.addTab(url);
browser.selectedTab = newTab;
}
browserWin.content.focus();
}
return true;
]]>
</body>
</method>
<method name="makeNicePluginName">
<parameter name="aName"/>
<body>
<![CDATA[
// Clean up the plugin name by stripping off any trailing version
// numbers or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
// Do this by first stripping the numbers, etc. off the end, and
// then removing "Plugin" (and then trimming to get rid of any
// whitespace). Otherwise, something like "Java(TM) Plug-in
// 1.7.0_07" gets mangled.
var newName = aName.replace(/[\s\d\.\-\_\(\)]+$/, "").replace(/\bplug-?in\b/i, "").trim();
return newName;
]]>
</body>
</method>
<method name="getPluginUI">
<parameter name="aPlugin"/>
<parameter name="aAnonId"/>
<body>
<![CDATA[
return aPlugin.ownerDocument.getAnonymousElementByAttribute(aPlugin, "anonid", aAnonId);
]]>
</body>
</method>
<method name="addLinkClickCallback">
<parameter name="linkNode"/>
<parameter name="callback"/>
<body>
<![CDATA[
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
// We use event bubbling for the event listeners.
let callbackArgs = Array.from(arguments).slice(2);
linkNode.addEventListener("click",
function(aEvent) {
if (!aEvent.isTrusted)
return;
if (aEvent.button != 0)
return;
aEvent.preventDefault();
aEvent.stopPropagation();
if (callbackArgs.length == 0)
callbackArgs = [ aEvent ];
callback.apply(this, callbackArgs);
}.bind(this));
linkNode.addEventListener("keydown",
function(aEvent) {
if (!aEvent.isTrusted)
return;
if (aEvent.keyCode != aEvent.DOM_VK_RETURN)
return;
aEvent.preventDefault();
aEvent.stopPropagation();
if (callbackArgs.length == 0)
callbackArgs = [ aEvent ];
callback.apply(this, callbackArgs);
}.bind(this));
]]>
</body>
</method>
<!-- Callback for user clicking "submit a report" link -->
<method name="submitReport">
<parameter name="pluginDumpID"/>
<parameter name="plugin"/>
<body>
<![CDATA[
var keyVals = {};
if (plugin) {
let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
if (userComment)
keyVals.PluginUserComment = userComment;
if (this.getPluginUI(plugin, "submitURLOptIn").checked)
keyVals.PluginContentURL = plugin.ownerDocument.URL;
}
this.CrashSubmit.submit(pluginDumpID, { extraExtraKeyVals: keyVals,
recordSubmission: true });
]]>
</body>
</method>
<!-- Callback for user clicking a "reload page" link -->
<method name="reloadPage">
<parameter name="flags"/>
<body>
<![CDATA[
this.activeBrowser.reloadWithFlags(flags);
]]>
</body>
</method>
<!-- Callback for user clicking the help icon -->
<method name="openHelpPage">
<body>
<![CDATA[
//XXX Ratty need in app help here.
openHelp("plugin-crashed", "chrome://communicator/locale/help/suitehelp.rdf");
]]>
</body>
</method>
<method name="showPluginCrashedNotification">
<parameter name="pluginDumpID"/>
<parameter name="messageString"/>
<body>
<![CDATA[
// If there's already an existing notification bar, don't do anything.
var notification = this.getNotificationWithValue("plugin-crashed");
if (notification)
return;
// Configure the notification bar
var priority = this.PRIORITY_WARNING_MEDIUM;
var reloadLabel = this._stringBundle.GetStringFromName("crashedpluginsMessage.reloadButton.label");
var reloadKey = this._stringBundle.GetStringFromName("crashedpluginsMessage.reloadButton.accesskey");
var submitLabel = this._stringBundle.GetStringFromName("crashedpluginsMessage.submitButton.label");
var submitKey = this._stringBundle.GetStringFromName("crashedpluginsMessage.submitButton.accesskey");
var buttons = [{
label: reloadLabel,
accessKey: reloadKey,
popup: null,
callback: this.reloadPage.bind(this)
}];
if (this.CrashSubmit && pluginDumpID) {
let submitButton = {
label: submitLabel,
accessKey: submitKey,
popup: null,
callback: this.submitReport.bind(this, pluginDumpID)
};
buttons.push(submitButton);
}
var notification = this.appendNotification(messageString, "plugin-crashed",
null, priority, buttons);
// Add the "learn more" link.
var link = notification.ownerDocument.createElementNS(XULNS, "label");
link.className = "text-link";
link.setAttribute("value", this._stringBundle.GetStringFromName("crashedpluginsMessage.learnMore"));
this.addLinkClickCallback(link, this.openHelpPage);
var description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
description.appendChild(link);
]]>
</body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (!aEvent.isTrusted)
return;
]]>
</body>
</method>
<method name="playSoundForBlockedPopup">
<body>
<![CDATA[
const kCustomSound = 1;
var playSound = Services.prefs.getBoolPref("privacy.popups.sound_enabled");
if (playSound) {
var sound = Cc["@mozilla.org/sound;1"]
.createInstance(Ci.nsISound);
var soundType = Services.prefs.getIntPref("privacy.popups.sound_type");
if (soundType == kCustomSound) {
var soundUrlSpec = Services.prefs.getCharPref("privacy.popups.sound_url");
var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]
.getService(Ci.nsIFileProtocolHandler);
var file = fileHandler.getFileFromURLSpec(soundUrlSpec);
if (file.exists()) {
var soundUrl = fileHandler.newFileURI(file);
sound.play(soundUrl);
return;
}
}
// Either a custom sound is selected which does not exist
// or the system beep was selected, so make the system beep.
sound.beep();
}
]]>
</body>
</method>
<field name="popupCount">0</field>
<method name="notifyPopupCountChanged">
<body>
<![CDATA[
this.dispatchEvent(new Event("PopupCountChanged",
{ bubbles: true, cancelable: true }));
]]>
</body>
</method>
<method name="allowPopupsForSite">
<parameter name="aEvent"/>
<body>
<![CDATA[
Services.perms.add(this.activeBrowser.currentURI, "popup",
Ci.nsIPermissionManager.ALLOW_ACTION);
this.removeCurrentNotification();
]]>
</body>
</method>
<method name="offlineAppRequested">
<parameter name="aDocument"/>
<body>
<![CDATA[
var documentURI = aDocument.documentURIObject;
var pm = Services.perms;
if (pm.testExactPermission(documentURI, "offline-app") !=
Ci.nsIPermissionManager.UNKNOWN_ACTION)
return;
var host = documentURI.asciiHost;
var notificationName = "offline-app-requested-" + host;
var notification = this.getNotificationWithValue(notificationName);
if (notification)
notification.documents.push(aDocument);
else {
var buttons = [{
label: this._stringBundle.GetStringFromName("offlineApps.always"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.always.accesskey"),
callback: function() {
pm.add(documentURI, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
.getService(Ci.nsIOfflineCacheUpdateService);
notification.documents.forEach(function(aDocument) {
if (!aDocument.documentElement)
return;
var manifest = aDocument.documentElement.getAttribute("manifest");
if (!manifest)
return;
try {
var manifestURI =
Services.io.newURI(manifest,
aDocument.characterSet,
aDocument.documentURIObject);
updateService.scheduleUpdate(manifestURI,
aDocument.documentURIObject,
window);
} catch (e) {
}
});
}
}, {
label: this._stringBundle.GetStringFromName("offlineApps.never"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.never.accesskey"),
callback: function() {
pm.add(documentURI, "offline-app", Ci.nsIPermissionManager.DENY_ACTION);
}
}, {
label: this._stringBundle.GetStringFromName("offlineApps.later"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.later.accesskey"),
callback: function() { /* no-op */ }
}];
var messageString = this._stringBundle.formatStringFromName(this.usePrivateBrowsing ?
"offlineApps.private" : "offlineApps.permissions", [host], 1);
var priority = this.PRIORITY_INFO_LOW;
notification = this.appendNotification(messageString, notificationName,
null, priority,
this.usePrivateBrowsing ?
null : buttons);
notification.documents = [aDocument];
}
]]>
</body>
</method>
<method name="checkUsage">
<parameter name="aURI"/>
<body>
<![CDATA[
if (Services.perms.testExactPermission(aURI, "offline-app") ==
Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN)
return;
var host = aURI.asciiHost;
var usage = 0;
var cacheService = Cc["@mozilla.org/network/application-cache-service;1"]
.getService(Ci.nsIApplicationCacheService);
cacheService.getGroups().forEach(function(aGroup) {
var uri = Services.io.newURI(aGroup);
if (uri.asciiHost == host)
usage += cacheService.getActiveCache(aGroup).usage;
});
var warnQuota = Services.prefs.getIntPref("offline-apps.quota.warn");
if (usage < warnQuota * 1024)
return;
var message = this._stringBundle.formatStringFromName("offlineApps.quota", [host, warnQuota / 1024], 2);
var priority = this.PRIORITY_WARNING_MEDIUM;
this.appendNotification(message, "offline-app-usage", null,
priority, null);
Services.perms.add(aURI, "offline-app",
Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
]]>
</body>
</method>
<method name="showRightsNotification">
<body>
<![CDATA[
var rightsBundle = Services.strings.createBundle("chrome://branding/locale/aboutRights.properties");
var buttonLabel = rightsBundle.GetStringFromName("buttonLabel");
var buttonAccessKey = rightsBundle.GetStringFromName("buttonAccessKey");
var productName = this._brandStringBundle.GetStringFromName("brandFullName");
var notifyRightsText = rightsBundle.formatStringFromName("notifyRightsText2", [productName], 1);
var buttons = [{
label: buttonLabel,
accessKey: buttonAccessKey,
popup: null,
callback: function (aNotificationBox, aButton) {
var browser = document.getBindingParent(aNotificationBox);
browser.addTab("about:rights", { focusNewTab: true });
}
}];
var box = this.appendNotification(notifyRightsText, "about-rights",
null, this.PRIORITY_INFO_LOW,
buttons);
box.persistence = 3; // arbitrary number, just so bar sticks around for a bit
]]>
</body>
</method>
<method name="showPlacesLockedWarning">
<body>
<![CDATA[
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var message = this._placesBundle.formatStringFromName("lockPrompt.text", [brandShortName], 1);
var buttons = [{
label: this._placesBundle.GetStringFromName("lockPromptInfoButton.label"),
accessKey: this._placesBundle.GetStringFromName("lockPromptInfoButton.accesskey"),
popup: null,
callback: function() {
openHelp("places-locked", "chrome://communicator/locale/help/suitehelp.rdf");
}
}];
var box = this.appendNotification(message, "places-locked", null,
this.PRIORITY_CRITICAL_MEDIUM,
buttons);
box.persistence = -1; // until user closes it
]]>
</body>
</method>
<method name="showUpdateWarning">
<body>
<![CDATA[
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var message = this._stringBundle.formatStringFromName("updatePrompt.text", [brandShortName], 1);
var buttons = [{
label: this._stringBundle.GetStringFromName("updatePromptCheckButton.label"),
accessKey: this._stringBundle.GetStringFromName("updatePromptCheckButton.accesskey"),
popup: null,
callback: function() {
Cc["@mozilla.org/updates/update-prompt;1"]
.createInstance(Ci.nsIUpdatePrompt)
.checkForUpdates();
}
}];
var box = this.appendNotification(message, "update-warning", null,
this.PRIORITY_CRITICAL_MEDIUM,
buttons);
box.persistence = -1; // until user closes it
]]>
</body>
</method>
<method name="removeNotifications">
<parameter name="aNotifications"/>
<body>
<![CDATA[
aNotifications.forEach(function(value) {
var box = this.getNotificationWithValue(value);
if (box)
this.removeNotification(box);
}, this);
]]>
</body>
</method>
<method name="lwthemeInstallRequest">
<parameter name="aHost"/>
<parameter name="aCallback"/>
<body>
<![CDATA[
var message = this._stringBundle.formatStringFromName("lwthemeInstallRequest.message", [aHost], 1);
var buttons = [{
label: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton.accesskey"),
popup: null,
callback: aCallback
}];
var box = this.appendNotification(message,
"lwtheme-install-request", null,
this.PRIORITY_INFO_MEDIUM,
buttons);
box.persistence = 1;
]]>
</body>
</method>
<method name="lwthemeInstallNotification">
<parameter name="aCallback"/>
<body>
<![CDATA[
var message = this._stringBundle.GetStringFromName("lwthemeInstallNotification.message");
var buttons = [{
label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton.accesskey"),
callback: aCallback
}, {
label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton.accesskey"),
callback: function() {
window.toEM("addons://list/theme");
}
}];
var box = this.appendNotification(message,
"lwtheme-install-notification",
null, this.PRIORITY_INFO_MEDIUM,
buttons);
box.persistence = 1;
box.timeout = Date.now() + 20000; // 20 seconds
]]>
</body>
</method>
<method name="lwthemeNeedsRestart">
<parameter name="aNewThemeName"/>
<body>
<![CDATA[
var message = this._stringBundle.formatStringFromName("lwthemeNeedsRestart.message", [aNewThemeName], 1);
var buttons = [{
label: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton.accesskey"),
popup: null,
callback: function() {
BrowserUtils.restartApplication();
}
}];
var box = this.appendNotification(message,
"lwtheme-install-notification",
null,this.PRIORITY_INFO_MEDIUM,
buttons);
box.persistence = 1;
box.timeout = Date.now() + 20000; // 20 seconds
]]>
</body>
</method>
<method name="promptIndexedDB">
<parameter name="aRequestor"/>
<parameter name="aWindow"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
var host = aWindow.document.documentURIObject.asciiHost;
var property = this.usePrivateBrowsing ? "offlineApps.private" :
"offlineApps." + aTopic;
var message = this._stringBundle.formatStringFromName(property,
[host, aData], 2);
var observer = aRequestor.getInterface(Ci.nsIObserver);
var buttons = [{
label: this._stringBundle.GetStringFromName("offlineApps.always"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.always.accesskey"),
popup: null,
callback: function allowIndexedDB() {
clearTimeout(box.timeout);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.ALLOW_ACTION);
}
}, {
label: this._stringBundle.GetStringFromName("offlineApps.never"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.never.accesskey"),
popup: null,
callback: function denyIndexedDB() {
clearTimeout(box.timeout);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.DENY_ACTION);
}
}, {
label: this._stringBundle.GetStringFromName("offlineApps.later"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.later.accesskey"),
popup: null,
callback: function laterIndexedDB() {
clearTimeout(box.timeout);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.UNKNOWN_ACTION);
}
}];
var box = this.appendNotification(message,
"indexedDB-" + aTopic + "-prompt",
null, this.PRIORITY_INFO_LOW,
this.usePrivateBrowsing ?
null : buttons);
box.timeout = setTimeout(function() {
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.UNKNOWN_ACTION);
if (box.parentNode)
box.parentNode.removeNotification(box);
}, 300000); // 5 minutes
]]>
</body>
</method>
<method name="cancelIndexedDB">
<parameter name="aRequestor"/>
<parameter name="aWindow"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
var popupNotification = this.getNotificationWithValue("indexedDB-" + aTopic + "-prompt");
if (popupNotification) {
clearTimeout(popupNotification.timeout);
this.removeNotification(popupNotification);
}
var observer = aRequestor.getInterface(Ci.nsIObserver);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.UNKNOWN_ACTION);
]]>
</body>
</method>
<method name="addonInstallBlocked">
<parameter name="installInfo"/>
<body>
<![CDATA[
var host;
try {
// this fails with nsSimpleURIs like data: URIs
host = installInfo.originatingURI.host;
} catch (ex) {
host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
}
var notificationName = "addon-install-blocked";
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var messageString = this._stringBundle.formatStringFromName("xpinstallPromptWarning",
[brandShortName, host], 2);
var buttons = [{
label: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton"),
accessKey: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton.accesskey"),
popup: null,
callback: function allowInstall() {
installInfo.install();
return false;
}
}];
if (!this.getNotificationWithValue(notificationName)) {
var priority = this.PRIORITY_WARNING_MEDIUM;
this.appendNotification(messageString, notificationName,
null, priority, buttons);
}
]]>
</body>
</method>
<method name="addonInstallCancelled">
<parameter name="installInfo"/>
<body>
<![CDATA[
var tmp = {};
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
var notificationName = "addon-install-cancelled";
var messageString = this._stringBundle.GetStringFromName("addonDownloadCancelled");
messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
var buttons = [{
label: this._stringBundle.GetStringFromName("addonDownloadRestartButton"),
accessKey: this._stringBundle.GetStringFromName("addonDownloadRestartButton.accesskey"),
popup: null,
callback: function() {
var weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]
.getService(Ci.amIWebInstallListener);
if (weblistener.onWebInstallRequested(installInfo.browser, installInfo.originatingURI,
[aInstall], 1)) {
aInstall.install();
}
}
}];
var priority = this.PRIORITY_INFO_MEDIUM;
this.appendNotification(messageString, notificationName,
null, priority, buttons);
installInfo.installs.every(function(aInstall) {
aInstall.cancel();
});
return true; // the downloading notification closed automatically
]]>
</body>
</method>
<method name="addonInstallComplete">
<parameter name="installInfo"/>
<body>
<![CDATA[
var tmp = {};
ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
var notificationName = "addon-install-complete"
var addonNotification = this.getNotificationWithValue(notificationName);
if (addonNotification)
this.removeNotification(addonNotification);
var buttons = [];
var messageString;
if (installInfo.installs.some(install =>
install.addon.pendingOperations &
tmp.AddonManager.PENDING_INSTALL)) {
messageString = this._stringBundle.GetStringFromName("addonsInstalledNeedsRestart");
buttons.push({
label: this._stringBundle.GetStringFromName("addonInstallRestartButton"),
accessKey: this._stringBundle.GetStringFromName("addonInstallRestartButton.accesskey"),
callback: function () {
BrowserUtils.restartApplication();
}
});
} else {
messageString = this._stringBundle.GetStringFromName("addonsInstalled");
}
if ("toEM" in window) {
buttons.push({
label: this._stringBundle.GetStringFromName("addonInstallManageButton"),
accessKey: this._stringBundle.GetStringFromName("addonInstallManageButton.accesskey"),
callback: function() {
window.toEM("addons://list/extension");
}
});
}
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
messageString = tmp.PluralForm.get(installInfo.installs.length, messageString)
.replace("#1", installInfo.installs[0].name)
.replace("#2", installInfo.installs.length)
.replace("#3", brandShortName);
var priority = this.PRIORITY_WARNING_MEDIUM;
this.appendNotification(messageString, notificationName,
null, priority, buttons);
]]>
</body>
</method>
<method name="addonInstallDisabled">
<parameter name="installInfo"/>
<body>
<![CDATA[
var messageString;
var buttons;
var notificationName = "addon-install-disabled";
if (Services.prefs.prefIsLocked("xpinstall.enabled")) {
messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessageLocked");
buttons = [];
} else {
messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessage");
buttons = [{
label: this._stringBundle.GetStringFromName("xpinstallDisabledButton"),
accessKey: this._stringBundle.GetStringFromName("xpinstallDisabledButton.accesskey"),
popup: null,
callback: function editPrefs() {
Services.prefs.setBoolPref("xpinstall.enabled", true);
return false;
}
}];
}
if (!this.getNotificationWithValue(notificationName)) {
var priority = this.PRIORITY_WARNING_MEDIUM;
this.appendNotification(messageString, notificationName,
null, priority, buttons);
}
]]>
</body>
</method>
<method name="addonInstallFailed">
<parameter name="installInfo"/>
<body>
<![CDATA[
var notificationName = "addon-install-failed";
if (!this.getNotificationWithValue(notificationName)) {
var host;
try {
// this fails with nsSimpleURIs like data: URIs
host = installInfo.originatingURI.host;
} catch (ex) {
host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
}
var error = "addonErrorIncompatible";
var name = installInfo.installs[0].name;
installInfo.installs.some(function(install) {
if (install.error) {
name = install.name;
error = "addonError" + install.error;
return true;
}
if (install.addon.blocklistState ==
Ci.nsIBlocklistService.STATE_BLOCKED) {
name = install.name;
error = "addonErrorBlocklisted";
}
return false;
});
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var version = Services.appinfo.version;
var messageString = this._stringBundle.GetStringFromName(error)
.replace("#1", name)
.replace("#2", host)
.replace("#3", brandShortName)
.replace("#4", version);
var priority = this.PRIORITY_WARNING_MEDIUM;
this.appendNotification(messageString, notificationName,
null, priority, []);
}
]]>
</body>
</method>
<method name="addonInstallStarted">
<parameter name="installInfo"/>
<body>
<![CDATA[
var tmp = {};
ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
if (installInfo.installs.every(function(aInstall) {
return aInstall.state == tmp.AddonManager.STATE_DOWNLOADED;
}))
return;
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
var notificationName = "addon-install-started";
var messageString = this._stringBundle.GetStringFromName("addonDownloading");
messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
var buttons = [{
label: this._stringBundle.GetStringFromName("addonDownloadCancelButton"),
accessKey: this._stringBundle.GetStringFromName("addonDownloadCancelButton.accesskey"),
popup: null,
callback: this.addonInstallCancelled.bind(this, installInfo)
}];
var priority = this.PRIORITY_INFO_MEDIUM;
var box = this.appendNotification(messageString, notificationName,
null, priority, buttons);
box.installInfo = installInfo;
installInfo.installs.forEach(function(aInstall) {
aInstall.addListener(box);
});
]]>
</body>
</method>
<method name="ignoreSafeBrowsingWarning">
<parameter name="aReason"/>
<parameter name="aBlockedInfo"/>
<body>
<![CDATA[
var uri = this.activeBrowser.currentURI;
var flag = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER;
this.activeBrowser.loadURIWithFlags(uri.asciiSpec, flag,
null, null, null);
Services.perms.add(uri, "safe-browsing",
Ci.nsIPermissionManager.ALLOW_ACTION,
Ci.nsIPermissionManager.EXPIRE_SESSION);
var title, label, accessKey, reportName, buttons;
switch (aReason) {
case "phishing":
title = "safebrowsing.deceptiveSite";
label = "safebrowsing.notADeceptiveSiteButton.label";
accessKey = "safebrowsing.notADeceptiveSiteButton.accessKey";
reportName = "PhishMistake";
break;
case "malware":
title = "safebrowsing.reportedAttackSite";
label = "safebrowsing.notAnAttackButton.label";
accessKey = "safebrowsing.notAnAttackButton.accessKey";
reportName = "MalwareMistake";
break;
case "unwanted":
title = "safebrowsing.reportedUnwantedSite";
break;
// No notifications for unknown reasons.
default:
return;
}
title = this._stringBundle.GetStringFromName(title);
buttons = [{
label: this._stringBundle.GetStringFromName("safebrowsing.getMeOutOfHereButton.label"),
accessKey: this._stringBundle.GetStringFromName("safebrowsing.getMeOutOfHereButton.accessKey"),
callback: getMeOutOfHere
}]
if (reportName) {
var tmp = {};
ChromeUtils.import("resource://gre/modules/SafeBrowsing.jsm", tmp);
var reportUrl = tmp.SafeBrowsing.getReportURL(reportName, aBlockedInfo);
// There's no button if we can not get report url, for example
// if the provider of blockedInfo is not Google.
if (reportUrl) {
buttons.push({
label: this._stringBundle.GetStringFromName(label),
accessKey: this._stringBundle.GetStringFromName(accessKey),
callback() { openUILinkIn(reportUrl, "tabfocused"); }
});
}
}
var type = "blocked-badware-page";
var notification = this.getNotificationWithValue(type);
if (notification)
this.removeNotification(notification);
var box = this.appendNotification(title, type, null,
this.PRIORITY_CRITICAL_HIGH,
buttons);
// Persist the notification until the user removes so it
// doesn't get removed on redirects.
box.persistence = -1;
]]>
</body>
</method>
<constructor>
<![CDATA[
var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
Services.obs.addObserver(this, "indexedDB-permissions-prompt");
Services.obs.addObserver(this, "indexedDB-quota-prompt");
Services.obs.addObserver(this, "indexedDB-quota-cancel");
Services.obs.addObserver(this, "addon-install-blocked");
Services.obs.addObserver(this, "addon-install-complete");
Services.obs.addObserver(this, "addon-install-disabled");
Services.obs.addObserver(this, "addon-install-failed");
Services.obs.addObserver(this, "addon-install-started");
Services.obs.addObserver(this, "offline-cache-update-completed");
Services.obs.addObserver(this, "perm-changed");
Services.obs.addObserver(this, "formsubmit");
Services.prefs.addObserver("privacy.popups.showBrowserMessage", this);
Services.prefs.addObserver("dom.disable_open_during_load", this);
this.addProgressListener();
if (AppConstants.MOZ_CRASHREPORTER)
ChromeUtils.import("resource://gre/modules/CrashSubmit.jsm", this);
]]>
</constructor>
<destructor>
<![CDATA[
this.destroy();
]]>
</destructor>
<field name="mDestroyed">false</field>
<!-- This is necessary because the destructor doesn't always get called when
we are removed from a tabbrowser. This will be explicitly called by tabbrowser -->
<method name="destroy">
<body>
<![CDATA[
if (this.mDestroyed)
return;
this.mDestroyed = true;
if (this._addedProgressListener) {
this.activeBrowser.webProgress.removeProgressListener(this);
this._addedProgressListener = false;
}
this._activeBrowser = null;
try {
Services.obs.removeObserver(this, "indexedDB-permissions-prompt");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "indexedDB-quota-prompt");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "indexedDB-quota-cancel");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "addon-install-blocked");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "addon-install-complete");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "addon-install-disabled");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "addon-install-failed");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "addon-install-started");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "offline-cache-update-completed");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "perm-changed");
} catch (ex) {}
try {
Services.obs.removeObserver(this, "formsubmit");
} catch (ex) {}
try {
Services.prefs.removeObserver("privacy.popups.showBrowserMessage", this);
} catch (ex) {}
try {
Services.prefs.removeObserver("dom.disable_open_during_load", this);
} catch (ex) {}
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="DOMContentLoaded" phase="capturing">
<![CDATA[
if (/^about:neterror\?e=netOffline/.test(event.target.documentURI))
event.target.addEventListener("click", function tryAgain(event) {
if (event.target.id == "errorTryAgain")
Services.io.offline = false;
}, true);
]]>
</handler>
<handler event="DOMUpdatePageReport" phase="capturing">
<![CDATA[
var browser = this.activeBrowser;
if (!browser.blockedPopups || browser.blockedPopups.reported != false)
return;
// this.popupCount can be 0, while browser.blockedPopups has not been cleared.
if (!this.popupCount && browser.blockedPopups.length > 1) {
this.popupCount = browser.blockedPopups.length;
} else {
this.popupCount++;
}
this.playSoundForBlockedPopup();
this.notifyPopupCountChanged();
var tmp = {};
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage"))
{
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var message = this._stringBundle.GetStringFromName("popupWarning.message");
message = tmp.PluralForm.get(this.popupCount, message)
.replace("#1", brandShortName)
.replace("#2", this.popupCount);
var notification = this.getNotificationWithValue("popup-blocked");
if (notification) {
notification.label = message;
} else {
var popupButtonText = this._stringBundle.GetStringFromName("popupWarningButton");
var popupButtonAccesskey = this._stringBundle.GetStringFromName("popupWarningButton.accesskey");
var buttons = [{
label: popupButtonText,
accessKey: popupButtonAccesskey,
popup: "popupNotificationMenu",
callback: null
}];
const priority = this.PRIORITY_WARNING_MEDIUM;
this.appendNotification(message, "popup-blocked",
null, priority, buttons);
}
}
]]>
</handler>
<handler event="PluginCrashed" phase="capturing">
<![CDATA[
// Ensure the plugin and event are of the right type.
var plugin = event.target;
var detail = event.detail;
if (!(detail instanceof Ci.nsIPropertyBag2))
return;
var submittedReport = detail.getPropertyAsBool("submittedCrashReport");
var doPrompt = true; // XXX followup for .getPropertyAsBool("doPrompt");
var submitReports = true; // XXX followup for .getPropertyAsBool("submitReports");
var pluginName = detail.getPropertyAsAString("pluginName");
var pluginFilename = detail.getPropertyAsAString("pluginFilename");
var pluginDumpID = detail.getPropertyAsAString("pluginDumpID");
// Remap the plugin name to a more user-presentable form.
pluginName = this.makeNicePluginName(pluginName);
// Force a style flush, so that we ensure our binding is attached.
plugin.clientTop;
// Configure the crashed-plugin placeholder.
var overlay = this.getPluginUI(plugin, "main");
var status;
var statusDiv = this.getPluginUI(plugin, "submitStatus");
if (this.CrashSubmit) {
// Determine which message to show regarding crash reports.
if (submittedReport) { // submitReports && !doPrompt, handled in observer
status = "submitted";
}
else if (!submitReports && !doPrompt) {
status = "noSubmit";
}
else { // doPrompt
status = "please";
this.getPluginUI(plugin, "submitButton").addEventListener("click",
function (event) {
if (event.button != 0 || !event.isTrusted)
return;
this.submitReport(pluginDumpID, plugin);
Services.prefs.setBoolPref("dom.ipc.plugins.reportCrashURL", optInCB.checked);
}.bind(this));
let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
optInCB.checked = Services.prefs.getBoolPref("dom.ipc.plugins.reportCrashURL");
}
// If we're showing the link to manually trigger report submission, we'll
// want to be able to update all the instances of the UI for this crash to
// show an updated message when a report is submitted.
if (doPrompt) {
let observer = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
observe: function(subject, topic, data) {
let propertyBag = subject;
if (!(propertyBag instanceof Ci.nsIPropertyBag2))
return;
// Ignore notifications for other crashes.
if (propertyBag.get("minidumpID") != pluginDumpID)
return;
statusDiv.setAttribute("status", data);
},
handleEvent : function(event) {
// Not expected to be called, just here for the closure.
}
}
// Use a weak reference, so we don't have to remove it...
Services.obs.addObserver(observer, "crash-report-status", true);
// ...alas, now we need something to hold a strong reference to prevent
// it from being GC. But I don't want to manually manage the reference's
// lifetime (which should be no greater than the page).
// Clever solution? Use a closure with an event listener on the statusDiv.
// When it goes away, so do the listener references and the closure.
statusDiv.addEventListener("mozCleverClosureHack", observer);
}
}
// If we don't have a minidumpID, we can't (or didn't) submit anything.
// This can happen if the plugin is killed from the task manager.
if (!pluginDumpID) {
status = "noReport";
}
statusDiv.setAttribute("status", status);
var helpIcon = this.getPluginUI(plugin, "helpIcon");
this.addLinkClickCallback(helpIcon, this.openHelpPage);
var messageString = this._stringBundle.formatStringFromName("crashedpluginsMessage.title", [pluginName], 1);
var crashText = this.getPluginUI(plugin, "crashedText");
crashText.textContent = messageString;
var link = this.getPluginUI(plugin, "reloadLink");
this.addLinkClickCallback(link, this.reloadPage);
overlay.classList.add("visible");
// If a previous plugin on the page was too small and resulted in
// adding a notification bar, then remove it because this plugin
// instance it big enough to serve as in-content notification.
var notification = this.getNotificationWithValue("plugin-crashed");
if (notification)
this.removeNotification(notification, true);
this.crashNotified = true;
]]>
</handler>
<handler event="MozApplicationManifest" phase="capturing">
<![CDATA[
if (!Services.prefs.getBoolPref("browser.offline-apps.notify"))
return;
try {
if (Services.prefs.getBoolPref("offline-apps.allow_by_default"))
return;
} catch (e) {
}
this.offlineAppRequested(event.originalTarget);
]]>
</handler>
<handler event="pageshow" phase="capturing">
<![CDATA[
// |event.persisted| is true when the page is loaded from the
// BF cache, so this code reshows the notification if necessary.
if (!event.persisted)
return;
]]>
</handler>
</handlers>
</binding>
<binding id="popup-notification"
<implementation>
<method name="onLocationChange">
<parameter name="aWebProgress" />
<parameter name="aRequest" />
<parameter name="aLocation" />
<parameter name="aFlags" />
<body>
<![CDATA[
const nsIWebProgressListener = Ci.nsIWebProgressListener;
if (aWebProgress.DOMWindow == this.activeBrowser.contentWindow &&
!(aFlags & nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
this.onDocumentChange();
PopupNotifications.locationChange(this.activeBrowser);
}
]]>
</body>
</method>
<method name="removeNotifications">
<parameter name="aNotifications"/>
<body>
<![CDATA[
aNotifications.forEach(function(value) {
var notification = PopupNotifications.getNotification(value);
if (notification)
PopupNotifications.remove(notification);
});
]]>
</body>
</method>
<method name="lwthemeInstallRequest">
<parameter name="aHost"/>
<parameter name="aCallback"/>
<body>
<![CDATA[
var message = this._stringBundle.formatStringFromName("lwthemeInstallRequest.message", [aHost], 1);
var action = {
label: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton.accesskey"),
callback: aCallback
};
PopupNotifications.show(this.activeBrowser,
"lwtheme-install-request", message,
"addons-notification-icon", action);
]]>
</body>
</method>
<method name="lwthemeInstallNotification">
<parameter name="aCallback"/>
<body>
<![CDATA[
var message = this._stringBundle.GetStringFromName("lwthemeInstallNotification.message");
var mainAction = {
label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton.accesskey"),
callback: aCallback
};
var secondaryActions = [{
label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton.accesskey"),
callback: function() {
window.toEM("addons://list/theme");
}
}];
var options = {
timeout: Date.now() + 20000 // 20 seconds
};
PopupNotifications.show(this.activeBrowser,
"lwtheme-install-notification", message,
"addons-notification-icon", mainAction,
secondaryActions, options);
]]>
</body>
</method>
<method name="lwthemeNeedsRestart">
<parameter name="aNewThemeName"/>
<body>
<![CDATA[
var message = this._stringBundle.formatStringFromName("lwthemeNeedsRestart.message", [aNewThemeName], 1);
var action = {
label: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton"),
accessKey: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton.accesskey"),
callback: function() {
BrowserUtils.restartApplication();
}
};
var options = {
timeout: Date.now() + 20000 // 20 seconds
};
PopupNotifications.show(this.activeBrowser,
"lwtheme-install-notification", message,
"addons-notification-icon", action, null,
options);
]]>
</body>
</method>
<method name="promptIndexedDB">
<parameter name="aRequestor"/>
<parameter name="aWindow"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
var host = aWindow.document.documentURIObject.asciiHost;
var property = this.usePrivateBrowsing ? "offlineApps.private" :
"offlineApps." + aTopic;
var message = this._stringBundle.formatStringFromName(property,
[host, aData], 2);
var observer = aRequestor.getInterface(Ci.nsIObserver);
var mainAction = {
label: this._stringBundle.GetStringFromName("offlineApps.always"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.always.accesskey"),
callback: function allowIndexedDB() {
clearTimeout(notification.timeout);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.ALLOW_ACTION);
}
};
var secondaryActions = [{
label: this._stringBundle.GetStringFromName("offlineApps.never"),
accessKey: this._stringBundle.GetStringFromName("offlineApps.never.accesskey"),
callback: function denyIndexedDB() {
clearTimeout(notification.timeout);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.DENY_ACTION);
}
}];
function notificationTimedOut() {
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.UNKNOWN_ACTION);
notification.remove();
}
var options = {
eventCallback: function(state) {
if (notification) {
// Always clear the timeout up front. If the doorhanger was
// temporarily dismissed, we'll set a new 30 second timeout
// to automatically cancel the request. If the doorhanger
// gets redisplayed we don't want it to time out unless it
// gets dismissed again. And if the doorhanger gets removed
// then we aren't interested in it any more.
clearTimeout(timeout);
if (state == "dismissed")
timeout = setTimeout(notificationTimedOut, 30000);
}
}
};
var notification =
PopupNotifications.show(this.activeBrowser,
"indexedDB-" + aTopic + "-prompt",
message, "indexedDB-notification-icon",
this.usePrivateBrowsing ? null : mainAction,
secondaryActions, options);
var timeout = setTimeout(notificationTimedOut, 300000); // 5 minutes
]]>
</body>
</method>
<method name="cancelIndexedDB">
<parameter name="aRequestor"/>
<parameter name="aWindow"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
var popupNotification = PopupNotifications.getNotification("indexedDB-" + aTopic + "-prompt", this.activeBrowser);
if (popupNotification)
popupNotification.remove(); // eventCallback clears the timeout
var observer = aRequestor.getInterface(Ci.nsIObserver);
observer.observe(null, "indexedDB-" + aTopic + "-response",
Ci.nsIPermissionManager.UNKNOWN_ACTION);
]]>
</body>
</method>
<method name="addonInstallBlocked">
<parameter name="installInfo"/>
<body>
<![CDATA[
var host;
try {
// this fails with nsSimpleURIs like data: URIs
host = installInfo.originatingURI.host;
} catch (ex) {
host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
}
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var messageString = this._stringBundle.formatStringFromName("xpinstallPromptWarning",
[brandShortName, host], 2);
var action = {
label: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton"),
accessKey: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton.accesskey"),
callback: function allowInstall() {
installInfo.install();
return false;
}
};
// Make notifications persist a minimum of 30 seconds
var options = {
timeout: Date.now() + 30000
};
PopupNotifications.show(this.activeBrowser,
"addon-install-blocked", messageString,
"addons-notification-icon", action,
null, options);
]]>
</body>
</method>
<method name="addonInstallCancelled">
<parameter name="installInfo"/>
<body>
<![CDATA[
var tmp = {};
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
var messageString = this._stringBundle.GetStringFromName("addonDownloadCancelled");
messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
var action = {
label: this._stringBundle.GetStringFromName("addonDownloadRestartButton"),
accessKey: this._stringBundle.GetStringFromName("addonDownloadRestartButton.accesskey"),
popup: null,
callback: function() {
var weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]
.getService(Ci.amIWebInstallListener);
if (weblistener.onWebInstallRequested(installInfo.browser, installInfo.originatingURI,
[aInstall], 1)) {
aInstall.install();
}
}
};
PopupNotifications.show(this.activeBrowser,
"addon-install-cancelled", messageString,
"addons-notification-icon", action);
installInfo.installs.every(function(aInstall) {
aInstall.cancel();
});
]]>
</body>
</method>
<method name="addonInstallComplete">
<parameter name="installInfo"/>
<body>
<![CDATA[
var tmp = {};
ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
var messageString;
var mainAction = null;
var secondaryActions = null;
if ("toEM" in window) {
mainAction = {
label: this._stringBundle.GetStringFromName("addonInstallManageButton"),
accessKey: this._stringBundle.GetStringFromName("addonInstallManageButton.accesskey"),
callback: function() {
window.toEM("addons://list/extension");
}
};
}
if (installInfo.installs.some(install =>
install.addon.pendingOperations &
tmp.AddonManager.PENDING_INSTALL)) {
messageString = this._stringBundle.GetStringFromName("addonsInstalledNeedsRestart");
if (mainAction)
secondaryActions = [mainAction];
mainAction = {
label: this._stringBundle.GetStringFromName("addonInstallRestartButton"),
accessKey: this._stringBundle.GetStringFromName("addonInstallRestartButton.accesskey"),
callback: function () {
BrowserUtils.restartApplication();
}
};
} else {
messageString = this._stringBundle.GetStringFromName("addonsInstalled");
}
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
messageString = tmp.PluralForm.get(installInfo.installs.length, messageString)
.replace("#1", installInfo.installs[0].name)
.replace("#2", installInfo.installs.length)
.replace("#3", brandShortName);
// Make notifications persist a minimum of 30 seconds
var options = {
timeout: Date.now() + 30000
};
PopupNotifications.show(this.activeBrowser,
"addon-install-complete", messageString,
"addons-notification-icon", mainAction,
secondaryActions, options);
]]>
</body>
</method>
<method name="addonInstallDisabled">
<parameter name="installInfo"/>
<body>
<![CDATA[
var messageString;
var action = null;
if (Services.prefs.prefIsLocked("xpinstall.enabled"))
messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessageLocked");
else {
messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessage");
action = {
label: this._stringBundle.GetStringFromName("xpinstallDisabledButton"),
accessKey: this._stringBundle.GetStringFromName("xpinstallDisabledButton.accesskey"),
callback: function editPrefs() {
Services.prefs.setBoolPref("xpinstall.enabled", true);
}
};
}
// Make notifications persist a minimum of 30 seconds
var options = {
timeout: Date.now() + 30000
};
PopupNotifications.show(this.activeBrowser,
"addon-install-disabled", messageString,
"addons-notification-icon", action,
null, options);
]]>
</body>
</method>
<method name="addonInstallFailed">
<parameter name="installInfo"/>
<body>
<![CDATA[
var host;
try {
// this fails with nsSimpleURIs like data: URIs
host = installInfo.originatingURI.host;
} catch (ex) {
host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
}
var error = "addonErrorIncompatible";
var name = installInfo.installs[0].name;
installInfo.installs.some(function(install) {
if (install.error) {
name = install.name;
error = "addonError" + install.error;
return true;
}
if (install.addon.blocklistState ==
Ci.nsIBlocklistService.STATE_BLOCKED) {
name = install.name;
error = "addonErrorBlocklisted";
}
return false;
});
var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
var version = Services.appinfo.version;
var messageString = this._stringBundle.GetStringFromName(error)
.replace("#1", name)
.replace("#2", host)
.replace("#3", brandShortName)
.replace("#4", version);
// Make notifications persist a minimum of 30 seconds
var options = {
timeout: Date.now() + 30000
};
PopupNotifications.show(this.activeBrowser,
"addon-install-failed", messageString,
"addons-notification-icon", null,
null, options);
]]>
</body>
</method>
<method name="addonInstallStarted">
<parameter name="installInfo"/>
<body>
<![CDATA[
var tmp = {};
ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
if (installInfo.installs.every(function(aInstall) {
return aInstall.state == tmp.AddonManager.STATE_DOWNLOADED;
}))
return;
ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
var messageString = this._stringBundle.GetStringFromName("addonDownloading");
messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
var action = {
label: this._stringBundle.GetStringFromName("addonDownloadCancelButton"),
accessKey: this._stringBundle.GetStringFromName("addonDownloadCancelButton.accesskey"),
callback: this.addonInstallCancelled.bind(this, installInfo)
};
var options = {
installInfo: installInfo
};
PopupNotifications.show(this.activeBrowser,
"addon-install-started", messageString,
"addons-notification-icon", action,
null, options);
]]>
</body>
</method>
<constructor>
<![CDATA[
]]>
</constructor>
</implementation>
</binding>
<binding id="addon-progress-notification"
<content>
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
<xul:hbox anonid="details" align="center" flex="1"
oncommand="this.parentNode.parentNode._doButtonCommand(event);">
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
<xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
<xul:progressmeter mode="undetermined" xbl:inherits="mode,value=progress"/>
<xul:label flex="1" xbl:inherits="value=status"/>
<children/>
</xul:hbox>
<xul:toolbarbutton ondblclick="event.stopPropagation();"
class="messageCloseButton tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close();"/>
</xul:hbox>
</content>
<implementation>
<destructor>
<![CDATA[
this.installInfo.installs.forEach(function(aInstall) {
aInstall.removeListener(this);
}, this);
]]>
</destructor>
<method name="updateProgress">
<body>
<![CDATA[
var count = 0;
var progress = 0;
var max = 0;
var tmp = {};
ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
this.installInfo.installs.forEach(function(aInstall) {
if (aInstall.maxProgress < 0)
max = -1;
else if (max >= 0)
max += aInstall.maxProgress;
progress += aInstall.progress;
if (aInstall.state < tmp.AddonManager.STATE_DOWNLOADED)
count++;
});
if (max < 0)
this.setAttribute("mode", "undetermined");
else {
this.setAttribute("mode", "determined");
this.setAttribute("progress", progress * 100 / max);
}
var now = Date.now();
if (!this.startTime) {
this.startTime = now;
this.lastUpdate = now - 750;
this.lastSeconds = null;
}
if (progress == max || now - this.lastUpdate >= 750) {
this.lastUpdate = now;
var elapsed = (now - this.startTime) / 1000;
var rate = elapsed && progress / elapsed;
ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", tmp);
var status;
[status, this.lastSeconds] = tmp.DownloadUtils.getDownloadStatus(progress, max, rate, this.lastSeconds);
this.setAttribute("status", status);
}
if (!count)
this.close();
]]>
</body>
</method>
<method name="onDownloadProgress">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
<method name="onDownloadFailed">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
<method name="onDownloadCancelled">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
<method name="onDownloadEnded">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
</implementation>
</binding>
<binding id="sidebar-notification"
<content>
<xul:vbox class="notification-inner outset" flex="1" xbl:inherits="type">
<xul:hbox align="center">
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
<xul:arrowscrollbox orient="horizontal" flex="1" pack="end"
oncommand="document.getBindingParent(this)._doButtonCommand(event);">
<children/>
</xul:arrowscrollbox>
<xul:toolbarbutton ondblclick="event.stopPropagation();"
class="messageCloseButton tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close();"/>
</xul:hbox>
<xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
</xul:vbox>
</content>
</binding>
<binding id="sidebar-addon-progress-notification"
<content>
<xul:vbox class="notification-inner outset" flex="1" xbl:inherits="type">
<xul:hbox align="center">
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
<xul:arrowscrollbox orient="horizontal" flex="1" pack="end"
oncommand="document.getBindingParent(this)._doButtonCommand(event);">
<children/>
</xul:arrowscrollbox>
<xul:toolbarbutton ondblclick="event.stopPropagation();"
class="messageCloseButton tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close();"/>
</xul:hbox>
<xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
<xul:progressmeter mode="undetermined" xbl:inherits="mode,value=progress"/>
<xul:label xbl:inherits="value=status"/>
</xul:vbox>
</content>
</binding>
<binding id="addon-progress-popup-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
<content align="start">
<xul:image class="popup-notification-icon" xbl:inherits="popupid"/>
<xul:vbox flex="1">
<xul:description class="popup-notification-description addon-progress-description"
xbl:inherits="xbl:text=label"/>
<xul:hbox class="popup-notification-button-container" align="center">
<xul:progressmeter mode="undetermined"
xbl:inherits="mode,value=progress"/>
<xul:spacer flex="1"/>
<xul:button anonid="button"
class="popup-notification-menubutton"
type="menu-button"
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
<xul:menupopup anonid="menupopup"
xbl:inherits="oncommand=menucommand">
<children/>
<xul:menuitem class="menuitem-iconic popup-notification-closeitem"
label="&closeNotificationItem.label;"
xbl:inherits="oncommand=closeitemcommand"/>
</xul:menupopup>
</xul:button>
</xul:hbox>
<xul:label xbl:inherits="xbl:text=status"/>
</xul:vbox>
<xul:vbox pack="start">
<xul:toolbarbutton anonid="closebutton"
class="messageCloseButton close-icon popup-notification-closebutton tabbable"
xbl:inherits="oncommand=closebuttoncommand"
tooltiptext="&closeNotification.tooltip;"/>
</xul:vbox>
</content>
<implementation>
<constructor>
<![CDATA[
this.installInfo = this.notification.options.installInfo;
this.installInfo.installs.forEach(function(aInstall) {
aInstall.addListener(this);
}, this);
]]>
</constructor>
<destructor>
<![CDATA[
this.installInfo.installs.forEach(function(aInstall) {
aInstall.removeListener(this);
}, this);
]]>
</destructor>
<method name="updateProgress">
<body>
<![CDATA[
var count = 0;
var progress = 0;
var max = 0;
var tmp = {};
ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
this.installInfo.installs.forEach(function(aInstall) {
if (aInstall.maxProgress < 0)
max = -1;
else if (max >= 0)
max += aInstall.maxProgress;
progress += aInstall.progress;
if (aInstall.state < tmp.AddonManager.STATE_DOWNLOADED)
count++;
});
if (max < 0)
this.setAttribute("mode", "undetermined");
else {
this.setAttribute("mode", "determined");
this.setAttribute("progress", progress * 100 / max);
}
var now = Date.now();
if (!this.startTime) {
this.startTime = now;
this.lastUpdate = now - 750;
this.lastSeconds = null;
}
if (progress == max || now - this.lastUpdate >= 750) {
this.lastUpdate = now;
var elapsed = (now - this.startTime) / 1000;
var rate = elapsed && progress / elapsed;
ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", tmp);
var status;
[status, this.lastSeconds] = tmp.DownloadUtils.getDownloadStatus(progress, max, rate, this.lastSeconds);
this.setAttribute("status", status);
}
if (!count)
PopupNotifications.remove(this.notification);
]]>
</body>
</method>
<method name="onDownloadProgress">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
<method name="onDownloadFailed">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
<method name="onDownloadCancelled">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
<method name="onDownloadEnded">
<body>
<![CDATA[
this.updateProgress();
]]>
</body>
</method>
</implementation>
</binding>
<binding id="center-item">
<content>
<xul:vbox flex="1" class="center-item-box"
xbl:inherits="warn,showseparator,padbottom">
<xul:hbox align="center">
<xul:image class="center-item-icon"
xbl:inherits="src=itemicon"/>
<xul:description class="center-item-label"
xbl:inherits="xbl:text=itemtext"/>
<xul:spacer flex="1"/>
<xul:button class="popup-notification-menubutton center-item-button"
oncommand="document.getBindingParent(this).runCallback();"
xbl:inherits="label=buttonlabel"/>
</xul:hbox>
<xul:hbox align="center" class="center-item-warning">
<xul:image class="center-item-warning-icon"/>
<xul:label class="center-item-warning-description" xbl:inherits="xbl:text=warningText"/>
<xul:label xbl:inherits="href=updateLink" value="&checkForUpdates;" class="text-link"/>
</xul:hbox>
</xul:vbox>
</content>
<resources>
</resources>
<implementation>
<field name="action"></field>
<method name="runCallback">
<body><![CDATA[
let action = this.action;
action.callback();
let cas = action.popupnotification.notification.options.centerActions;
cas.splice(cas.indexOf(action), 1);
PopupNotifications._dismiss();
]]></body>
</method>
</implementation>
</binding>
</bindings>