Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/objectTabs.js

Issue 29329839: Issue 3228 - Unbreak object tabs (Closed)
Patch Set: Added comments Created Nov. 25, 2015, 10:49 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/main.js ('k') | lib/requestNotifier.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/objectTabs.js
===================================================================
--- a/lib/objectTabs.js
+++ b/lib/objectTabs.js
@@ -14,485 +14,99 @@
* You should have received a copy of the GNU General Public License
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @fileOverview Code responsible for showing and hiding object tabs.
*/
+let {Prefs} = require("prefs");
+let {Utils} = require("utils");
+
/**
- * Class responsible for showing and hiding object tabs.
- * @class
+ * Random element class, to be used for object tabs displayed on top of the
+ * plugin content.
+ * @type string
*/
-var objTabs =
+let classVisibleTop = null;
+
+/**
+ * Random element class, to be used for object tabs displayed at the bottom of
+ * the plugin content.
+ * @type string
+ */
+let classVisibleBottom = null;
+
+/**
+ * Random element class, to be used for object tabs that are hidden.
+ * @type string
+ */
+let classHidden = null;
+
+Utils.addChildMessageListener("AdblockPlus:GetObjectTabsStatus", function()
{
- /**
- * Number of milliseconds to wait until hiding tab after the mouse moves away.
- * @type Integer
- */
- HIDE_DELAY: 1000,
+ let {UI} = require("ui");
- /**
- * Flag used to trigger object tabs initialization first time object tabs are
- * used.
- * @type Boolean
- */
- initialized: false,
+ return !!(Prefs.enabled && Prefs.frameobjects && UI.overlay && classHidden);
+});
- /**
- * Will be set to true while initialization is in progress.
- * @type Boolean
- */
- initializing: false,
+Utils.addChildMessageListener("AdblockPlus:GetObjectTabsTexts", function()
+{
+ let {UI} = require("ui");
- /**
- * Parameters for _showTab, to be called once initialization is complete.
- */
- delayedShowParams: null,
+ return {
+ label: UI.overlay.attributes.objtabtext,
+ tooltip: UI.overlay.attributes.objtabtooltip,
+ classVisibleTop, classVisibleBottom, classHidden
+ };
+});
- /**
- * Randomly generated class to be used for visible object tabs on top of object.
- * @type String
- */
- objTabClassVisibleTop: null,
+Utils.addChildMessageListener("AdblockPlus:BlockItem", function(item)
+{
+ let {UI} = require("ui");
+ UI.blockItem(UI.currentWindow, null, item);
+});
- /**
- * Randomly generated class to be used for visible object tabs at the bottom of the object.
- * @type String
- */
- objTabClassVisibleBottom: null,
-
- /**
- * Randomly generated class to be used for invisible object tabs.
- * @type String
- */
- objTabClassHidden: null,
-
- /**
- * Document element the object tab is currently being displayed for.
- * @type Element
- */
- currentElement: null,
-
- /**
- * Windows that the window event handler is currently registered for.
- * @type Window[]
- */
- windowListeners: null,
-
- /**
- * Panel element currently used as object tab.
- * @type Element
- */
- objtabElement: null,
-
- /**
- * Time of previous position update.
- * @type Integer
- */
- prevPositionUpdate: 0,
-
- /**
- * Timer used to update position of the object tab.
- * @type nsITimer
- */
- positionTimer: null,
-
- /**
- * Timer used to delay hiding of the object tab.
- * @type nsITimer
- */
- hideTimer: null,
-
- /**
- * Used when hideTimer is running, time when the tab should be hidden.
- * @type Integer
- */
- hideTargetTime: 0,
-
- /**
- * Initializes object tabs (generates random classes and registers stylesheet).
- */
- _initCSS: function()
+function init()
+{
+ function processCSSData(event)
{
- function processCSSData(request)
- {
- if (onShutdown.done)
- return;
-
- let data = request.responseText;
-
- let rnd = [];
- let offset = "a".charCodeAt(0);
- for (let i = 0; i < 60; i++)
- rnd.push(offset + Math.random() * 26);
-
- this.objTabClassVisibleTop = String.fromCharCode.apply(String, rnd.slice(0, 20));
- this.objTabClassVisibleBottom = String.fromCharCode.apply(String, rnd.slice(20, 40));
- this.objTabClassHidden = String.fromCharCode.apply(String, rnd.slice(40, 60));
-
- let {Utils} = require("utils");
- let url = Utils.makeURI("data:text/css," + encodeURIComponent(data.replace(/%%CLASSVISIBLETOP%%/g, this.objTabClassVisibleTop)
- .replace(/%%CLASSVISIBLEBOTTOM%%/g, this.objTabClassVisibleBottom)
- .replace(/%%CLASSHIDDEN%%/g, this.objTabClassHidden)));
- Utils.styleService.loadAndRegisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET);
- onShutdown.add(function()
- {
- Utils.styleService.unregisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET);
- });
-
- this.initializing = false;
- this.initialized = true;
-
- if (this.delayedShowParams)
- this._showTab.apply(this, this.delayedShowParams);
- }
-
- this.delayedShowParams = arguments;
-
- if (!this.initializing)
- {
- this.initializing = true;
-
- // Load CSS asynchronously
- try {
- let request = new XMLHttpRequest();
- request.mozBackgroundRequest = true;
- request.open("GET", "chrome://adblockplus/content/objtabs.css");
- request.overrideMimeType("text/plain");
-
- request.addEventListener("load", processCSSData.bind(this, request), false);
- request.send(null);
- }
- catch (e)
- {
- Cu.reportError(e);
- this.initializing = false;
- }
- }
- },
-
- /**
- * Called to show object tab for an element.
- */
- showTabFor: function(/**Element*/ element)
- {
- // Object tabs aren't usable in Fennec
- let {application} = require("info");
- if (application == "fennec" || application == "fennec2" ||
- application == "adblockbrowser")
+ if (onShutdown.done)
return;
- let {Prefs} = require("prefs");
- if (!Prefs.frameobjects)
- return;
+ let data = event.target.responseText;
- if (this.hideTimer)
+ let rnd = [];
+ let offset = "a".charCodeAt(0);
+ for (let i = 0; i < 60; i++)
+ rnd.push(offset + Math.random() * 26);
+
+ classVisibleTop = String.fromCharCode.apply(String, rnd.slice(0, 20));
+ classVisibleBottom = String.fromCharCode.apply(String, rnd.slice(20, 40));
+ classHidden = String.fromCharCode.apply(String, rnd.slice(40, 60));
+
+ let url = Utils.makeURI("data:text/css," + encodeURIComponent(data.replace(/%%CLASSVISIBLETOP%%/g, classVisibleTop)
+ .replace(/%%CLASSVISIBLEBOTTOM%%/g, classVisibleBottom)
+ .replace(/%%CLASSHIDDEN%%/g, classHidden)));
+ Utils.styleService.loadAndRegisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET);
+ onShutdown.add(function()
{
- this.hideTimer.cancel();
- this.hideTimer = null;
- }
+ Utils.styleService.unregisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET);
+ });
+ }
- if (this.objtabElement)
- this.objtabElement.style.setProperty("opacity", "1", "important");
-
- if (this.currentElement != element)
- {
- this._hideTab();
-
- let {Policy} = require("contentPolicy");
- let {RequestNotifier} = require("requestNotifier");
- let data = RequestNotifier.getDataForNode(element, true, "OBJECT");
- if (data)
- {
- if (this.initialized)
- this._showTab(element, data[1]);
- else
- this._initCSS(element, data[1]);
- }
- }
- },
-
- /**
- * Called to hide object tab for an element (actual hiding happens delayed).
- */
- hideTabFor: function(/**Element*/ element)
+ // Load CSS asynchronously
+ try
{
- if (element != this.currentElement || this.hideTimer)
- return;
-
- this.hideTargetTime = Date.now() + this.HIDE_DELAY;
- this.hideTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- this.hideTimer.init(this, 40, Ci.nsITimer.TYPE_REPEATING_SLACK);
- },
-
- /**
- * Makes the tab element visible.
- * @param {Element} element
- * @param {RequestEntry} data
- */
- _showTab: function(element, data)
+ let request = new XMLHttpRequest();
+ request.mozBackgroundRequest = true;
+ request.open("GET", "chrome://adblockplus/content/objtabs.css");
+ request.overrideMimeType("text/plain");
+ request.addEventListener("load", processCSSData, false);
+ request.send(null);
+ }
+ catch (e)
{
- let {UI} = require("ui");
- if (!UI.overlay)
- return;
-
- let doc = element.ownerDocument.defaultView.top.document;
-
- this.objtabElement = doc.createElementNS("http://www.w3.org/1999/xhtml", "a");
- this.objtabElement.textContent = UI.overlay.attributes.objtabtext;
- this.objtabElement.setAttribute("title", UI.overlay.attributes.objtabtooltip);
- this.objtabElement.setAttribute("href", data.location);
- this.objtabElement.setAttribute("class", this.objTabClassHidden);
- this.objtabElement.style.setProperty("opacity", "1", "important");
- this.objtabElement.nodeData = data;
-
- this.currentElement = element;
-
- // Register paint listeners for the relevant windows
- this.windowListeners = [];
- let wnd = element.ownerDocument.defaultView;
- while (wnd)
- {
- wnd.addEventListener("MozAfterPaint", objectWindowEventHandler, false);
- this.windowListeners.push(wnd);
- wnd = (wnd.parent != wnd ? wnd.parent : null);
- }
-
- // Register mouse listeners on the object tab
- this.objtabElement.addEventListener("mouseover", objectTabEventHander, false);
- this.objtabElement.addEventListener("mouseout", objectTabEventHander, false);
- this.objtabElement.addEventListener("click", objectTabEventHander, true);
-
- // Insert the tab into the document and adjust its position
- doc.documentElement.appendChild(this.objtabElement);
- if (!this.positionTimer)
- {
- this.positionTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- this.positionTimer.init(this, 200, Ci.nsITimer.TYPE_REPEATING_SLACK);
- }
- this._positionTab();
- },
-
- /**
- * Hides the tab element.
- */
- _hideTab: function()
- {
- this.delayedShowParams = null;
-
- if (this.objtabElement)
- {
- // Prevent recursive calls via popuphidden handler
- let objtab = this.objtabElement;
- this.objtabElement = null;
- this.currentElement = null;
-
- if (this.hideTimer)
- {
- this.hideTimer.cancel();
- this.hideTimer = null;
- }
-
- if (this.positionTimer)
- {
- this.positionTimer.cancel();
- this.positionTimer = null;
- }
-
- try {
- objtab.parentNode.removeChild(objtab);
- } catch (e) {}
- objtab.removeEventListener("mouseover", objectTabEventHander, false);
- objtab.removeEventListener("mouseout", objectTabEventHander, false);
- objtab.nodeData = null;
-
- for (let wnd of this.windowListeners)
- wnd.removeEventListener("MozAfterPaint", objectWindowEventHandler, false);
- this.windowListeners = null;
- }
- },
-
- /**
- * Updates position of the tab element.
- */
- _positionTab: function()
- {
- // Test whether element is still in document
- let elementDoc = null;
- try
- {
- elementDoc = this.currentElement.ownerDocument;
- } catch (e) {} // Ignore "can't access dead object" error
- if (!elementDoc || !this.currentElement.offsetWidth || !this.currentElement.offsetHeight ||
- !elementDoc.defaultView || !elementDoc.documentElement)
- {
- this._hideTab();
- return;
- }
-
- let objRect = this._getElementPosition(this.currentElement);
-
- let className = this.objTabClassVisibleTop;
- let left = objRect.right - this.objtabElement.offsetWidth;
- let top = objRect.top - this.objtabElement.offsetHeight;
- if (top < 0)
- {
- top = objRect.bottom;
- className = this.objTabClassVisibleBottom;
- }
-
- if (this.objtabElement.style.left != left + "px")
- this.objtabElement.style.setProperty("left", left + "px", "important");
- if (this.objtabElement.style.top != top + "px")
- this.objtabElement.style.setProperty("top", top + "px", "important");
-
- if (this.objtabElement.getAttribute("class") != className)
- this.objtabElement.setAttribute("class", className);
-
- this.prevPositionUpdate = Date.now();
- },
-
- /**
- * Calculates element's position relative to the top frame and considering
- * clipping due to scrolling.
- * @return {{left: Number, top: Number, right: Number, bottom: Number}}
- */
- _getElementPosition: function(/**Element*/ element)
- {
- // Restrict rectangle coordinates by the boundaries of a window's client area
- function intersectRect(rect, wnd)
- {
- // Cannot use wnd.innerWidth/Height because they won't account for scrollbars
- let doc = wnd.document;
- let wndWidth = doc.documentElement.clientWidth;
- let wndHeight = doc.documentElement.clientHeight;
- if (doc.compatMode == "BackCompat") // clientHeight will be bogus in quirks mode
- wndHeight = Math.max(doc.documentElement.offsetHeight, doc.body.offsetHeight) - wnd.scrollMaxY - 1;
-
- rect.left = Math.max(rect.left, 0);
- rect.top = Math.max(rect.top, 0);
- rect.right = Math.min(rect.right, wndWidth);
- rect.bottom = Math.min(rect.bottom, wndHeight);
- }
-
- let rect = element.getBoundingClientRect();
- let wnd = element.ownerDocument.defaultView;
-
- let style = wnd.getComputedStyle(element, null);
- let offsets = [
- parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft),
- parseFloat(style.borderTopWidth) + parseFloat(style.paddingTop),
- parseFloat(style.borderRightWidth) + parseFloat(style.paddingRight),
- parseFloat(style.borderBottomWidth) + parseFloat(style.paddingBottom)
- ];
-
- rect = {left: rect.left + offsets[0], top: rect.top + offsets[1],
- right: rect.right - offsets[2], bottom: rect.bottom - offsets[3]};
- while (true)
- {
- intersectRect(rect, wnd);
-
- if (!wnd.frameElement)
- break;
-
- // Recalculate coordinates to be relative to frame's parent window
- let frameElement = wnd.frameElement;
- wnd = frameElement.ownerDocument.defaultView;
-
- let frameRect = frameElement.getBoundingClientRect();
- let frameStyle = wnd.getComputedStyle(frameElement, null);
- let relLeft = frameRect.left + parseFloat(frameStyle.borderLeftWidth) + parseFloat(frameStyle.paddingLeft);
- let relTop = frameRect.top + parseFloat(frameStyle.borderTopWidth) + parseFloat(frameStyle.paddingTop);
-
- rect.left += relLeft;
- rect.right += relLeft;
- rect.top += relTop;
- rect.bottom += relTop;
- }
-
- return rect;
- },
-
- doBlock: function()
- {
- let {UI} = require("ui");
- let {Utils} = require("utils");
- let chromeWindow = Utils.getChromeWindow(this.currentElement.ownerDocument.defaultView);
- UI.blockItem(chromeWindow, this.currentElement, this.objtabElement.nodeData);
- },
-
- /**
- * Called whenever a timer fires.
- * @param {nsISupport} subject
- * @param {string} topic
- * @param {string} data
- */
- observe: function(subject, topic, data)
- {
- if (subject == this.positionTimer)
- {
- // Don't update position if it was already updated recently (via MozAfterPaint)
- if (Date.now() - this.prevPositionUpdate > 100)
- this._positionTab();
- }
- else if (subject == this.hideTimer)
- {
- let now = Date.now();
- if (now >= this.hideTargetTime)
- this._hideTab();
- else if (this.hideTargetTime - now < this.HIDE_DELAY / 2)
- this.objtabElement.style.setProperty("opacity", (this.hideTargetTime - now) * 2 / this.HIDE_DELAY, "important");
- }
+ Cu.reportError(e);
}
-};
-
-onShutdown.add(objTabs._hideTab.bind(objTabs));
-
-/**
- * Function called whenever the mouse enters or leaves an object.
- */
-function objectMouseEventHander(/**Event*/ event)
-{
- if (!event.isTrusted)
- return;
-
- if (event.type == "mouseover")
- objTabs.showTabFor(event.target);
- else if (event.type == "mouseout")
- objTabs.hideTabFor(event.target);
}
-
-/**
- * Function called for paint events of the object tab window.
- */
-function objectWindowEventHandler(/**Event*/ event)
-{
- if (!event.isTrusted)
- return;
-
- // Don't trigger update too often, avoid overusing CPU on frequent page updates
- if (event.type == "MozAfterPaint" && Date.now() - objTabs.prevPositionUpdate > 20)
- objTabs._positionTab();
-}
-
-/**
- * Function called whenever the mouse enters or leaves an object tab.
- */
-function objectTabEventHander(/**Event*/ event)
-{
- if (onShutdown.done || !event.isTrusted)
- return;
-
- if (event.type == "click" && event.button == 0)
- {
- event.preventDefault();
- event.stopPropagation();
-
- objTabs.doBlock();
- }
- else if (event.type == "mouseover")
- objTabs.showTabFor(objTabs.currentElement);
- else if (event.type == "mouseout")
- objTabs.hideTabFor(objTabs.currentElement);
-}
-exports.objectMouseEventHander = objectMouseEventHander;
+init();
« no previous file with comments | « lib/main.js ('k') | lib/requestNotifier.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld