| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-2015 Eyeo GmbH | 3  * Copyright (C) 2006-2015 Eyeo GmbH | 
| 4  * | 4  * | 
| 5  * Adblock Plus is free software: you can redistribute it and/or modify | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
| 6  * it under the terms of the GNU General Public License version 3 as | 6  * it under the terms of the GNU General Public License version 3 as | 
| 7  * published by the Free Software Foundation. | 7  * published by the Free Software Foundation. | 
| 8  * | 8  * | 
| 9  * Adblock Plus is distributed in the hope that it will be useful, | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
| 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 12  * GNU General Public License for more details. | 12  * GNU General Public License for more details. | 
| 13  * | 13  * | 
| 14  * You should have received a copy of the GNU General Public License | 14  * You should have received a copy of the GNU General Public License | 
| 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| 16  */ | 16  */ | 
| 17 | 17 | 
| 18 /** | 18 /** | 
| 19  * @fileOverview Code responsible for showing and hiding object tabs. | 19  * @fileOverview Code responsible for showing and hiding object tabs. | 
| 20  */ | 20  */ | 
| 21 | 21 | 
|  | 22 let {Prefs} = require("prefs"); | 
|  | 23 let {Utils} = require("utils"); | 
|  | 24 | 
| 22 /** | 25 /** | 
| 23  * Class responsible for showing and hiding object tabs. | 26  * Random element class, to be used for object tabs displayed on top of the | 
| 24  * @class | 27  * plugin content. | 
|  | 28  * @type string | 
| 25  */ | 29  */ | 
| 26 var objTabs = | 30 let classVisibleTop = null; | 
|  | 31 | 
|  | 32 /** | 
|  | 33  * Random element class, to be used for object tabs displayed at the bottom of | 
|  | 34  * the plugin content. | 
|  | 35  * @type string | 
|  | 36  */ | 
|  | 37 let classVisibleBottom = null; | 
|  | 38 | 
|  | 39 /** | 
|  | 40  * Random element class, to be used for object tabs that are hidden. | 
|  | 41  * @type string | 
|  | 42  */ | 
|  | 43 let classHidden = null; | 
|  | 44 | 
|  | 45 Utils.addChildMessageListener("AdblockPlus:GetObjectTabsStatus", function() | 
| 27 { | 46 { | 
| 28   /** | 47   let {UI} = require("ui"); | 
| 29    * Number of milliseconds to wait until hiding tab after the mouse moves away. |  | 
| 30    * @type Integer |  | 
| 31    */ |  | 
| 32   HIDE_DELAY: 1000, |  | 
| 33 | 48 | 
| 34   /** | 49   return !!(Prefs.enabled && Prefs.frameobjects && UI.overlay && classHidden); | 
| 35    * Flag used to trigger object tabs initialization first time object tabs are | 50 }); | 
| 36    * used. |  | 
| 37    * @type Boolean |  | 
| 38    */ |  | 
| 39   initialized: false, |  | 
| 40 | 51 | 
| 41   /** | 52 Utils.addChildMessageListener("AdblockPlus:GetObjectTabsTexts", function() | 
| 42    * Will be set to true while initialization is in progress. | 53 { | 
| 43    * @type Boolean | 54   let {UI} = require("ui"); | 
| 44    */ |  | 
| 45   initializing: false, |  | 
| 46 | 55 | 
| 47   /** | 56   return { | 
| 48    * Parameters for _showTab, to be called once initialization is complete. | 57     label: UI.overlay.attributes.objtabtext, | 
| 49    */ | 58     tooltip: UI.overlay.attributes.objtabtooltip, | 
| 50   delayedShowParams: null, | 59     classVisibleTop, classVisibleBottom, classHidden | 
|  | 60   }; | 
|  | 61 }); | 
| 51 | 62 | 
| 52   /** | 63 Utils.addChildMessageListener("AdblockPlus:BlockItem", function(item) | 
| 53    * Randomly generated class to be used for visible object tabs on top of objec
     t. | 64 { | 
| 54    * @type String | 65   let {UI} = require("ui"); | 
| 55    */ | 66   UI.blockItem(UI.currentWindow, null, item); | 
| 56   objTabClassVisibleTop: null, | 67 }); | 
| 57 | 68 | 
| 58   /** | 69 function init() | 
| 59    * Randomly generated class to be used for visible object tabs at the bottom o
     f the object. | 70 { | 
| 60    * @type String | 71   function processCSSData(event) | 
| 61    */ |  | 
| 62   objTabClassVisibleBottom: null, |  | 
| 63 |  | 
| 64   /** |  | 
| 65    * Randomly generated class to be used for invisible object tabs. |  | 
| 66    * @type String |  | 
| 67    */ |  | 
| 68   objTabClassHidden: null, |  | 
| 69 |  | 
| 70   /** |  | 
| 71    * Document element the object tab is currently being displayed for. |  | 
| 72    * @type Element |  | 
| 73    */ |  | 
| 74   currentElement: null, |  | 
| 75 |  | 
| 76   /** |  | 
| 77    * Windows that the window event handler is currently registered for. |  | 
| 78    * @type Window[] |  | 
| 79    */ |  | 
| 80   windowListeners: null, |  | 
| 81 |  | 
| 82   /** |  | 
| 83    * Panel element currently used as object tab. |  | 
| 84    * @type Element |  | 
| 85    */ |  | 
| 86   objtabElement: null, |  | 
| 87 |  | 
| 88   /** |  | 
| 89    * Time of previous position update. |  | 
| 90    * @type Integer |  | 
| 91    */ |  | 
| 92   prevPositionUpdate: 0, |  | 
| 93 |  | 
| 94   /** |  | 
| 95    * Timer used to update position of the object tab. |  | 
| 96    * @type nsITimer |  | 
| 97    */ |  | 
| 98   positionTimer: null, |  | 
| 99 |  | 
| 100   /** |  | 
| 101    * Timer used to delay hiding of the object tab. |  | 
| 102    * @type nsITimer |  | 
| 103    */ |  | 
| 104   hideTimer: null, |  | 
| 105 |  | 
| 106   /** |  | 
| 107    * Used when hideTimer is running, time when the tab should be hidden. |  | 
| 108    * @type Integer |  | 
| 109    */ |  | 
| 110   hideTargetTime: 0, |  | 
| 111 |  | 
| 112   /** |  | 
| 113    * Initializes object tabs (generates random classes and registers stylesheet)
     . |  | 
| 114    */ |  | 
| 115   _initCSS: function() |  | 
| 116   { | 72   { | 
| 117     function processCSSData(request) | 73     if (onShutdown.done) | 
| 118     { |  | 
| 119       if (onShutdown.done) |  | 
| 120         return; |  | 
| 121 |  | 
| 122       let data = request.responseText; |  | 
| 123 |  | 
| 124       let rnd = []; |  | 
| 125       let offset = "a".charCodeAt(0); |  | 
| 126       for (let i = 0; i < 60; i++) |  | 
| 127         rnd.push(offset + Math.random() * 26); |  | 
| 128 |  | 
| 129       this.objTabClassVisibleTop = String.fromCharCode.apply(String, rnd.slice(0
     , 20)); |  | 
| 130       this.objTabClassVisibleBottom = String.fromCharCode.apply(String, rnd.slic
     e(20, 40)); |  | 
| 131       this.objTabClassHidden = String.fromCharCode.apply(String, rnd.slice(40, 6
     0)); |  | 
| 132 |  | 
| 133       let {Utils} = require("utils"); |  | 
| 134       let url = Utils.makeURI("data:text/css," + encodeURIComponent(data.replace
     (/%%CLASSVISIBLETOP%%/g, this.objTabClassVisibleTop) |  | 
| 135                                                                         .replace
     (/%%CLASSVISIBLEBOTTOM%%/g, this.objTabClassVisibleBottom) |  | 
| 136                                                                         .replace
     (/%%CLASSHIDDEN%%/g, this.objTabClassHidden))); |  | 
| 137       Utils.styleService.loadAndRegisterSheet(url, Ci.nsIStyleSheetService.USER_
     SHEET); |  | 
| 138       onShutdown.add(function() |  | 
| 139       { |  | 
| 140         Utils.styleService.unregisterSheet(url, Ci.nsIStyleSheetService.USER_SHE
     ET); |  | 
| 141       }); |  | 
| 142 |  | 
| 143       this.initializing = false; |  | 
| 144       this.initialized = true; |  | 
| 145 |  | 
| 146       if (this.delayedShowParams) |  | 
| 147         this._showTab.apply(this, this.delayedShowParams); |  | 
| 148     } |  | 
| 149 |  | 
| 150     this.delayedShowParams = arguments; |  | 
| 151 |  | 
| 152     if (!this.initializing) |  | 
| 153     { |  | 
| 154       this.initializing = true; |  | 
| 155 |  | 
| 156       // Load CSS asynchronously |  | 
| 157       try { |  | 
| 158         let request = new XMLHttpRequest(); |  | 
| 159         request.mozBackgroundRequest = true; |  | 
| 160         request.open("GET", "chrome://adblockplus/content/objtabs.css"); |  | 
| 161         request.overrideMimeType("text/plain"); |  | 
| 162 |  | 
| 163         request.addEventListener("load", processCSSData.bind(this, request), fal
     se); |  | 
| 164         request.send(null); |  | 
| 165       } |  | 
| 166       catch (e) |  | 
| 167       { |  | 
| 168         Cu.reportError(e); |  | 
| 169         this.initializing = false; |  | 
| 170       } |  | 
| 171     } |  | 
| 172   }, |  | 
| 173 |  | 
| 174   /** |  | 
| 175    * Called to show object tab for an element. |  | 
| 176    */ |  | 
| 177   showTabFor: function(/**Element*/ element) |  | 
| 178   { |  | 
| 179     // Object tabs aren't usable in Fennec |  | 
| 180     let {application} = require("info"); |  | 
| 181     if (application == "fennec" || application == "fennec2" || |  | 
| 182         application == "adblockbrowser") |  | 
| 183       return; | 74       return; | 
| 184 | 75 | 
| 185     let {Prefs} = require("prefs"); | 76     let data = event.target.responseText; | 
| 186     if (!Prefs.frameobjects) |  | 
| 187       return; |  | 
| 188 | 77 | 
| 189     if (this.hideTimer) | 78     let rnd = []; | 
|  | 79     let offset = "a".charCodeAt(0); | 
|  | 80     for (let i = 0; i < 60; i++) | 
|  | 81       rnd.push(offset + Math.random() * 26); | 
|  | 82 | 
|  | 83     classVisibleTop = String.fromCharCode.apply(String, rnd.slice(0, 20)); | 
|  | 84     classVisibleBottom = String.fromCharCode.apply(String, rnd.slice(20, 40)); | 
|  | 85     classHidden = String.fromCharCode.apply(String, rnd.slice(40, 60)); | 
|  | 86 | 
|  | 87     let url = Utils.makeURI("data:text/css," + encodeURIComponent(data.replace(/
     %%CLASSVISIBLETOP%%/g, classVisibleTop) | 
|  | 88                                                                       .replace(/
     %%CLASSVISIBLEBOTTOM%%/g, classVisibleBottom) | 
|  | 89                                                                       .replace(/
     %%CLASSHIDDEN%%/g, classHidden))); | 
|  | 90     Utils.styleService.loadAndRegisterSheet(url, Ci.nsIStyleSheetService.USER_SH
     EET); | 
|  | 91     onShutdown.add(function() | 
| 190     { | 92     { | 
| 191       this.hideTimer.cancel(); | 93       Utils.styleService.unregisterSheet(url, Ci.nsIStyleSheetService.USER_SHEET
     ); | 
| 192       this.hideTimer = null; | 94     }); | 
| 193     } | 95   } | 
| 194 | 96 | 
| 195     if (this.objtabElement) | 97   // Load CSS asynchronously | 
| 196       this.objtabElement.style.setProperty("opacity", "1", "important"); | 98   try | 
| 197 |  | 
| 198     if (this.currentElement != element) |  | 
| 199     { |  | 
| 200       this._hideTab(); |  | 
| 201 |  | 
| 202       let {Policy} = require("contentPolicy"); |  | 
| 203       let {RequestNotifier} = require("requestNotifier"); |  | 
| 204       let data = RequestNotifier.getDataForNode(element, true, "OBJECT"); |  | 
| 205       if (data) |  | 
| 206       { |  | 
| 207         if (this.initialized) |  | 
| 208           this._showTab(element, data[1]); |  | 
| 209         else |  | 
| 210           this._initCSS(element, data[1]); |  | 
| 211       } |  | 
| 212     } |  | 
| 213   }, |  | 
| 214 |  | 
| 215   /** |  | 
| 216    * Called to hide object tab for an element (actual hiding happens delayed). |  | 
| 217    */ |  | 
| 218   hideTabFor: function(/**Element*/ element) |  | 
| 219   { | 99   { | 
| 220     if (element != this.currentElement || this.hideTimer) | 100     let request = new XMLHttpRequest(); | 
| 221       return; | 101     request.mozBackgroundRequest = true; | 
| 222 | 102     request.open("GET", "chrome://adblockplus/content/objtabs.css"); | 
| 223     this.hideTargetTime = Date.now() + this.HIDE_DELAY; | 103     request.overrideMimeType("text/plain"); | 
| 224     this.hideTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); | 104     request.addEventListener("load", processCSSData, false); | 
| 225     this.hideTimer.init(this, 40, Ci.nsITimer.TYPE_REPEATING_SLACK); | 105     request.send(null); | 
| 226   }, | 106   } | 
| 227 | 107   catch (e) | 
| 228   /** |  | 
| 229    * Makes the tab element visible. |  | 
| 230    * @param {Element} element |  | 
| 231    * @param {RequestEntry} data |  | 
| 232    */ |  | 
| 233   _showTab: function(element, data) |  | 
| 234   { | 108   { | 
| 235     let {UI} = require("ui"); | 109     Cu.reportError(e); | 
| 236     if (!UI.overlay) |  | 
| 237       return; |  | 
| 238 |  | 
| 239     let doc = element.ownerDocument.defaultView.top.document; |  | 
| 240 |  | 
| 241     this.objtabElement = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"
     ); |  | 
| 242     this.objtabElement.textContent = UI.overlay.attributes.objtabtext; |  | 
| 243     this.objtabElement.setAttribute("title", UI.overlay.attributes.objtabtooltip
     ); |  | 
| 244     this.objtabElement.setAttribute("href", data.location); |  | 
| 245     this.objtabElement.setAttribute("class", this.objTabClassHidden); |  | 
| 246     this.objtabElement.style.setProperty("opacity", "1", "important"); |  | 
| 247     this.objtabElement.nodeData = data; |  | 
| 248 |  | 
| 249     this.currentElement = element; |  | 
| 250 |  | 
| 251     // Register paint listeners for the relevant windows |  | 
| 252     this.windowListeners = []; |  | 
| 253     let wnd = element.ownerDocument.defaultView; |  | 
| 254     while (wnd) |  | 
| 255     { |  | 
| 256       wnd.addEventListener("MozAfterPaint", objectWindowEventHandler, false); |  | 
| 257       this.windowListeners.push(wnd); |  | 
| 258       wnd = (wnd.parent != wnd ? wnd.parent : null); |  | 
| 259     } |  | 
| 260 |  | 
| 261     // Register mouse listeners on the object tab |  | 
| 262     this.objtabElement.addEventListener("mouseover", objectTabEventHander, false
     ); |  | 
| 263     this.objtabElement.addEventListener("mouseout", objectTabEventHander, false)
     ; |  | 
| 264     this.objtabElement.addEventListener("click", objectTabEventHander, true); |  | 
| 265 |  | 
| 266     // Insert the tab into the document and adjust its position |  | 
| 267     doc.documentElement.appendChild(this.objtabElement); |  | 
| 268     if (!this.positionTimer) |  | 
| 269     { |  | 
| 270       this.positionTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer
     ); |  | 
| 271       this.positionTimer.init(this, 200, Ci.nsITimer.TYPE_REPEATING_SLACK); |  | 
| 272     } |  | 
| 273     this._positionTab(); |  | 
| 274   }, |  | 
| 275 |  | 
| 276   /** |  | 
| 277    * Hides the tab element. |  | 
| 278    */ |  | 
| 279   _hideTab: function() |  | 
| 280   { |  | 
| 281     this.delayedShowParams = null; |  | 
| 282 |  | 
| 283     if (this.objtabElement) |  | 
| 284     { |  | 
| 285       // Prevent recursive calls via popuphidden handler |  | 
| 286       let objtab = this.objtabElement; |  | 
| 287       this.objtabElement = null; |  | 
| 288       this.currentElement = null; |  | 
| 289 |  | 
| 290       if (this.hideTimer) |  | 
| 291       { |  | 
| 292         this.hideTimer.cancel(); |  | 
| 293         this.hideTimer = null; |  | 
| 294       } |  | 
| 295 |  | 
| 296       if (this.positionTimer) |  | 
| 297       { |  | 
| 298         this.positionTimer.cancel(); |  | 
| 299         this.positionTimer = null; |  | 
| 300       } |  | 
| 301 |  | 
| 302       try { |  | 
| 303         objtab.parentNode.removeChild(objtab); |  | 
| 304       } catch (e) {} |  | 
| 305       objtab.removeEventListener("mouseover", objectTabEventHander, false); |  | 
| 306       objtab.removeEventListener("mouseout", objectTabEventHander, false); |  | 
| 307       objtab.nodeData = null; |  | 
| 308 |  | 
| 309       for (let wnd of this.windowListeners) |  | 
| 310         wnd.removeEventListener("MozAfterPaint", objectWindowEventHandler, false
     ); |  | 
| 311       this.windowListeners = null; |  | 
| 312     } |  | 
| 313   }, |  | 
| 314 |  | 
| 315   /** |  | 
| 316    * Updates position of the tab element. |  | 
| 317    */ |  | 
| 318   _positionTab: function() |  | 
| 319   { |  | 
| 320     // Test whether element is still in document |  | 
| 321     let elementDoc = null; |  | 
| 322     try |  | 
| 323     { |  | 
| 324       elementDoc = this.currentElement.ownerDocument; |  | 
| 325     } catch (e) {}  // Ignore "can't access dead object" error |  | 
| 326     if (!elementDoc || !this.currentElement.offsetWidth || !this.currentElement.
     offsetHeight || |  | 
| 327         !elementDoc.defaultView || !elementDoc.documentElement) |  | 
| 328     { |  | 
| 329       this._hideTab(); |  | 
| 330       return; |  | 
| 331     } |  | 
| 332 |  | 
| 333     let objRect = this._getElementPosition(this.currentElement); |  | 
| 334 |  | 
| 335     let className = this.objTabClassVisibleTop; |  | 
| 336     let left = objRect.right - this.objtabElement.offsetWidth; |  | 
| 337     let top = objRect.top - this.objtabElement.offsetHeight; |  | 
| 338     if (top < 0) |  | 
| 339     { |  | 
| 340       top = objRect.bottom; |  | 
| 341       className = this.objTabClassVisibleBottom; |  | 
| 342     } |  | 
| 343 |  | 
| 344     if (this.objtabElement.style.left != left + "px") |  | 
| 345       this.objtabElement.style.setProperty("left", left + "px", "important"); |  | 
| 346     if (this.objtabElement.style.top != top + "px") |  | 
| 347       this.objtabElement.style.setProperty("top", top + "px", "important"); |  | 
| 348 |  | 
| 349     if (this.objtabElement.getAttribute("class") != className) |  | 
| 350       this.objtabElement.setAttribute("class", className); |  | 
| 351 |  | 
| 352     this.prevPositionUpdate = Date.now(); |  | 
| 353   }, |  | 
| 354 |  | 
| 355   /** |  | 
| 356    * Calculates element's position relative to the top frame and considering |  | 
| 357    * clipping due to scrolling. |  | 
| 358    * @return {{left: Number, top: Number, right: Number, bottom: Number}} |  | 
| 359    */ |  | 
| 360   _getElementPosition: function(/**Element*/ element) |  | 
| 361   { |  | 
| 362     // Restrict rectangle coordinates by the boundaries of a window's client are
     a |  | 
| 363     function intersectRect(rect, wnd) |  | 
| 364     { |  | 
| 365       // Cannot use wnd.innerWidth/Height because they won't account for scrollb
     ars |  | 
| 366       let doc = wnd.document; |  | 
| 367       let wndWidth = doc.documentElement.clientWidth; |  | 
| 368       let wndHeight = doc.documentElement.clientHeight; |  | 
| 369       if (doc.compatMode == "BackCompat") // clientHeight will be bogus in quirk
     s mode |  | 
| 370         wndHeight = Math.max(doc.documentElement.offsetHeight, doc.body.offsetHe
     ight) - wnd.scrollMaxY - 1; |  | 
| 371 |  | 
| 372       rect.left = Math.max(rect.left, 0); |  | 
| 373       rect.top = Math.max(rect.top, 0); |  | 
| 374       rect.right = Math.min(rect.right, wndWidth); |  | 
| 375       rect.bottom = Math.min(rect.bottom, wndHeight); |  | 
| 376     } |  | 
| 377 |  | 
| 378     let rect = element.getBoundingClientRect(); |  | 
| 379     let wnd = element.ownerDocument.defaultView; |  | 
| 380 |  | 
| 381     let style = wnd.getComputedStyle(element, null); |  | 
| 382     let offsets = [ |  | 
| 383       parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft), |  | 
| 384       parseFloat(style.borderTopWidth) + parseFloat(style.paddingTop), |  | 
| 385       parseFloat(style.borderRightWidth) + parseFloat(style.paddingRight), |  | 
| 386       parseFloat(style.borderBottomWidth) + parseFloat(style.paddingBottom) |  | 
| 387     ]; |  | 
| 388 |  | 
| 389     rect = {left: rect.left + offsets[0], top: rect.top + offsets[1], |  | 
| 390             right: rect.right - offsets[2], bottom: rect.bottom - offsets[3]}; |  | 
| 391     while (true) |  | 
| 392     { |  | 
| 393       intersectRect(rect, wnd); |  | 
| 394 |  | 
| 395       if (!wnd.frameElement) |  | 
| 396         break; |  | 
| 397 |  | 
| 398       // Recalculate coordinates to be relative to frame's parent window |  | 
| 399       let frameElement = wnd.frameElement; |  | 
| 400       wnd = frameElement.ownerDocument.defaultView; |  | 
| 401 |  | 
| 402       let frameRect = frameElement.getBoundingClientRect(); |  | 
| 403       let frameStyle = wnd.getComputedStyle(frameElement, null); |  | 
| 404       let relLeft = frameRect.left + parseFloat(frameStyle.borderLeftWidth) + pa
     rseFloat(frameStyle.paddingLeft); |  | 
| 405       let relTop = frameRect.top + parseFloat(frameStyle.borderTopWidth) + parse
     Float(frameStyle.paddingTop); |  | 
| 406 |  | 
| 407       rect.left += relLeft; |  | 
| 408       rect.right += relLeft; |  | 
| 409       rect.top += relTop; |  | 
| 410       rect.bottom += relTop; |  | 
| 411     } |  | 
| 412 |  | 
| 413     return rect; |  | 
| 414   }, |  | 
| 415 |  | 
| 416   doBlock: function() |  | 
| 417   { |  | 
| 418     let {UI} = require("ui"); |  | 
| 419     let {Utils} = require("utils"); |  | 
| 420     let chromeWindow = Utils.getChromeWindow(this.currentElement.ownerDocument.d
     efaultView); |  | 
| 421     UI.blockItem(chromeWindow, this.currentElement, this.objtabElement.nodeData)
     ; |  | 
| 422   }, |  | 
| 423 |  | 
| 424   /** |  | 
| 425    * Called whenever a timer fires. |  | 
| 426    * @param {nsISupport} subject |  | 
| 427    * @param {string} topic |  | 
| 428    * @param {string} data |  | 
| 429    */ |  | 
| 430   observe: function(subject, topic, data) |  | 
| 431   { |  | 
| 432     if (subject == this.positionTimer) |  | 
| 433     { |  | 
| 434       // Don't update position if it was already updated recently (via MozAfterP
     aint) |  | 
| 435       if (Date.now() - this.prevPositionUpdate > 100) |  | 
| 436         this._positionTab(); |  | 
| 437     } |  | 
| 438     else if (subject == this.hideTimer) |  | 
| 439     { |  | 
| 440       let now = Date.now(); |  | 
| 441       if (now >= this.hideTargetTime) |  | 
| 442         this._hideTab(); |  | 
| 443       else if (this.hideTargetTime - now < this.HIDE_DELAY / 2) |  | 
| 444         this.objtabElement.style.setProperty("opacity", (this.hideTargetTime - n
     ow) * 2 / this.HIDE_DELAY, "important"); |  | 
| 445     } |  | 
| 446   } | 110   } | 
| 447 }; |  | 
| 448 |  | 
| 449 onShutdown.add(objTabs._hideTab.bind(objTabs)); |  | 
| 450 |  | 
| 451 /** |  | 
| 452  * Function called whenever the mouse enters or leaves an object. |  | 
| 453  */ |  | 
| 454 function objectMouseEventHander(/**Event*/ event) |  | 
| 455 { |  | 
| 456   if (!event.isTrusted) |  | 
| 457     return; |  | 
| 458 |  | 
| 459   if (event.type == "mouseover") |  | 
| 460     objTabs.showTabFor(event.target); |  | 
| 461   else if (event.type == "mouseout") |  | 
| 462     objTabs.hideTabFor(event.target); |  | 
| 463 } | 111 } | 
| 464 | 112 init(); | 
| 465 /** |  | 
| 466  * Function called for paint events of the object tab window. |  | 
| 467  */ |  | 
| 468 function objectWindowEventHandler(/**Event*/ event) |  | 
| 469 { |  | 
| 470   if (!event.isTrusted) |  | 
| 471     return; |  | 
| 472 |  | 
| 473   // Don't trigger update too often, avoid overusing CPU on frequent page update
     s |  | 
| 474   if (event.type == "MozAfterPaint" && Date.now() - objTabs.prevPositionUpdate >
      20) |  | 
| 475     objTabs._positionTab(); |  | 
| 476 } |  | 
| 477 |  | 
| 478 /** |  | 
| 479  * Function called whenever the mouse enters or leaves an object tab. |  | 
| 480  */ |  | 
| 481 function objectTabEventHander(/**Event*/ event) |  | 
| 482 { |  | 
| 483   if (onShutdown.done || !event.isTrusted) |  | 
| 484     return; |  | 
| 485 |  | 
| 486   if (event.type == "click" && event.button == 0) |  | 
| 487   { |  | 
| 488     event.preventDefault(); |  | 
| 489     event.stopPropagation(); |  | 
| 490 |  | 
| 491     objTabs.doBlock(); |  | 
| 492   } |  | 
| 493   else if (event.type == "mouseover") |  | 
| 494     objTabs.showTabFor(objTabs.currentElement); |  | 
| 495   else if (event.type == "mouseout") |  | 
| 496     objTabs.hideTabFor(objTabs.currentElement); |  | 
| 497 } |  | 
| 498 exports.objectMouseEventHander = objectMouseEventHander; |  | 
| OLD | NEW | 
|---|