diff options
Diffstat (limited to 'comm/suite/components/bindings/notification.xml')
-rw-r--r-- | comm/suite/components/bindings/notification.xml | 2423 |
1 files changed, 2423 insertions, 0 deletions
diff --git a/comm/suite/components/bindings/notification.xml b/comm/suite/components/bindings/notification.xml new file mode 100644 index 0000000000..8965df67cb --- /dev/null +++ b/comm/suite/components/bindings/notification.xml @@ -0,0 +1,2423 @@ +<?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" + xmlns="http://www.mozilla.org/xbl" + xmlns:xbl="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <binding id="browser-notificationbox" + extends="chrome://global/content/bindings/notification.xml#notificationbox"> + <implementation implements="nsIObserver, nsIFormSubmitObserver, nsIWebProgressListener, nsIWebProgressListener2, nsIDOMEventListener"> + <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) { + const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + 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) { + var browserURL = "chrome://navigator/content/navigator.xul"; + 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 XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + 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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); + ChromeUtils.import("resource://gre/modules/BrowserUtils.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" + extends="chrome://communicator/content/bindings/notification.xml#browser-notificationbox"> + <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[ + ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm"); + ]]> + </constructor> + </implementation> + </binding> + + <binding id="addon-progress-notification" + extends="chrome://global/content/bindings/notification.xml#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" + extends="chrome://global/content/bindings/notification.xml#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" + extends="chrome://communicator/content/bindings/notification.xml#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> + <stylesheet src="chrome://global/skin/notification.css"/> + </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> |