browser/components/downloads/content/indicator.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /**
michael@0 8 * Handles the indicator that displays the progress of ongoing downloads, which
michael@0 9 * is also used as the anchor for the downloads panel.
michael@0 10 *
michael@0 11 * This module includes the following constructors and global objects:
michael@0 12 *
michael@0 13 * DownloadsButton
michael@0 14 * Main entry point for the downloads indicator. Depending on how the toolbars
michael@0 15 * have been customized, this object determines if we should show a fully
michael@0 16 * functional indicator, a placeholder used during customization and in the
michael@0 17 * customization palette, or a neutral view as a temporary anchor for the
michael@0 18 * downloads panel.
michael@0 19 *
michael@0 20 * DownloadsIndicatorView
michael@0 21 * Builds and updates the actual downloads status widget, responding to changes
michael@0 22 * in the global status data, or provides a neutral view if the indicator is
michael@0 23 * removed from the toolbars and only used as a temporary anchor. In addition,
michael@0 24 * handles the user interaction events raised by the widget.
michael@0 25 */
michael@0 26
michael@0 27 "use strict";
michael@0 28
michael@0 29 ////////////////////////////////////////////////////////////////////////////////
michael@0 30 //// DownloadsButton
michael@0 31
michael@0 32 /**
michael@0 33 * Main entry point for the downloads indicator. Depending on how the toolbars
michael@0 34 * have been customized, this object determines if we should show a fully
michael@0 35 * functional indicator, a placeholder used during customization and in the
michael@0 36 * customization palette, or a neutral view as a temporary anchor for the
michael@0 37 * downloads panel.
michael@0 38 */
michael@0 39 const DownloadsButton = {
michael@0 40 /**
michael@0 41 * Location of the indicator overlay.
michael@0 42 */
michael@0 43 get kIndicatorOverlay()
michael@0 44 "chrome://browser/content/downloads/indicatorOverlay.xul",
michael@0 45
michael@0 46 /**
michael@0 47 * Returns a reference to the downloads button position placeholder, or null
michael@0 48 * if not available because it has been removed from the toolbars.
michael@0 49 */
michael@0 50 get _placeholder()
michael@0 51 {
michael@0 52 return document.getElementById("downloads-button");
michael@0 53 },
michael@0 54
michael@0 55 /**
michael@0 56 * This function is called asynchronously just after window initialization.
michael@0 57 *
michael@0 58 * NOTE: This function should limit the input/output it performs to improve
michael@0 59 * startup time.
michael@0 60 */
michael@0 61 initializeIndicator: function DB_initializeIndicator()
michael@0 62 {
michael@0 63 DownloadsIndicatorView.ensureInitialized();
michael@0 64 },
michael@0 65
michael@0 66 /**
michael@0 67 * Indicates whether toolbar customization is in progress.
michael@0 68 */
michael@0 69 _customizing: false,
michael@0 70
michael@0 71 /**
michael@0 72 * This function is called when toolbar customization starts.
michael@0 73 *
michael@0 74 * During customization, we never show the actual download progress indication
michael@0 75 * or the event notifications, but we show a neutral placeholder. The neutral
michael@0 76 * placeholder is an ordinary button defined in the browser window that can be
michael@0 77 * moved freely between the toolbars and the customization palette.
michael@0 78 */
michael@0 79 customizeStart: function DB_customizeStart()
michael@0 80 {
michael@0 81 // Prevent the indicator from being displayed as a temporary anchor
michael@0 82 // during customization, even if requested using the getAnchor method.
michael@0 83 this._customizing = true;
michael@0 84 this._anchorRequested = false;
michael@0 85 },
michael@0 86
michael@0 87 /**
michael@0 88 * This function is called when toolbar customization ends.
michael@0 89 */
michael@0 90 customizeDone: function DB_customizeDone()
michael@0 91 {
michael@0 92 this._customizing = false;
michael@0 93 DownloadsIndicatorView.afterCustomize();
michael@0 94 },
michael@0 95
michael@0 96 /**
michael@0 97 * Determines the position where the indicator should appear, and moves its
michael@0 98 * associated element to the new position.
michael@0 99 *
michael@0 100 * @return Anchor element, or null if the indicator is not visible.
michael@0 101 */
michael@0 102 _getAnchorInternal: function DB_getAnchorInternal()
michael@0 103 {
michael@0 104 let indicator = DownloadsIndicatorView.indicator;
michael@0 105 if (!indicator) {
michael@0 106 // Exit now if the indicator overlay isn't loaded yet, or if the button
michael@0 107 // is not in the document.
michael@0 108 return null;
michael@0 109 }
michael@0 110
michael@0 111 indicator.open = this._anchorRequested;
michael@0 112
michael@0 113 let widget = CustomizableUI.getWidget("downloads-button")
michael@0 114 .forWindow(window);
michael@0 115 // Determine if the indicator is located on an invisible toolbar.
michael@0 116 if (!isElementVisible(indicator.parentNode) && !widget.overflowed) {
michael@0 117 return null;
michael@0 118 }
michael@0 119
michael@0 120 return DownloadsIndicatorView.indicatorAnchor;
michael@0 121 },
michael@0 122
michael@0 123 /**
michael@0 124 * Checks whether the indicator is, or will soon be visible in the browser
michael@0 125 * window.
michael@0 126 *
michael@0 127 * @param aCallback
michael@0 128 * Called once the indicator overlay has loaded. Gets a boolean
michael@0 129 * argument representing the indicator visibility.
michael@0 130 */
michael@0 131 checkIsVisible: function DB_checkIsVisible(aCallback)
michael@0 132 {
michael@0 133 function DB_CEV_callback() {
michael@0 134 if (!this._placeholder) {
michael@0 135 aCallback(false);
michael@0 136 } else {
michael@0 137 let element = DownloadsIndicatorView.indicator || this._placeholder;
michael@0 138 aCallback(isElementVisible(element.parentNode));
michael@0 139 }
michael@0 140 }
michael@0 141 DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay,
michael@0 142 DB_CEV_callback.bind(this));
michael@0 143 },
michael@0 144
michael@0 145 /**
michael@0 146 * Indicates whether we should try and show the indicator temporarily as an
michael@0 147 * anchor for the panel, even if the indicator would be hidden by default.
michael@0 148 */
michael@0 149 _anchorRequested: false,
michael@0 150
michael@0 151 /**
michael@0 152 * Ensures that there is an anchor available for the panel.
michael@0 153 *
michael@0 154 * @param aCallback
michael@0 155 * Called when the anchor is available, passing the element where the
michael@0 156 * panel should be anchored, or null if an anchor is not available (for
michael@0 157 * example because both the tab bar and the navigation bar are hidden).
michael@0 158 */
michael@0 159 getAnchor: function DB_getAnchor(aCallback)
michael@0 160 {
michael@0 161 // Do not allow anchoring the panel to the element while customizing.
michael@0 162 if (this._customizing) {
michael@0 163 aCallback(null);
michael@0 164 return;
michael@0 165 }
michael@0 166
michael@0 167 function DB_GA_callback() {
michael@0 168 this._anchorRequested = true;
michael@0 169 aCallback(this._getAnchorInternal());
michael@0 170 }
michael@0 171
michael@0 172 DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay,
michael@0 173 DB_GA_callback.bind(this));
michael@0 174 },
michael@0 175
michael@0 176 /**
michael@0 177 * Allows the temporary anchor to be hidden.
michael@0 178 */
michael@0 179 releaseAnchor: function DB_releaseAnchor()
michael@0 180 {
michael@0 181 this._anchorRequested = false;
michael@0 182 this._getAnchorInternal();
michael@0 183 },
michael@0 184
michael@0 185 get _tabsToolbar()
michael@0 186 {
michael@0 187 delete this._tabsToolbar;
michael@0 188 return this._tabsToolbar = document.getElementById("TabsToolbar");
michael@0 189 },
michael@0 190
michael@0 191 get _navBar()
michael@0 192 {
michael@0 193 delete this._navBar;
michael@0 194 return this._navBar = document.getElementById("nav-bar");
michael@0 195 }
michael@0 196 };
michael@0 197
michael@0 198 ////////////////////////////////////////////////////////////////////////////////
michael@0 199 //// DownloadsIndicatorView
michael@0 200
michael@0 201 /**
michael@0 202 * Builds and updates the actual downloads status widget, responding to changes
michael@0 203 * in the global status data, or provides a neutral view if the indicator is
michael@0 204 * removed from the toolbars and only used as a temporary anchor. In addition,
michael@0 205 * handles the user interaction events raised by the widget.
michael@0 206 */
michael@0 207 const DownloadsIndicatorView = {
michael@0 208 /**
michael@0 209 * True when the view is connected with the underlying downloads data.
michael@0 210 */
michael@0 211 _initialized: false,
michael@0 212
michael@0 213 /**
michael@0 214 * True when the user interface elements required to display the indicator
michael@0 215 * have finished loading in the browser window, and can be referenced.
michael@0 216 */
michael@0 217 _operational: false,
michael@0 218
michael@0 219 /**
michael@0 220 * Prepares the downloads indicator to be displayed.
michael@0 221 */
michael@0 222 ensureInitialized: function DIV_ensureInitialized()
michael@0 223 {
michael@0 224 if (this._initialized) {
michael@0 225 return;
michael@0 226 }
michael@0 227 this._initialized = true;
michael@0 228
michael@0 229 window.addEventListener("unload", this.onWindowUnload, false);
michael@0 230 DownloadsCommon.getIndicatorData(window).addView(this);
michael@0 231 },
michael@0 232
michael@0 233 /**
michael@0 234 * Frees the internal resources related to the indicator.
michael@0 235 */
michael@0 236 ensureTerminated: function DIV_ensureTerminated()
michael@0 237 {
michael@0 238 if (!this._initialized) {
michael@0 239 return;
michael@0 240 }
michael@0 241 this._initialized = false;
michael@0 242
michael@0 243 window.removeEventListener("unload", this.onWindowUnload, false);
michael@0 244 DownloadsCommon.getIndicatorData(window).removeView(this);
michael@0 245
michael@0 246 // Reset the view properties, so that a neutral indicator is displayed if we
michael@0 247 // are visible only temporarily as an anchor.
michael@0 248 this.counter = "";
michael@0 249 this.percentComplete = 0;
michael@0 250 this.paused = false;
michael@0 251 this.attention = false;
michael@0 252 },
michael@0 253
michael@0 254 /**
michael@0 255 * Ensures that the user interface elements required to display the indicator
michael@0 256 * are loaded, then invokes the given callback.
michael@0 257 */
michael@0 258 _ensureOperational: function DIV_ensureOperational(aCallback)
michael@0 259 {
michael@0 260 if (this._operational) {
michael@0 261 if (aCallback) {
michael@0 262 aCallback();
michael@0 263 }
michael@0 264 return;
michael@0 265 }
michael@0 266
michael@0 267 // If we don't have a _placeholder, there's no chance that the overlay
michael@0 268 // will load correctly: bail (and don't set _operational to true!)
michael@0 269 if (!DownloadsButton._placeholder) {
michael@0 270 return;
michael@0 271 }
michael@0 272
michael@0 273 function DIV_EO_callback() {
michael@0 274 this._operational = true;
michael@0 275
michael@0 276 // If the view is initialized, we need to update the elements now that
michael@0 277 // they are finally available in the document.
michael@0 278 if (this._initialized) {
michael@0 279 DownloadsCommon.getIndicatorData(window).refreshView(this);
michael@0 280 }
michael@0 281
michael@0 282 if (aCallback) {
michael@0 283 aCallback();
michael@0 284 }
michael@0 285 }
michael@0 286
michael@0 287 DownloadsOverlayLoader.ensureOverlayLoaded(
michael@0 288 DownloadsButton.kIndicatorOverlay,
michael@0 289 DIV_EO_callback.bind(this));
michael@0 290 },
michael@0 291
michael@0 292 //////////////////////////////////////////////////////////////////////////////
michael@0 293 //// Direct control functions
michael@0 294
michael@0 295 /**
michael@0 296 * Set while we are waiting for a notification to fade out.
michael@0 297 */
michael@0 298 _notificationTimeout: null,
michael@0 299
michael@0 300 /**
michael@0 301 * Check if the panel containing aNode is open.
michael@0 302 * @param aNode
michael@0 303 * the node whose panel we're interested in.
michael@0 304 */
michael@0 305 _isAncestorPanelOpen: function DIV_isAncestorPanelOpen(aNode)
michael@0 306 {
michael@0 307 while (aNode && aNode.localName != "panel") {
michael@0 308 aNode = aNode.parentNode;
michael@0 309 }
michael@0 310 return aNode && aNode.state == "open";
michael@0 311 },
michael@0 312
michael@0 313 /**
michael@0 314 * If the status indicator is visible in its assigned position, shows for a
michael@0 315 * brief time a visual notification of a relevant event, like a new download.
michael@0 316 *
michael@0 317 * @param aType
michael@0 318 * Set to "start" for new downloads, "finish" for completed downloads.
michael@0 319 */
michael@0 320 showEventNotification: function DIV_showEventNotification(aType)
michael@0 321 {
michael@0 322 if (!this._initialized) {
michael@0 323 return;
michael@0 324 }
michael@0 325
michael@0 326 if (!DownloadsCommon.animateNotifications) {
michael@0 327 return;
michael@0 328 }
michael@0 329
michael@0 330 // No need to show visual notification if the panel is visible.
michael@0 331 if (DownloadsPanel.isPanelShowing) {
michael@0 332 return;
michael@0 333 }
michael@0 334
michael@0 335 let anchor = DownloadsButton._placeholder;
michael@0 336 let widgetGroup = CustomizableUI.getWidget("downloads-button");
michael@0 337 let widget = widgetGroup.forWindow(window);
michael@0 338 if (widget.overflowed || widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
michael@0 339 if (anchor && this._isAncestorPanelOpen(anchor)) {
michael@0 340 // If the containing panel is open, don't do anything, because the
michael@0 341 // notification would appear under the open panel. See
michael@0 342 // https://bugzilla.mozilla.org/show_bug.cgi?id=984023
michael@0 343 return;
michael@0 344 }
michael@0 345
michael@0 346 // Otherwise, try to use the anchor of the panel:
michael@0 347 anchor = widget.anchor;
michael@0 348 }
michael@0 349 if (!anchor || !isElementVisible(anchor.parentNode)) {
michael@0 350 // Our container isn't visible, so can't show the animation:
michael@0 351 return;
michael@0 352 }
michael@0 353
michael@0 354 if (this._notificationTimeout) {
michael@0 355 clearTimeout(this._notificationTimeout);
michael@0 356 }
michael@0 357
michael@0 358 // The notification element is positioned to show in the same location as
michael@0 359 // the downloads button. It's not in the downloads button itself in order to
michael@0 360 // be able to anchor the notification elsewhere if required, and to ensure
michael@0 361 // the notification isn't clipped by overflow properties of the anchor's
michael@0 362 // container.
michael@0 363 let notifier = this.notifier;
michael@0 364 if (notifier.style.transform == '') {
michael@0 365 let anchorRect = anchor.getBoundingClientRect();
michael@0 366 let notifierRect = notifier.getBoundingClientRect();
michael@0 367 let topDiff = anchorRect.top - notifierRect.top;
michael@0 368 let leftDiff = anchorRect.left - notifierRect.left;
michael@0 369 let heightDiff = anchorRect.height - notifierRect.height;
michael@0 370 let widthDiff = anchorRect.width - notifierRect.width;
michael@0 371 let translateX = (leftDiff + .5 * widthDiff) + "px";
michael@0 372 let translateY = (topDiff + .5 * heightDiff) + "px";
michael@0 373 notifier.style.transform = "translate(" + translateX + ", " + translateY + ")";
michael@0 374 }
michael@0 375 notifier.setAttribute("notification", aType);
michael@0 376 this._notificationTimeout = setTimeout(function () {
michael@0 377 notifier.removeAttribute("notification");
michael@0 378 notifier.style.transform = '';
michael@0 379 }, 1000);
michael@0 380 },
michael@0 381
michael@0 382 //////////////////////////////////////////////////////////////////////////////
michael@0 383 //// Callback functions from DownloadsIndicatorData
michael@0 384
michael@0 385 /**
michael@0 386 * Indicates whether the indicator should be shown because there are some
michael@0 387 * downloads to be displayed.
michael@0 388 */
michael@0 389 set hasDownloads(aValue)
michael@0 390 {
michael@0 391 if (this._hasDownloads != aValue || (!this._operational && aValue)) {
michael@0 392 this._hasDownloads = aValue;
michael@0 393
michael@0 394 // If there is at least one download, ensure that the view elements are
michael@0 395 if (aValue) {
michael@0 396 this._ensureOperational();
michael@0 397 }
michael@0 398 }
michael@0 399 return aValue;
michael@0 400 },
michael@0 401 get hasDownloads()
michael@0 402 {
michael@0 403 return this._hasDownloads;
michael@0 404 },
michael@0 405 _hasDownloads: false,
michael@0 406
michael@0 407 /**
michael@0 408 * Status text displayed in the indicator. If this is set to an empty value,
michael@0 409 * then the small downloads icon is displayed instead of the text.
michael@0 410 */
michael@0 411 set counter(aValue)
michael@0 412 {
michael@0 413 if (!this._operational) {
michael@0 414 return this._counter;
michael@0 415 }
michael@0 416
michael@0 417 if (this._counter !== aValue) {
michael@0 418 this._counter = aValue;
michael@0 419 if (this._counter)
michael@0 420 this.indicator.setAttribute("counter", "true");
michael@0 421 else
michael@0 422 this.indicator.removeAttribute("counter");
michael@0 423 // We have to set the attribute instead of using the property because the
michael@0 424 // XBL binding isn't applied if the element is invisible for any reason.
michael@0 425 this._indicatorCounter.setAttribute("value", aValue);
michael@0 426 }
michael@0 427 return aValue;
michael@0 428 },
michael@0 429 _counter: null,
michael@0 430
michael@0 431 /**
michael@0 432 * Progress indication to display, from 0 to 100, or -1 if unknown. The
michael@0 433 * progress bar is hidden if the current progress is unknown and no status
michael@0 434 * text is set in the "counter" property.
michael@0 435 */
michael@0 436 set percentComplete(aValue)
michael@0 437 {
michael@0 438 if (!this._operational) {
michael@0 439 return this._percentComplete;
michael@0 440 }
michael@0 441
michael@0 442 if (this._percentComplete !== aValue) {
michael@0 443 this._percentComplete = aValue;
michael@0 444 if (this._percentComplete >= 0)
michael@0 445 this.indicator.setAttribute("progress", "true");
michael@0 446 else
michael@0 447 this.indicator.removeAttribute("progress");
michael@0 448 // We have to set the attribute instead of using the property because the
michael@0 449 // XBL binding isn't applied if the element is invisible for any reason.
michael@0 450 this._indicatorProgress.setAttribute("value", Math.max(aValue, 0));
michael@0 451 }
michael@0 452 return aValue;
michael@0 453 },
michael@0 454 _percentComplete: null,
michael@0 455
michael@0 456 /**
michael@0 457 * Indicates whether the progress won't advance because of a paused state.
michael@0 458 * Setting this property forces a paused progress bar to be displayed, even if
michael@0 459 * the current progress information is unavailable.
michael@0 460 */
michael@0 461 set paused(aValue)
michael@0 462 {
michael@0 463 if (!this._operational) {
michael@0 464 return this._paused;
michael@0 465 }
michael@0 466
michael@0 467 if (this._paused != aValue) {
michael@0 468 this._paused = aValue;
michael@0 469 if (this._paused) {
michael@0 470 this.indicator.setAttribute("paused", "true")
michael@0 471 } else {
michael@0 472 this.indicator.removeAttribute("paused");
michael@0 473 }
michael@0 474 }
michael@0 475 return aValue;
michael@0 476 },
michael@0 477 _paused: false,
michael@0 478
michael@0 479 /**
michael@0 480 * Set when the indicator should draw user attention to itself.
michael@0 481 */
michael@0 482 set attention(aValue)
michael@0 483 {
michael@0 484 if (!this._operational) {
michael@0 485 return this._attention;
michael@0 486 }
michael@0 487
michael@0 488 if (this._attention != aValue) {
michael@0 489 this._attention = aValue;
michael@0 490 if (aValue) {
michael@0 491 this.indicator.setAttribute("attention", "true");
michael@0 492 } else {
michael@0 493 this.indicator.removeAttribute("attention");
michael@0 494 }
michael@0 495 }
michael@0 496 return aValue;
michael@0 497 },
michael@0 498 _attention: false,
michael@0 499
michael@0 500 //////////////////////////////////////////////////////////////////////////////
michael@0 501 //// User interface event functions
michael@0 502
michael@0 503 onWindowUnload: function DIV_onWindowUnload()
michael@0 504 {
michael@0 505 // This function is registered as an event listener, we can't use "this".
michael@0 506 DownloadsIndicatorView.ensureTerminated();
michael@0 507 },
michael@0 508
michael@0 509 onCommand: function DIV_onCommand(aEvent)
michael@0 510 {
michael@0 511 // If the downloads button is in the menu panel, open the Library
michael@0 512 let widgetGroup = CustomizableUI.getWidget("downloads-button");
michael@0 513 if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
michael@0 514 DownloadsPanel.showDownloadsHistory();
michael@0 515 } else {
michael@0 516 DownloadsPanel.showPanel();
michael@0 517 }
michael@0 518
michael@0 519 aEvent.stopPropagation();
michael@0 520 },
michael@0 521
michael@0 522 onDragOver: function DIV_onDragOver(aEvent)
michael@0 523 {
michael@0 524 browserDragAndDrop.dragOver(aEvent);
michael@0 525 },
michael@0 526
michael@0 527 onDrop: function DIV_onDrop(aEvent)
michael@0 528 {
michael@0 529 let dt = aEvent.dataTransfer;
michael@0 530 // If dragged item is from our source, do not try to
michael@0 531 // redownload already downloaded file.
michael@0 532 if (dt.mozGetDataAt("application/x-moz-file", 0))
michael@0 533 return;
michael@0 534
michael@0 535 let name = {};
michael@0 536 let url = browserDragAndDrop.drop(aEvent, name);
michael@0 537 if (url) {
michael@0 538 if (url.startsWith("about:")) {
michael@0 539 return;
michael@0 540 }
michael@0 541
michael@0 542 let sourceDoc = dt.mozSourceNode ? dt.mozSourceNode.ownerDocument : document;
michael@0 543 saveURL(url, name.value, null, true, true, null, sourceDoc);
michael@0 544 aEvent.preventDefault();
michael@0 545 }
michael@0 546 },
michael@0 547
michael@0 548 _indicator: null,
michael@0 549 __indicatorCounter: null,
michael@0 550 __indicatorProgress: null,
michael@0 551
michael@0 552 /**
michael@0 553 * Returns a reference to the main indicator element, or null if the element
michael@0 554 * is not present in the browser window yet.
michael@0 555 */
michael@0 556 get indicator()
michael@0 557 {
michael@0 558 if (this._indicator) {
michael@0 559 return this._indicator;
michael@0 560 }
michael@0 561
michael@0 562 let indicator = document.getElementById("downloads-button");
michael@0 563 if (!indicator || indicator.getAttribute("indicator") != "true") {
michael@0 564 return null;
michael@0 565 }
michael@0 566
michael@0 567 return this._indicator = indicator;
michael@0 568 },
michael@0 569
michael@0 570 get indicatorAnchor()
michael@0 571 {
michael@0 572 let widget = CustomizableUI.getWidget("downloads-button")
michael@0 573 .forWindow(window);
michael@0 574 if (widget.overflowed) {
michael@0 575 return widget.anchor;
michael@0 576 }
michael@0 577 return document.getElementById("downloads-indicator-anchor");
michael@0 578 },
michael@0 579
michael@0 580 get _indicatorCounter()
michael@0 581 {
michael@0 582 return this.__indicatorCounter ||
michael@0 583 (this.__indicatorCounter = document.getElementById("downloads-indicator-counter"));
michael@0 584 },
michael@0 585
michael@0 586 get _indicatorProgress()
michael@0 587 {
michael@0 588 return this.__indicatorProgress ||
michael@0 589 (this.__indicatorProgress = document.getElementById("downloads-indicator-progress"));
michael@0 590 },
michael@0 591
michael@0 592 get notifier()
michael@0 593 {
michael@0 594 return this._notifier ||
michael@0 595 (this._notifier = document.getElementById("downloads-notification-anchor"));
michael@0 596 },
michael@0 597
michael@0 598 _onCustomizedAway: function() {
michael@0 599 this._indicator = null;
michael@0 600 this.__indicatorCounter = null;
michael@0 601 this.__indicatorProgress = null;
michael@0 602 },
michael@0 603
michael@0 604 afterCustomize: function() {
michael@0 605 // If the cached indicator is not the one currently in the document,
michael@0 606 // invalidate our references
michael@0 607 if (this._indicator != document.getElementById("downloads-button")) {
michael@0 608 this._onCustomizedAway();
michael@0 609 this._operational = false;
michael@0 610 this.ensureTerminated();
michael@0 611 this.ensureInitialized();
michael@0 612 }
michael@0 613 }
michael@0 614 };
michael@0 615

mercurial