123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const EXPORTED_SYMBOLS = ["S4EStatusService"];
- const CU = Components.utils;
- CU.import("resource://gre/modules/Services.jsm");
- CU.import("resource://gre/modules/XPCOMUtils.jsm");
- function S4EStatusService(window, service, getters)
- {
- this._window = window;
- this._service = service;
- this._getters = getters;
- this._overLinkService = new S4EOverlinkService(this._window, this._service, this);
- }
- S4EStatusService.prototype =
- {
- _window: null,
- _service: null,
- _getters: null,
- _overLinkService: null,
- _overLink: { val: "", type: "" },
- _network: { val: "", type: "" },
- _networkXHR: { val: "", type: "" },
- _status: { val: "", type: "" },
- _jsStatus: { val: "", type: "" },
- _defaultStatus: { val: "", type: "" },
- _isFullScreen: false,
- _isVideo: false,
- _statusText: { val: "", type: "" },
- _noUpdate: false,
- _statusChromeTimeoutID: 0,
- _statusContentTimeoutID: 0,
- getCompositeStatusText: function()
- {
- return this._statusText.val;
- },
- getStatusText: function()
- {
- return this._status.val;
- },
- setNetworkStatus: function(status, busy)
- {
- if(busy)
- {
- this._network = { val: status, type: "network" };
- this._networkXHR = { val: "", type: "network xhr" };
- }
- else
- {
- this._networkXHR = { val: status, type: "network xhr" };
- }
- this.updateStatusField();
- },
- setStatusText: function(status)
- {
- this._status = { val: status, type: "status chrome" };
- this.updateStatusField();
- },
- setJSStatus: function(status)
- {
- this._jsStatus = { val: status, type: "status content" };
- this.updateStatusField();
- },
- setJSDefaultStatus: function(status)
- {
- // This was removed from Firefox in Bug 862917
- },
- setDefaultStatus: function(status)
- {
- this._defaultStatus = { val: status, type: "status chrome default" };
- this.updateStatusField();
- },
- setOverLink: function(link, aAnchor)
- {
- this._overLinkService.update(link, aAnchor);
- },
- setOverLinkInternal: function(link, aAnchor)
- {
- let status = this._service.status;
- let statusLinkOver = this._service.statusLinkOver;
- if(statusLinkOver)
- {
- link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g, encodeURIComponent);
- if(status == statusLinkOver)
- {
- this._overLink = { val: link, type: "overLink", anchor: aAnchor };
- this.updateStatusField();
- }
- else
- {
- this.setStatusField(statusLinkOver, { val: link, type: "overLink", anchor: aAnchor }, true);
- }
- }
- },
- setNoUpdate: function(nu)
- {
- this._noUpdate = nu;
- },
- buildBinding: function() {
-
- // Object.prototype.watch() shim, based on Eli Grey's polyfill
- // object.watch
- if (!this._window.XULBrowserWindow.watch) {
- Object.defineProperty(this._window.XULBrowserWindow, "watch", {
- enumerable: false,
- configurable: true,
- writable: false,
- value: function (prop, handler) {
- var oldval = this[prop],
- newval = oldval,
- getter = function () {
- return newval;
- },
- setter = function (val) {
- oldval = newval;
- return newval = handler.call(this, prop, oldval, val);
- }
- ;
- try {
- if (delete this[prop]) { // can't watch constants
- Object.defineProperty(this, prop, {
- get: getter,
- set: setter,
- enumerable: true,
- configurable: true
- });
- }
- } catch(e) {
- // This fails fatally on non-configurable props, so just
- // ignore errors if it does.
- }
- }
- });
- }
- // object.unwatch
- if (!this._window.XULBrowserWindow.unwatch) {
- Object.defineProperty(this._window.XULBrowserWindow, "unwatch", {
- enumerable: false,
- configurable: true,
- writable: false,
- value: function (prop) {
- var val = this[prop];
- delete this[prop]; // remove accessors
- this[prop] = val;
- }
- });
- }
-
- let XULBWPropHandler = function(prop, oldval, newval) {
- CU.reportError("Attempt to modify XULBrowserWindow." + prop);
- return oldval;
- };
- ["updateStatusField", "onStatusChange"].forEach(function(prop)
- {
- this._window.XULBrowserWindow.unwatch(prop);
- this._window.XULBrowserWindow[prop] = function() {};
- this._window.XULBrowserWindow.watch(prop, XULBWPropHandler);
- }, this);
- ["getCompositeStatusText", "getStatusText", "setStatusText", "setJSStatus",
- "setJSDefaultStatus", "setDefaultStatus", "setOverLink"].forEach(function(prop)
- {
- this._window.XULBrowserWindow.unwatch(prop);
- this._window.XULBrowserWindow[prop] = this[prop].bind(this);
- this._window.XULBrowserWindow.watch(prop, XULBWPropHandler);
- }, this);
- },
- destroy: function()
- {
- // No need to unbind from the XULBrowserWindow, it's already null at this point
- this.clearTimer("_statusChromeTimeoutID");
- this.clearTimer("_statusContentTimeoutID");
- this._overLinkService.destroy();
- ["_overLink", "_network", "_networkXHR", "_status", "_jsStatus", "_defaultStatus",
- "_statusText", "_window", "_service", "_getters", "_overLinkService"].forEach(function(prop)
- {
- delete this[prop];
- }, this);
- },
- buildTextOrder: function()
- {
- this.__defineGetter__("_textOrder", function()
- {
- let textOrder = ["_overLink"];
- if(this._service.statusNetwork)
- {
- textOrder.push("_network");
- if(this._service.statusNetworkXHR)
- {
- textOrder.push("_networkXHR");
- }
- }
- textOrder.push("_status", "_jsStatus");
- if(this._service.statusDefault)
- {
- textOrder.push("_defaultStatus");
- }
- delete this._textOrder;
- return this._textOrder = textOrder;
- });
- },
- updateStatusField: function(force)
- {
- let text = { val: "", type: "" };
- for(let i = 0; !text.val && i < this._textOrder.length; i++)
- {
- text = this[this._textOrder[i]];
- }
- if(this._statusText.val != text.val || force)
- {
- if(this._noUpdate)
- {
- return;
- }
- this._statusText = text;
- this.setStatusField(this._service.status, text, false);
- if(text.val && this._service.statusTimeout)
- {
- this.setTimer(text.type);
- }
- }
- },
- setFullScreenState: function(isFullScreen, isVideo)
- {
- this._isFullScreen = isFullScreen;
- this._isVideo = isFullScreen && isVideo;
- this.clearStatusField();
- this.updateStatusField(true);
- },
- setTimer: function(type)
- {
- let typeArgs = type.split(" ", 3);
- if(typeArgs.length < 2 || typeArgs[0] != "status")
- {
- return;
- }
- if(typeArgs[1] == "chrome")
- {
- this.clearTimer("_statusChromeTimeoutID");
- this._statusChromeTimeoutID = this._window.setTimeout(function(self, isDefault)
- {
- self._statusChromeTimeoutID = 0;
- if(isDefault)
- {
- self.setDefaultStatus("");
- }
- else
- {
- self.setStatusText("");
- }
- }, this._service.statusTimeout, this, (typeArgs.length == 3 && typeArgs[2] == "default"));
- }
- else
- {
- this.clearTimer("_statusContentTimeoutID");
- this._statusContentTimeoutID = this._window.setTimeout(function(self)
- {
- self._statusContentTimeoutID = 0;
- self.setJSStatus("");
- }, this._service.statusTimeout, this);
- }
- },
- clearTimer: function(timerName)
- {
- if(this[timerName] != 0)
- {
- this._window.clearTimeout(this[timerName]);
- this[timerName] = 0;
- }
- },
- clearStatusField: function()
- {
- this._getters.statusOverlay.value = "";
- let status_label = this._getters.statusWidgetLabel;
- if(status_label)
- {
- status_label.value = "";
- }
- },
- setStatusField: function(location, text, allowTooltip)
- {
- if(!location)
- {
- return;
- }
- let label = null;
- if(this._isFullScreen)
- {
- switch(location)
- {
- case 1: // Toolbar
- location = 3
- break;
- case 2: // URL bar
- if(Services.prefs.getBoolPref("browser.fullscreen.autohide"))
- {
- location = 3
- }
- break;
- }
- }
- switch(location)
- {
- case 1: // Toolbar
- label = this._getters.statusWidgetLabel;
- break;
- case 2: // URL Bar
- break;
- case 3: // Popup
- default:
- if(this._isVideo)
- {
- return;
- }
- label = this._getters.statusOverlay;
- break;
- }
- if(label)
- {
- label.setAttribute("previoustype", label.getAttribute("type"));
- label.setAttribute("type", text.type);
- label.value = text.val;
- label.setAttribute("crop", text.type == "overLink" ? "center" : "end");
- }
- }
- };
- function S4EOverlinkService(window, service, statusService) {
- this._window = window;
- this._service = service;
- this._statusService = statusService;
- }
- S4EOverlinkService.prototype =
- {
- _window: null,
- _service: null,
- _statusService: null,
- _timer: 0,
- _currentLink: { link: "", anchor: null },
- _pendingLink: { link: "", anchor: null },
- _listening: false,
- update: function(aLink, aAnchor)
- {
- this.clearTimer();
- this.stopListen();
- this._pendingLink = { link: aLink, anchor: aAnchor };
- if(!aLink)
- {
- if(this._window.XULBrowserWindow.hideOverLinkImmediately || !this._service.statusLinkOverDelayHide)
- {
- this._show();
- }
- else
- {
- this._showDelayed();
- }
- }
- else if(this._currentLink.link || !this._service.statusLinkOverDelayShow)
- {
- this._show();
- }
- else
- {
- this._showDelayed();
- this.startListen();
- }
- },
- destroy: function()
- {
- this.clearTimer();
- this.stopListen();
- ["_currentLink", "_pendingLink", "_statusService", "_window"].forEach(function(prop)
- {
- delete this[prop];
- }, this);
- },
- startListen: function()
- {
- if(!this._listening)
- {
- this._window.addEventListener("mousemove", this, true);
- this._listening = true;
- }
- },
- stopListen: function()
- {
- if(this._listening)
- {
- this._window.removeEventListener("mousemove", this, true);
- this._listening = false;
- }
- },
- clearTimer: function()
- {
- if(this._timer != 0)
- {
- this._window.clearTimeout(this._timer);
- this._timer = 0;
- }
- },
- handleEvent: function(event)
- {
- switch(event.type)
- {
- case "mousemove":
- this.clearTimer();
- this._showDelayed();
- }
- },
- _showDelayed: function()
- {
- let delay = ((this._pendingLink.link)
- ? this._service.statusLinkOverDelayShow
- : this._service.statusLinkOverDelayHide);
- this._timer = this._window.setTimeout(function(self)
- {
- self._timer = 0;
- self._show();
- self.stopListen();
- }, delay, this);
- },
- _show: function()
- {
- this._currentLink = this._pendingLink;
- this._statusService.setOverLinkInternal(this._currentLink.link, this._currentLink.anchor);
- }
- };
|