summaryrefslogtreecommitdiffstats
path: root/comm/suite/browser/nsBrowserStatusHandler.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/browser/nsBrowserStatusHandler.js')
-rw-r--r--comm/suite/browser/nsBrowserStatusHandler.js473
1 files changed, 473 insertions, 0 deletions
diff --git a/comm/suite/browser/nsBrowserStatusHandler.js b/comm/suite/browser/nsBrowserStatusHandler.js
new file mode 100644
index 0000000000..b4699f8f9c
--- /dev/null
+++ b/comm/suite/browser/nsBrowserStatusHandler.js
@@ -0,0 +1,473 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+function nsBrowserStatusHandler()
+{
+ this.init();
+}
+
+nsBrowserStatusHandler.prototype =
+{
+ // Stored Status, Link and Loading values
+ status : "",
+ defaultStatus : "",
+ jsStatus : "",
+ jsDefaultStatus : "",
+ overLink : "",
+ feeds : [],
+
+ QueryInterface : function(aIID)
+ {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsIXULBrowserWindow) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ init : function()
+ {
+ this.urlBar = document.getElementById("urlbar");
+ this.throbberElement = document.getElementById("navigator-throbber");
+ this.statusMeter = document.getElementById("statusbar-icon");
+ this.statusPanel = document.getElementById("statusbar-progresspanel");
+ this.stopButton = document.getElementById("stop-button");
+ this.stopMenu = document.getElementById("menuitem-stop");
+ this.stopContext = document.getElementById("context-stop");
+ this.statusTextField = document.getElementById("statusbar-display");
+ this.isImage = document.getElementById("isImage");
+ this.securityButton = document.getElementById("security-button");
+ this.evButton = document.getElementById("ev-button");
+ this.feedsMenu = document.getElementById("feedsMenu");
+ this.feedsButton = document.getElementById("feedsButton");
+
+ // Initialize the security button's state and tooltip text
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ this.onSecurityChange(null, null, nsIWebProgressListener.STATE_IS_INSECURE);
+ },
+
+ destroy : function()
+ {
+ // XXXjag to avoid leaks :-/, see bug 60729
+ this.urlBar = null;
+ this.throbberElement = null;
+ this.statusMeter = null;
+ this.statusPanel = null;
+ this.stopButton = null;
+ this.stopMenu = null;
+ this.stopContext = null;
+ this.statusTextField = null;
+ this.isImage = null;
+ this.securityButton = null;
+ this.evButton = null;
+ this.feedsButton = null;
+ this.feedsMenu = null;
+ },
+
+ // nsIXULBrowserWindow
+ setJSStatus : function(status)
+ {
+ this.jsStatus = status;
+ this.updateStatusField();
+ },
+
+ // nsIXULBrowserWindow
+ setJSDefaultStatus : function(status)
+ {
+ this.jsDefaultStatus = status;
+ this.updateStatusField();
+ },
+
+ setDefaultStatus : function(status)
+ {
+ this.defaultStatus = status;
+ this.updateStatusField();
+ },
+
+ // nsIXULBrowserWindow
+ setOverLink : function(link, context)
+ {
+ this.overLink = link;
+ // clear out 'Done' (or other message) on first hover
+ if (this.defaultStatus)
+ this.defaultStatus = "";
+ this.updateStatusField();
+ if (link)
+ this.statusTextField.setAttribute('crop', 'center');
+ else
+ this.statusTextField.setAttribute('crop', 'end');
+ },
+
+ // nsIXULBrowserWindow
+ // Called before links are navigated to to allow us to retarget them if needed.
+ onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
+ return originalTarget;
+ },
+
+ updateStatusField : function()
+ {
+ var text = this.overLink || this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
+
+ // check the current value so we don't trigger an attribute change
+ // and cause needless (slow!) UI updates
+ if (this.statusTextField.label != text)
+ this.statusTextField.label = text;
+ },
+
+/**
+ * Returns true if |aMimeType| is text-based, false otherwise.
+ *
+ * @param aMimeType
+ * The MIME type to check.
+ *
+ * If adding types to this function, please also check the similar
+ * function in mozilla/toolkit/content/widgets/findbar.xml.
+ */
+ mimeTypeIsTextBased : function(contentType)
+ {
+ return /^text\/|\+xml$/.test(contentType) ||
+ contentType == "application/x-javascript" ||
+ contentType == "application/javascript" ||
+ contentType == "application/xml" ||
+ contentType == "mozilla.application/cached-xul";
+ },
+
+ populateFeeds : function(popup)
+ {
+ // First clear out any old items
+ while (popup.hasChildNodes())
+ popup.lastChild.remove();
+
+ for (var i = 0; i < this.feeds.length; i++) {
+ var link = this.feeds[i];
+ var menuitem = document.createElement("menuitem");
+ menuitem.className = "menuitem-iconic bookmark-item";
+ menuitem.statusText = link.href;
+ menuitem.setAttribute("label", link.title || link.href);
+ popup.appendChild(menuitem);
+ }
+ },
+
+ onFeedAvailable : function(aLink)
+ {
+ this.feeds.push(aLink);
+ this.feedsMenu.removeAttribute("disabled");
+ this.feedsButton.hidden = false;
+ },
+
+ onLinkIconAvailable : function(aHref)
+ {
+ if (aHref && gProxyFavIcon &&
+ Services.prefs.getBoolPref("browser.chrome.site_icons")) {
+ var browser = getBrowser();
+ if (browser.userTypedValue === null)
+ gProxyFavIcon.setAttribute("src", aHref);
+ }
+ },
+
+ onProgressChange : function (aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress)
+ {
+ if (aMaxTotalProgress > 0) {
+ // This is highly optimized. Don't touch this code unless
+ // you are intimately familiar with the cost of setting
+ // attrs on XUL elements. -- hyatt
+ var percentage = (aCurTotalProgress * 100) / aMaxTotalProgress;
+ this.statusMeter.value = percentage;
+ }
+ },
+
+ onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
+ {
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ const nsIChannel = Ci.nsIChannel;
+ var ctype;
+ if (aStateFlags & nsIWebProgressListener.STATE_START) {
+ // This (thanks to the filter) is a network start or the first
+ // stray request (the first request outside of the document load),
+ // initialize the throbber and his friends.
+
+ // Call start document load listeners (only if this is a network load)
+ if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
+ aRequest && aWebProgress.isTopLevel)
+ this.startDocumentLoad(aRequest);
+
+ if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
+ // Show the progress meter
+ this.statusPanel.collapsed = false;
+ // Turn the throbber on.
+ this.throbberElement.setAttribute("busy", "true");
+ }
+
+ // XXX: These need to be based on window activity...
+ this.stopButton.disabled = false;
+ this.stopMenu.removeAttribute('disabled');
+ this.stopContext.removeAttribute('disabled');
+ }
+ else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
+ if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+ if (aRequest) {
+ if (aWebProgress.isTopLevel)
+ this.endDocumentLoad(aRequest, aStatus);
+ }
+ }
+
+ // This (thanks to the filter) is a network stop or the last
+ // request stop outside of loading the document, stop throbbers
+ // and progress bars and such
+ if (aRequest) {
+ var msg = "";
+ // Get the channel if the request is a channel
+ if (aRequest instanceof nsIChannel) {
+ var location = aRequest.URI.spec;
+ if (location != "about:blank") {
+ switch (aStatus) {
+ case Cr.NS_BINDING_ABORTED:
+ msg = gNavigatorBundle.getString("nv_stopped");
+ break;
+ case Cr.NS_ERROR_NET_TIMEOUT:
+ msg = gNavigatorBundle.getString("nv_timeout");
+ break;
+ }
+ }
+ }
+ // If msg is false then we did not have an error (channel may have
+ // been null, in the case of a stray image load).
+ if (!msg) {
+ msg = gNavigatorBundle.getString("nv_done");
+ }
+ this.status = "";
+ this.setDefaultStatus(msg);
+
+ // Disable menu entries for images, enable otherwise
+ if (content.document && this.mimeTypeIsTextBased(content.document.contentType))
+ this.isImage.removeAttribute('disabled');
+ else
+ this.isImage.setAttribute('disabled', 'true');
+ }
+
+ // Turn the progress meter and throbber off.
+ this.statusPanel.collapsed = true;
+ this.statusMeter.value = 0; // be sure to clear the progress bar
+ this.throbberElement.removeAttribute("busy");
+
+ // XXX: These need to be based on window activity...
+ // XXXjag: <command id="cmd_stop"/> ?
+ this.stopButton.disabled = true;
+ this.stopMenu.setAttribute('disabled', 'true');
+ this.stopContext.setAttribute('disabled', 'true');
+
+ ZoomListeners.onLocationChange(getBrowser().currentURI);
+ }
+ },
+
+ onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
+ {
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ if (gContextMenu) {
+ // Optimise for the common case
+ if (aWebProgress.isTopLevel)
+ document.getElementById("contentAreaContextMenu").hidePopup();
+ else {
+ for (var contextWindow = gContextMenu.target.ownerDocument.defaultView;
+ contextWindow != contextWindow.parent;
+ contextWindow = contextWindow.parent) {
+ if (contextWindow == aWebProgress.DOMWindow) {
+ document.getElementById("contentAreaContextMenu").hidePopup();
+ break;
+ }
+ }
+ }
+ }
+
+ if (document.tooltipNode) {
+ // Optimise for the common case
+ if (aWebProgress.isTopLevel) {
+ document.getElementById("aHTMLTooltip").hidePopup();
+ document.tooltipNode = null;
+ } else {
+ for (var tooltipWindow = document.tooltipNode.ownerDocument.defaultView;
+ tooltipWindow != tooltipWindow.parent;
+ tooltipWindow = tooltipWindow.parent) {
+ if (tooltipWindow == aWebProgress.DOMWindow) {
+ document.getElementById("aHTMLTooltip").hidePopup();
+ document.tooltipNode = null;
+ break;
+ }
+ }
+ }
+ }
+
+ // Hide the form invalid popup.
+ if (gFormSubmitObserver.panel) {
+ gFormSubmitObserver.panel.hidePopup();
+ }
+
+ // XXX temporary hack for bug 104532.
+ // Depends heavily on setOverLink implementation
+ if (!aRequest)
+ this.status = this.jsStatus = this.jsDefaultStatus = "";
+
+ this.setOverLink("");
+
+ // Disable menu entries for images, enable otherwise
+ if (content.document && this.mimeTypeIsTextBased(content.document.contentType))
+ this.isImage.removeAttribute('disabled');
+ else
+ this.isImage.setAttribute('disabled', 'true');
+
+ // We should probably not do this if the value has changed since the user
+ // searched
+ // Update urlbar only if a new page was loaded on the primary content area
+ // Do not update urlbar if there was a subframe navigation
+
+ var browser = getBrowser().selectedBrowser;
+ if (aWebProgress.isTopLevel) {
+ var userTypedValue = browser.userTypedValue;
+ if (userTypedValue === null) {
+ URLBarSetURI(aLocation, true);
+ } else {
+ this.urlBar.value = userTypedValue;
+ SetPageProxyState("invalid", null);
+ }
+
+ BookmarkingUI.updateStarState();
+
+ this.feedsMenu.setAttribute("disabled", "true");
+ this.feedsButton.hidden = true;
+ this.feeds = [];
+
+ // When background tab comes into foreground or loading a new page
+ // (aRequest set), might want to update zoom.
+ if (FullZoom.updateBackgroundTabs || aRequest){
+ FullZoom.onLocationChange(getBrowser().currentURI, !aRequest, browser);
+ ZoomListeners.onLocationChange(getBrowser().currentURI);
+ }
+ }
+ UpdateBackForwardButtons();
+
+ UpdateStatusBarPopupIcon();
+
+ BrowserSearch.updateSearchButton();
+ },
+
+ onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
+ {
+ this.status = aMessage;
+ this.updateStatusField();
+ },
+
+ onSecurityChange : function(aWebProgress, aRequest, aState)
+ {
+ const wpl = Ci.nsIWebProgressListener;
+ const wpl_security_bits = wpl.STATE_IS_SECURE |
+ wpl.STATE_IS_BROKEN |
+ wpl.STATE_IS_INSECURE;
+
+ var highlightSecure =
+ Services.prefs.getBoolPref("browser.urlbar.highlight.secure");
+
+ /* aState is defined as a bitmask that may be extended in the future.
+ * We filter out any unknown bits before testing for known values.
+ */
+ switch (aState & wpl_security_bits) {
+ case wpl.STATE_IS_SECURE:
+ const nsISSLStatusProvider = Ci.nsISSLStatusProvider;
+ var cert = getBrowser().securityUI.QueryInterface(nsISSLStatusProvider)
+ .SSLStatus.serverCert;
+ var issuerName = cert.issuerOrganization ||
+ cert.issuerCommonName || cert.issuerName;
+ this.securityButton.setAttribute("tooltiptext",
+ gNavigatorBundle.getFormattedString("securityButtonTooltipSecure",
+ [issuerName]));
+ this.securityButton.setAttribute("level", "high");
+ if (highlightSecure)
+ this.urlBar.setAttribute("level", "high");
+ else
+ this.urlBar.removeAttribute("level");
+ break;
+ case wpl.STATE_IS_BROKEN:
+ this.securityButton.setAttribute("tooltiptext",
+ gNavigatorBundle.getString("securityButtonTooltipMixedContent"));
+ this.securityButton.setAttribute("level", "broken");
+ if (highlightSecure)
+ this.urlBar.setAttribute("level", "broken");
+ else
+ this.urlBar.removeAttribute("level");
+ break;
+ case wpl.STATE_IS_INSECURE:
+ default:
+ this.securityButton.setAttribute("tooltiptext",
+ gNavigatorBundle.getString("securityButtonTooltipInsecure"));
+ this.securityButton.removeAttribute("level");
+ this.urlBar.removeAttribute("level");
+ break;
+ }
+
+ if (aState & wpl.STATE_IDENTITY_EV_TOPLEVEL) {
+ var organization =
+ getBrowser().securityUI
+ .QueryInterface(Ci.nsISSLStatusProvider)
+ .SSLStatus
+ .QueryInterface(Ci.nsISSLStatus)
+ .serverCert.organization;
+ this.securityButton.setAttribute("label", organization);
+ this.evButton.setAttribute("tooltiptext", organization);
+ this.evButton.hidden = false;
+ } else {
+ this.securityButton.removeAttribute("label");
+ this.evButton.hidden = true;
+ }
+ },
+
+ startDocumentLoad : function(aRequest)
+ {
+ var uri = aRequest.QueryInterface(Ci.nsIChannel).originalURI;
+
+ // clear out search-engine data
+ getBrowser().selectedBrowser.engines = null;
+
+ // Set the URI now if it isn't already set, so that the user can tell which
+ // site is loading. Only do this if user requested the load via chrome UI,
+ // to minimise spoofing risk.
+ if (!content.opener &&
+ !gURLBar.value &&
+ getWebNavigation().currentURI.spec == "about:blank")
+ URLBarSetURI(uri);
+
+ try {
+ Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
+ } catch (e) {
+ }
+ },
+
+ endDocumentLoad : function(aRequest, aStatus)
+ {
+ const nsIChannel = Ci.nsIChannel;
+ var urlStr = aRequest.QueryInterface(nsIChannel).originalURI.spec;
+
+ if (Components.isSuccessCode(aStatus))
+ dump("Document "+urlStr+" loaded successfully\n"); // per QA request
+ else {
+ // per QA request
+ var e = new Components.Exception("", aStatus);
+ var name = e.name;
+ dump("Error loading URL "+urlStr+" : "+
+ Number(aStatus).toString(16));
+ if (name)
+ dump(" ("+name+")");
+ dump('\n');
+ }
+
+ var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
+ try {
+ Services.obs.notifyObservers(content, notification, urlStr);
+ } catch (e) {
+ }
+ }
+}
+