michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Progress heartbeat timer duration (ms) michael@0: const kHeartbeatDuration = 1000; michael@0: // Start and end progress screen css margins as percentages michael@0: const kProgressMarginStart = 30; michael@0: const kProgressMarginEnd = 70; michael@0: michael@0: const WebProgress = { michael@0: get _identityBox() { return document.getElementById("identity-box"); }, michael@0: michael@0: init: function init() { michael@0: messageManager.addMessageListener("Content:StateChange", this); michael@0: messageManager.addMessageListener("Content:LocationChange", this); michael@0: messageManager.addMessageListener("Content:SecurityChange", this); michael@0: michael@0: Elements.progress.addEventListener("transitionend", this, true); michael@0: Elements.tabList.addEventListener("TabSelect", this, true); michael@0: michael@0: let urlBar = document.getElementById("urlbar-edit"); michael@0: urlBar.addEventListener("input", this, false); michael@0: michael@0: return this; michael@0: }, michael@0: michael@0: receiveMessage: function receiveMessage(aMessage) { michael@0: let json = aMessage.json; michael@0: let tab = Browser.getTabForBrowser(aMessage.target); michael@0: michael@0: switch (aMessage.name) { michael@0: case "Content:StateChange": { michael@0: if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) { michael@0: if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START) michael@0: this._windowStart(json, tab); michael@0: else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP) michael@0: this._windowStop(json, tab); michael@0: } michael@0: michael@0: if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) { michael@0: if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START) michael@0: this._networkStart(json, tab); michael@0: else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP) michael@0: this._networkStop(json, tab); michael@0: } michael@0: michael@0: this._progressStep(tab); michael@0: break; michael@0: } michael@0: michael@0: case "Content:LocationChange": { michael@0: this._locationChange(json, tab); michael@0: this._progressStep(tab); michael@0: break; michael@0: } michael@0: michael@0: case "Content:SecurityChange": { michael@0: this._securityChange(json, tab); michael@0: this._progressStep(tab); michael@0: break; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: handleEvent: function handleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case "transitionend": michael@0: this._progressTransEnd(aEvent); michael@0: break; michael@0: case "TabSelect": michael@0: this._onTabSelect(aEvent); michael@0: break; michael@0: case "input": michael@0: this._onUrlBarInput(aEvent); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: _securityChange: function _securityChange(aJson, aTab) { michael@0: let state = aJson.state; michael@0: let nsIWebProgressListener = Ci.nsIWebProgressListener; michael@0: michael@0: if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) { michael@0: aTab._identityState = "verifiedIdentity"; michael@0: } else if (state & nsIWebProgressListener.STATE_IS_SECURE) { michael@0: aTab._identityState = "verifiedDomain"; michael@0: } else { michael@0: aTab._identityState = ""; michael@0: } michael@0: michael@0: if (aTab == Browser.selectedTab) { michael@0: this._identityBox.className = aTab._identityState; michael@0: } michael@0: }, michael@0: michael@0: _locationChange: function _locationChange(aJson, aTab) { michael@0: let spec = aJson.location; michael@0: let location = spec.split("#")[0]; // Ignore fragment identifier changes. michael@0: michael@0: if (aTab == Browser.selectedTab) { michael@0: BrowserUI.updateURI(); michael@0: BrowserUI.update(); michael@0: BrowserUI.updateStartURIAttributes(aJson.location); michael@0: } michael@0: michael@0: let locationHasChanged = (location != aTab.browser.lastLocation); michael@0: if (locationHasChanged) { michael@0: Browser.getNotificationBox(aTab.browser).removeTransientNotifications(); michael@0: aTab.browser.lastLocation = location; michael@0: aTab.browser.userTypedValue = ""; michael@0: aTab.browser.appIcon = { href: null, size:-1 }; michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (CrashReporter.enabled) michael@0: CrashReporter.annotateCrashReport("URL", spec); michael@0: #endif michael@0: } michael@0: michael@0: let event = document.createEvent("UIEvents"); michael@0: event.initUIEvent("URLChanged", true, false, window, locationHasChanged); michael@0: aTab.browser.dispatchEvent(event); michael@0: }, michael@0: michael@0: _networkStart: function _networkStart(aJson, aTab) { michael@0: aTab.startLoading(); michael@0: michael@0: if (aTab == Browser.selectedTab) { michael@0: // NO_STARTUI_VISIBILITY since the current uri for the tab has not michael@0: // been updated yet. If we're coming off of the start page, this michael@0: // would briefly show StartUI until _locationChange is called. michael@0: BrowserUI.update(BrowserUI.NO_STARTUI_VISIBILITY); michael@0: } michael@0: }, michael@0: michael@0: _networkStop: function _networkStop(aJson, aTab) { michael@0: aTab.endLoading(); michael@0: michael@0: if (aTab == Browser.selectedTab) { michael@0: BrowserUI.update(); michael@0: } michael@0: }, michael@0: michael@0: _windowStart: function _windowStart(aJson, aTab) { michael@0: this._progressStart(aJson, aTab); michael@0: }, michael@0: michael@0: _windowStop: function _windowStop(aJson, aTab) { michael@0: this._progressStop(aJson, aTab); michael@0: }, michael@0: michael@0: _progressStart: function _progressStart(aJson, aTab) { michael@0: // We will get multiple calls from _windowStart, so michael@0: // only process once. michael@0: if (aTab._progressActive) michael@0: return; michael@0: michael@0: aTab._progressActive = true; michael@0: michael@0: // 'Whoosh' in michael@0: aTab._progressCount = kProgressMarginStart; michael@0: this._showProgressBar(aTab); michael@0: }, michael@0: michael@0: _showProgressBar: function (aTab) { michael@0: // display the track michael@0: if (aTab == Browser.selectedTab) { michael@0: Elements.progressContainer.removeAttribute("collapsed"); michael@0: Elements.progress.style.width = aTab._progressCount + "%"; michael@0: Elements.progress.removeAttribute("fade"); michael@0: } michael@0: michael@0: // Create a pulse timer to keep things moving even if we don't michael@0: // collect any state changes. michael@0: setTimeout(function() { michael@0: WebProgress._progressStepTimer(aTab); michael@0: }, kHeartbeatDuration, this); michael@0: }, michael@0: michael@0: _stepProgressCount: function _stepProgressCount(aTab) { michael@0: // Step toward the end margin in smaller slices as we get closer michael@0: let left = kProgressMarginEnd - aTab._progressCount; michael@0: let step = left * .05; michael@0: aTab._progressCount += Math.ceil(step); michael@0: michael@0: // Don't go past the 'whoosh out' margin. michael@0: if (aTab._progressCount > kProgressMarginEnd) { michael@0: aTab._progressCount = kProgressMarginEnd; michael@0: } michael@0: }, michael@0: michael@0: _progressStep: function _progressStep(aTab) { michael@0: if (!aTab._progressActive) michael@0: return; michael@0: this._stepProgressCount(aTab); michael@0: if (aTab == Browser.selectedTab) { michael@0: Elements.progress.style.width = aTab._progressCount + "%"; michael@0: } michael@0: }, michael@0: michael@0: _progressStepTimer: function _progressStepTimer(aTab) { michael@0: if (!aTab._progressActive) michael@0: return; michael@0: this._progressStep(aTab); michael@0: michael@0: setTimeout(function() { michael@0: WebProgress._progressStepTimer(aTab); michael@0: }, kHeartbeatDuration, this); michael@0: }, michael@0: michael@0: _progressStop: function _progressStop(aJson, aTab) { michael@0: aTab._progressActive = false; michael@0: // 'Whoosh out' and fade michael@0: if (aTab == Browser.selectedTab) { michael@0: Elements.progress.style.width = "100%"; michael@0: Elements.progress.setAttribute("fade", true); michael@0: } michael@0: }, michael@0: michael@0: _progressTransEnd: function _progressTransEnd(aEvent) { michael@0: if (!Elements.progress.hasAttribute("fade")) michael@0: return; michael@0: // Close out fade finished, reset michael@0: if (aEvent.propertyName == "opacity") { michael@0: Elements.progress.style.width = "0px"; michael@0: Elements.progressContainer.setAttribute("collapsed", true); michael@0: } michael@0: }, michael@0: michael@0: _onTabSelect: function(aEvent) { michael@0: let tab = Browser.getTabFromChrome(aEvent.originalTarget); michael@0: this._identityBox.className = tab._identityState || ""; michael@0: if (tab._progressActive) { michael@0: this._showProgressBar(tab); michael@0: } else { michael@0: Elements.progress.setAttribute("fade", true); michael@0: Elements.progressContainer.setAttribute("collapsed", true); michael@0: } michael@0: }, michael@0: michael@0: _onUrlBarInput: function(aEvent) { michael@0: Browser.selectedTab._identityState = this._identityBox.className = ""; michael@0: }, michael@0: };