forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1065 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1065 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 | 
						|
/* 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/. */
 | 
						|
 | 
						|
/* This content script contains code that requires a tab browser. */
 | 
						|
 | 
						|
/* eslint-env mozilla/frame-script */
 | 
						|
 | 
						|
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 | 
						|
 | 
						|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | 
						|
Cu.import("resource://gre/modules/Services.jsm");
 | 
						|
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
 | 
						|
  "resource:///modules/E10SUtils.jsm");
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
 | 
						|
  "resource://gre/modules/BrowserUtils.jsm");
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "Utils",
 | 
						|
  "resource://gre/modules/sessionstore/Utils.jsm");
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
 | 
						|
  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "AboutReader",
 | 
						|
  "resource://gre/modules/AboutReader.jsm");
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
 | 
						|
  "resource://gre/modules/ReaderMode.jsm");
 | 
						|
XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() {
 | 
						|
  let ssdp = Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery;
 | 
						|
  // Register targets
 | 
						|
  ssdp.registerDevice({
 | 
						|
    id: "roku:ecp",
 | 
						|
    target: "roku:ecp",
 | 
						|
    factory(aService) {
 | 
						|
      Cu.import("resource://gre/modules/RokuApp.jsm");
 | 
						|
      return new RokuApp(aService);
 | 
						|
    },
 | 
						|
    types: ["video/mp4"],
 | 
						|
    extensions: ["mp4"]
 | 
						|
  });
 | 
						|
  return ssdp;
 | 
						|
});
 | 
						|
 | 
						|
// TabChildGlobal
 | 
						|
var global = this;
 | 
						|
 | 
						|
 | 
						|
addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
 | 
						|
  sendAsyncMessage("PointerLock:Entered", {
 | 
						|
    originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
 | 
						|
  sendAsyncMessage("PointerLock:Exited");
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
addMessageListener("Browser:HideSessionRestoreButton", function(message) {
 | 
						|
  // Hide session restore button on about:home
 | 
						|
  let doc = content.document;
 | 
						|
  let container;
 | 
						|
  if (doc.documentURI.toLowerCase() == "about:home" &&
 | 
						|
      (container = doc.getElementById("sessionRestoreContainer"))) {
 | 
						|
    container.hidden = true;
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
addMessageListener("Browser:Reload", function(message) {
 | 
						|
  /* First, we'll try to use the session history object to reload so
 | 
						|
   * that framesets are handled properly. If we're in a special
 | 
						|
   * window (such as view-source) that has no session history, fall
 | 
						|
   * back on using the web navigation's reload method.
 | 
						|
   */
 | 
						|
 | 
						|
  let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
 | 
						|
  try {
 | 
						|
    let sh = webNav.sessionHistory;
 | 
						|
    if (sh)
 | 
						|
      webNav = sh.QueryInterface(Ci.nsIWebNavigation);
 | 
						|
  } catch (e) {
 | 
						|
  }
 | 
						|
 | 
						|
  let reloadFlags = message.data.flags;
 | 
						|
  try {
 | 
						|
    E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
 | 
						|
                                    () => webNav.reload(reloadFlags));
 | 
						|
  } catch (e) {
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
addMessageListener("MixedContent:ReenableProtection", function() {
 | 
						|
  docShell.mixedContentChannel = null;
 | 
						|
});
 | 
						|
 | 
						|
addMessageListener("SecondScreen:tab-mirror", function(message) {
 | 
						|
  if (!Services.prefs.getBoolPref("browser.casting.enabled")) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  let app = SimpleServiceDiscovery.findAppForService(message.data.service);
 | 
						|
  if (app) {
 | 
						|
    let width = content.innerWidth;
 | 
						|
    let height = content.innerHeight;
 | 
						|
    let viewport = {cssWidth: width, cssHeight: height, width, height};
 | 
						|
    app.mirror(function() {}, content, viewport, function() {}, content);
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
var AboutHomeListener = {
 | 
						|
  init(chromeGlobal) {
 | 
						|
    chromeGlobal.addEventListener("AboutHomeLoad", this, false, true);
 | 
						|
  },
 | 
						|
 | 
						|
  get isAboutHome() {
 | 
						|
    return content.document.documentURI.toLowerCase() == "about:home";
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(aEvent) {
 | 
						|
    if (!this.isAboutHome) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    switch (aEvent.type) {
 | 
						|
      case "AboutHomeLoad":
 | 
						|
        this.onPageLoad();
 | 
						|
        break;
 | 
						|
      case "click":
 | 
						|
        this.onClick(aEvent);
 | 
						|
        break;
 | 
						|
      case "pagehide":
 | 
						|
        this.onPageHide(aEvent);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(aMessage) {
 | 
						|
    if (!this.isAboutHome) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    switch (aMessage.name) {
 | 
						|
      case "AboutHome:Update":
 | 
						|
        this.onUpdate(aMessage.data);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  onUpdate(aData) {
 | 
						|
    let doc = content.document;
 | 
						|
    if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isContentWindowPrivate(content))
 | 
						|
      doc.getElementById("launcher").setAttribute("session", "true");
 | 
						|
 | 
						|
    // Inject search engine and snippets URL.
 | 
						|
    let docElt = doc.documentElement;
 | 
						|
    // Set snippetsVersion last, which triggers to show the snippets when it's set.
 | 
						|
    docElt.setAttribute("snippetsURL", aData.snippetsURL);
 | 
						|
    if (aData.showKnowYourRights)
 | 
						|
      docElt.setAttribute("showKnowYourRights", "true");
 | 
						|
    docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
 | 
						|
  },
 | 
						|
 | 
						|
  onPageLoad() {
 | 
						|
    addMessageListener("AboutHome:Update", this);
 | 
						|
    addEventListener("click", this, true);
 | 
						|
    addEventListener("pagehide", this, true);
 | 
						|
 | 
						|
    sendAsyncMessage("AboutHome:MaybeShowMigrateMessage");
 | 
						|
    sendAsyncMessage("AboutHome:RequestUpdate");
 | 
						|
  },
 | 
						|
 | 
						|
  onClick(aEvent) {
 | 
						|
    if (!aEvent.isTrusted || // Don't trust synthetic events
 | 
						|
        aEvent.button == 2 || aEvent.target.localName != "button") {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let originalTarget = aEvent.originalTarget;
 | 
						|
    let ownerDoc = originalTarget.ownerDocument;
 | 
						|
    if (ownerDoc.documentURI != "about:home") {
 | 
						|
      // This shouldn't happen, but we're being defensive.
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let elmId = originalTarget.getAttribute("id");
 | 
						|
 | 
						|
    switch (elmId) {
 | 
						|
      case "restorePreviousSession":
 | 
						|
        sendAsyncMessage("AboutHome:RestorePreviousSession");
 | 
						|
        ownerDoc.getElementById("launcher").removeAttribute("session");
 | 
						|
        break;
 | 
						|
 | 
						|
      case "downloads":
 | 
						|
        sendAsyncMessage("AboutHome:Downloads");
 | 
						|
        break;
 | 
						|
 | 
						|
      case "bookmarks":
 | 
						|
        sendAsyncMessage("AboutHome:Bookmarks");
 | 
						|
        break;
 | 
						|
 | 
						|
      case "history":
 | 
						|
        sendAsyncMessage("AboutHome:History");
 | 
						|
        break;
 | 
						|
 | 
						|
      case "addons":
 | 
						|
        sendAsyncMessage("AboutHome:Addons");
 | 
						|
        break;
 | 
						|
 | 
						|
      case "sync":
 | 
						|
        sendAsyncMessage("AboutHome:Sync");
 | 
						|
        break;
 | 
						|
 | 
						|
      case "settings":
 | 
						|
        sendAsyncMessage("AboutHome:Settings");
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  onPageHide(aEvent) {
 | 
						|
    if (aEvent.target.defaultView.frameElement) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    removeMessageListener("AboutHome:Update", this);
 | 
						|
    removeEventListener("click", this, true);
 | 
						|
    removeEventListener("pagehide", this, true);
 | 
						|
  },
 | 
						|
};
 | 
						|
AboutHomeListener.init(this);
 | 
						|
 | 
						|
var AboutPrivateBrowsingListener = {
 | 
						|
  init(chromeGlobal) {
 | 
						|
    chromeGlobal.addEventListener("AboutPrivateBrowsingOpenWindow", this,
 | 
						|
                                  false, true);
 | 
						|
    chromeGlobal.addEventListener("AboutPrivateBrowsingToggleTrackingProtection", this,
 | 
						|
                                  false, true);
 | 
						|
    chromeGlobal.addEventListener("AboutPrivateBrowsingDontShowIntroPanelAgain", this,
 | 
						|
                                  false, true);
 | 
						|
  },
 | 
						|
 | 
						|
  get isAboutPrivateBrowsing() {
 | 
						|
    return content.document.documentURI.toLowerCase() == "about:privatebrowsing";
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(aEvent) {
 | 
						|
    if (!this.isAboutPrivateBrowsing) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    switch (aEvent.type) {
 | 
						|
      case "AboutPrivateBrowsingOpenWindow":
 | 
						|
        sendAsyncMessage("AboutPrivateBrowsing:OpenPrivateWindow");
 | 
						|
        break;
 | 
						|
      case "AboutPrivateBrowsingToggleTrackingProtection":
 | 
						|
        sendAsyncMessage("AboutPrivateBrowsing:ToggleTrackingProtection");
 | 
						|
        break;
 | 
						|
      case "AboutPrivateBrowsingDontShowIntroPanelAgain":
 | 
						|
        sendAsyncMessage("AboutPrivateBrowsing:DontShowIntroPanelAgain");
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 | 
						|
AboutPrivateBrowsingListener.init(this);
 | 
						|
 | 
						|
var AboutReaderListener = {
 | 
						|
 | 
						|
  _articlePromise: null,
 | 
						|
 | 
						|
  _isLeavingReaderableReaderMode: false,
 | 
						|
 | 
						|
  init() {
 | 
						|
    addEventListener("AboutReaderContentLoaded", this, false, true);
 | 
						|
    addEventListener("DOMContentLoaded", this, false);
 | 
						|
    addEventListener("pageshow", this, false);
 | 
						|
    addEventListener("pagehide", this, false);
 | 
						|
    addMessageListener("Reader:ToggleReaderMode", this);
 | 
						|
    addMessageListener("Reader:PushState", this);
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(message) {
 | 
						|
    switch (message.name) {
 | 
						|
      case "Reader:ToggleReaderMode":
 | 
						|
        if (!this.isAboutReader) {
 | 
						|
          this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
 | 
						|
          ReaderMode.enterReaderMode(docShell, content);
 | 
						|
        } else {
 | 
						|
          this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
 | 
						|
          ReaderMode.leaveReaderMode(docShell, content);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case "Reader:PushState":
 | 
						|
        this.updateReaderButton(!!(message.data && message.data.isArticle));
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  get isAboutReader() {
 | 
						|
    if (!content) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    return content.document.documentURI.startsWith("about:reader");
 | 
						|
  },
 | 
						|
 | 
						|
  get isReaderableAboutReader() {
 | 
						|
    return this.isAboutReader &&
 | 
						|
      !content.document.documentElement.dataset.isError;
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(aEvent) {
 | 
						|
    if (aEvent.originalTarget.defaultView != content) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    switch (aEvent.type) {
 | 
						|
      case "AboutReaderContentLoaded":
 | 
						|
        if (!this.isAboutReader) {
 | 
						|
          return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (content.document.body) {
 | 
						|
          // Update the toolbar icon to show the "reader active" icon.
 | 
						|
          sendAsyncMessage("Reader:UpdateReaderButton");
 | 
						|
          new AboutReader(global, content, this._articlePromise);
 | 
						|
          this._articlePromise = null;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case "pagehide":
 | 
						|
        this.cancelPotentialPendingReadabilityCheck();
 | 
						|
        // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
 | 
						|
        // visible in the location bar when transitioning from reader-mode page
 | 
						|
        // back to the readable source page.
 | 
						|
        sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
 | 
						|
        if (this._isLeavingReaderableReaderMode) {
 | 
						|
          this._isLeavingReaderableReaderMode = false;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case "pageshow":
 | 
						|
        // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
 | 
						|
        // event, so we need to rely on "pageshow" in this case.
 | 
						|
        if (aEvent.persisted) {
 | 
						|
          this.updateReaderButton();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case "DOMContentLoaded":
 | 
						|
        this.updateReaderButton();
 | 
						|
        break;
 | 
						|
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * NB: this function will update the state of the reader button asynchronously
 | 
						|
   * after the next mozAfterPaint call (assuming reader mode is enabled and
 | 
						|
   * this is a suitable document). Calling it on things which won't be
 | 
						|
   * painted is not going to work.
 | 
						|
   */
 | 
						|
  updateReaderButton(forceNonArticle) {
 | 
						|
    if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
 | 
						|
        !content || !(content.document instanceof content.HTMLDocument) ||
 | 
						|
        content.document.mozSyntheticDocument) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this.scheduleReadabilityCheckPostPaint(forceNonArticle);
 | 
						|
  },
 | 
						|
 | 
						|
  cancelPotentialPendingReadabilityCheck() {
 | 
						|
    if (this._pendingReadabilityCheck) {
 | 
						|
      removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
 | 
						|
      delete this._pendingReadabilityCheck;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  scheduleReadabilityCheckPostPaint(forceNonArticle) {
 | 
						|
    if (this._pendingReadabilityCheck) {
 | 
						|
      // We need to stop this check before we re-add one because we don't know
 | 
						|
      // if forceNonArticle was true or false last time.
 | 
						|
      this.cancelPotentialPendingReadabilityCheck();
 | 
						|
    }
 | 
						|
    this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
 | 
						|
    addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
 | 
						|
  },
 | 
						|
 | 
						|
  onPaintWhenWaitedFor(forceNonArticle, event) {
 | 
						|
    // In non-e10s, we'll get called for paints other than ours, and so it's
 | 
						|
    // possible that this page hasn't been laid out yet, in which case we
 | 
						|
    // should wait until we get an event that does relate to our layout. We
 | 
						|
    // determine whether any of our content got painted by checking if there
 | 
						|
    // are any painted rects.
 | 
						|
    if (!event.clientRects.length) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this.cancelPotentialPendingReadabilityCheck();
 | 
						|
    // Only send updates when there are articles; there's no point updating with
 | 
						|
    // |false| all the time.
 | 
						|
    if (ReaderMode.isProbablyReaderable(content.document)) {
 | 
						|
      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
 | 
						|
    } else if (forceNonArticle) {
 | 
						|
      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
 | 
						|
    }
 | 
						|
  },
 | 
						|
};
 | 
						|
AboutReaderListener.init();
 | 
						|
 | 
						|
 | 
						|
var ContentSearchMediator = {
 | 
						|
 | 
						|
  whitelist: new Set([
 | 
						|
    "about:home",
 | 
						|
    "about:newtab",
 | 
						|
  ]),
 | 
						|
 | 
						|
  init(chromeGlobal) {
 | 
						|
    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
 | 
						|
    addMessageListener("ContentSearch", this);
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(event) {
 | 
						|
    if (this._contentWhitelisted) {
 | 
						|
      this._sendMsg(event.detail.type, event.detail.data);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(msg) {
 | 
						|
    if (msg.data.type == "AddToWhitelist") {
 | 
						|
      for (let uri of msg.data.data) {
 | 
						|
        this.whitelist.add(uri);
 | 
						|
      }
 | 
						|
      this._sendMsg("AddToWhitelistAck");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (this._contentWhitelisted) {
 | 
						|
      this._fireEvent(msg.data.type, msg.data.data);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  get _contentWhitelisted() {
 | 
						|
    return this.whitelist.has(content.document.documentURI);
 | 
						|
  },
 | 
						|
 | 
						|
  _sendMsg(type, data = null) {
 | 
						|
    sendAsyncMessage("ContentSearch", {
 | 
						|
      type,
 | 
						|
      data,
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  _fireEvent(type, data = null) {
 | 
						|
    let event = Cu.cloneInto({
 | 
						|
      detail: {
 | 
						|
        type,
 | 
						|
        data,
 | 
						|
      },
 | 
						|
    }, content);
 | 
						|
    content.dispatchEvent(new content.CustomEvent("ContentSearchService",
 | 
						|
                                                  event));
 | 
						|
  },
 | 
						|
};
 | 
						|
ContentSearchMediator.init(this);
 | 
						|
 | 
						|
var PageStyleHandler = {
 | 
						|
  init() {
 | 
						|
    addMessageListener("PageStyle:Switch", this);
 | 
						|
    addMessageListener("PageStyle:Disable", this);
 | 
						|
    addEventListener("pageshow", () => this.sendStyleSheetInfo());
 | 
						|
  },
 | 
						|
 | 
						|
  get markupDocumentViewer() {
 | 
						|
    return docShell.contentViewer;
 | 
						|
  },
 | 
						|
 | 
						|
  sendStyleSheetInfo() {
 | 
						|
    let filteredStyleSheets = this._filterStyleSheets(this.getAllStyleSheets());
 | 
						|
 | 
						|
    sendAsyncMessage("PageStyle:StyleSheets", {
 | 
						|
      filteredStyleSheets,
 | 
						|
      authorStyleDisabled: this.markupDocumentViewer.authorStyleDisabled,
 | 
						|
      preferredStyleSheetSet: content.document.preferredStyleSheetSet
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  getAllStyleSheets(frameset = content) {
 | 
						|
    let selfSheets = Array.slice(frameset.document.styleSheets);
 | 
						|
    let subSheets = Array.map(frameset.frames, frame => this.getAllStyleSheets(frame));
 | 
						|
    return selfSheets.concat(...subSheets);
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(msg) {
 | 
						|
    switch (msg.name) {
 | 
						|
      case "PageStyle:Switch":
 | 
						|
        this.markupDocumentViewer.authorStyleDisabled = false;
 | 
						|
        this._stylesheetSwitchAll(content, msg.data.title);
 | 
						|
        break;
 | 
						|
 | 
						|
      case "PageStyle:Disable":
 | 
						|
        this.markupDocumentViewer.authorStyleDisabled = true;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    this.sendStyleSheetInfo();
 | 
						|
  },
 | 
						|
 | 
						|
  _stylesheetSwitchAll(frameset, title) {
 | 
						|
    if (!title || this._stylesheetInFrame(frameset, title)) {
 | 
						|
      this._stylesheetSwitchFrame(frameset, title);
 | 
						|
    }
 | 
						|
 | 
						|
    for (let i = 0; i < frameset.frames.length; i++) {
 | 
						|
      // Recurse into sub-frames.
 | 
						|
      this._stylesheetSwitchAll(frameset.frames[i], title);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  _stylesheetSwitchFrame(frame, title) {
 | 
						|
    var docStyleSheets = frame.document.styleSheets;
 | 
						|
 | 
						|
    for (let i = 0; i < docStyleSheets.length; ++i) {
 | 
						|
      let docStyleSheet = docStyleSheets[i];
 | 
						|
      if (docStyleSheet.title) {
 | 
						|
        docStyleSheet.disabled = (docStyleSheet.title != title);
 | 
						|
      } else if (docStyleSheet.disabled) {
 | 
						|
        docStyleSheet.disabled = false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  _stylesheetInFrame(frame, title) {
 | 
						|
    return Array.some(frame.document.styleSheets, (styleSheet) => styleSheet.title == title);
 | 
						|
  },
 | 
						|
 | 
						|
  _filterStyleSheets(styleSheets) {
 | 
						|
    let result = [];
 | 
						|
 | 
						|
    for (let currentStyleSheet of styleSheets) {
 | 
						|
      if (!currentStyleSheet.title)
 | 
						|
        continue;
 | 
						|
 | 
						|
      // Skip any stylesheets that don't match the screen media type.
 | 
						|
      if (currentStyleSheet.media.length > 0) {
 | 
						|
        let mediaQueryList = currentStyleSheet.media.mediaText;
 | 
						|
        if (!content.matchMedia(mediaQueryList).matches) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      let URI;
 | 
						|
      try {
 | 
						|
        if (!currentStyleSheet.ownerNode ||
 | 
						|
            // special-case style nodes, which have no href
 | 
						|
            currentStyleSheet.ownerNode.nodeName.toLowerCase() != "style") {
 | 
						|
          URI = Services.io.newURI(currentStyleSheet.href);
 | 
						|
        }
 | 
						|
      } catch (e) {
 | 
						|
        if (e.result != Cr.NS_ERROR_MALFORMED_URI) {
 | 
						|
          throw e;
 | 
						|
        }
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      // We won't send data URIs all of the way up to the parent, as these
 | 
						|
      // can be arbitrarily large.
 | 
						|
      let sentURI = (!URI || URI.scheme == "data") ? null : URI.spec;
 | 
						|
 | 
						|
      result.push({
 | 
						|
        title: currentStyleSheet.title,
 | 
						|
        disabled: currentStyleSheet.disabled,
 | 
						|
        href: sentURI,
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    return result;
 | 
						|
  },
 | 
						|
};
 | 
						|
PageStyleHandler.init();
 | 
						|
 | 
						|
// Keep a reference to the translation content handler to avoid it it being GC'ed.
 | 
						|
var trHandler = null;
 | 
						|
if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
 | 
						|
  Cu.import("resource:///modules/translation/TranslationContentHandler.jsm");
 | 
						|
  trHandler = new TranslationContentHandler(global, docShell);
 | 
						|
}
 | 
						|
 | 
						|
function gKeywordURIFixup(fixupInfo) {
 | 
						|
  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
 | 
						|
  if (!fixupInfo.consumer) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Ignore info from other docshells
 | 
						|
  let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
 | 
						|
  if (parent != docShell)
 | 
						|
    return;
 | 
						|
 | 
						|
  let data = {};
 | 
						|
  for (let f of Object.keys(fixupInfo)) {
 | 
						|
    if (f == "consumer" || typeof fixupInfo[f] == "function")
 | 
						|
      continue;
 | 
						|
 | 
						|
    if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
 | 
						|
      data[f] = fixupInfo[f].spec;
 | 
						|
    } else {
 | 
						|
      data[f] = fixupInfo[f];
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  sendAsyncMessage("Browser:URIFixup", data);
 | 
						|
}
 | 
						|
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
 | 
						|
addEventListener("unload", () => {
 | 
						|
  Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
 | 
						|
}, false);
 | 
						|
 | 
						|
addMessageListener("Browser:AppTab", function(message) {
 | 
						|
  if (docShell) {
 | 
						|
    docShell.isAppTab = message.data.isAppTab;
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
let PrerenderContentHandler = {
 | 
						|
  init() {
 | 
						|
    this._pending = [];
 | 
						|
    this._idMonotonic = 0;
 | 
						|
    this._initialized = true;
 | 
						|
    addMessageListener("Prerender:Canceled", this);
 | 
						|
    addMessageListener("Prerender:Swapped", this);
 | 
						|
  },
 | 
						|
 | 
						|
  get initialized() {
 | 
						|
    return !!this._initialized;
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(aMessage) {
 | 
						|
    switch (aMessage.name) {
 | 
						|
      case "Prerender:Canceled": {
 | 
						|
        for (let i = 0; i < this._pending.length; ++i) {
 | 
						|
          if (this._pending[i].id === aMessage.data.id) {
 | 
						|
            if (this._pending[i].failure) {
 | 
						|
              this._pending[i].failure.run();
 | 
						|
            }
 | 
						|
            // Remove the item from the array
 | 
						|
            this._pending.splice(i, 1);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case "Prerender:Swapped": {
 | 
						|
        for (let i = 0; i < this._pending.length; ++i) {
 | 
						|
          if (this._pending[i].id === aMessage.data.id) {
 | 
						|
            if (this._pending[i].success) {
 | 
						|
              this._pending[i].success.run();
 | 
						|
            }
 | 
						|
            // Remove the item from the array
 | 
						|
            this._pending.splice(i, 1);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  startPrerenderingDocument(aHref, aReferrer, aTriggeringPrincipal) {
 | 
						|
    // XXX: Make this constant a pref
 | 
						|
    if (this._pending.length >= 2) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let id = ++this._idMonotonic;
 | 
						|
    sendAsyncMessage("Prerender:Request", {
 | 
						|
      href: aHref.spec,
 | 
						|
      referrer: aReferrer ? aReferrer.spec : null,
 | 
						|
      id,
 | 
						|
      triggeringPrincipal: Utils.serializePrincipal(aTriggeringPrincipal),
 | 
						|
    });
 | 
						|
 | 
						|
    this._pending.push({
 | 
						|
      href: aHref,
 | 
						|
      referrer: aReferrer,
 | 
						|
      id,
 | 
						|
      success: null,
 | 
						|
      failure: null,
 | 
						|
    });
 | 
						|
  },
 | 
						|
 | 
						|
  shouldSwitchToPrerenderedDocument(aHref, aReferrer, aSuccess, aFailure) {
 | 
						|
    // Check if we think there is a prerendering document pending for the given
 | 
						|
    // href and referrer. If we think there is one, we will send a message to
 | 
						|
    // the parent process asking it to do a swap, and hook up the success and
 | 
						|
    // failure listeners.
 | 
						|
    for (let i = 0; i < this._pending.length; ++i) {
 | 
						|
      let p = this._pending[i];
 | 
						|
      if (p.href.equals(aHref) && p.referrer.equals(aReferrer)) {
 | 
						|
        p.success = aSuccess;
 | 
						|
        p.failure = aFailure;
 | 
						|
        sendAsyncMessage("Prerender:Swap", {id: p.id});
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
 | 
						|
  // We only want to initialize the PrerenderContentHandler in the content
 | 
						|
  // process. Outside of the content process, this should be unused.
 | 
						|
  PrerenderContentHandler.init();
 | 
						|
}
 | 
						|
 | 
						|
var WebBrowserChrome = {
 | 
						|
  onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
 | 
						|
    return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
 | 
						|
  },
 | 
						|
 | 
						|
  // Check whether this URI should load in the current process
 | 
						|
  shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
 | 
						|
    if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
 | 
						|
      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
  },
 | 
						|
 | 
						|
  shouldLoadURIInThisProcess(aURI) {
 | 
						|
    return E10SUtils.shouldLoadURIInThisProcess(aURI);
 | 
						|
  },
 | 
						|
 | 
						|
  // Try to reload the currently active or currently loading page in a new process.
 | 
						|
  reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aLoadFlags) {
 | 
						|
    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true, aLoadFlags);
 | 
						|
    return true;
 | 
						|
  },
 | 
						|
 | 
						|
  startPrerenderingDocument(aHref, aReferrer, aTriggeringPrincipal) {
 | 
						|
    if (PrerenderContentHandler.initialized) {
 | 
						|
      PrerenderContentHandler.startPrerenderingDocument(aHref, aReferrer, aTriggeringPrincipal);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  shouldSwitchToPrerenderedDocument(aHref, aReferrer, aSuccess, aFailure) {
 | 
						|
    if (PrerenderContentHandler.initialized) {
 | 
						|
      return PrerenderContentHandler.shouldSwitchToPrerenderedDocument(
 | 
						|
        aHref, aReferrer, aSuccess, aFailure);
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
 | 
						|
  let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                         .getInterface(Ci.nsITabChild);
 | 
						|
  tabchild.webBrowserChrome = WebBrowserChrome;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
var DOMFullscreenHandler = {
 | 
						|
 | 
						|
  init() {
 | 
						|
    addMessageListener("DOMFullscreen:Entered", this);
 | 
						|
    addMessageListener("DOMFullscreen:CleanUp", this);
 | 
						|
    addEventListener("MozDOMFullscreen:Request", this);
 | 
						|
    addEventListener("MozDOMFullscreen:Entered", this);
 | 
						|
    addEventListener("MozDOMFullscreen:NewOrigin", this);
 | 
						|
    addEventListener("MozDOMFullscreen:Exit", this);
 | 
						|
    addEventListener("MozDOMFullscreen:Exited", this);
 | 
						|
  },
 | 
						|
 | 
						|
  get _windowUtils() {
 | 
						|
    if (!content) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    return content.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                  .getInterface(Ci.nsIDOMWindowUtils);
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(aMessage) {
 | 
						|
    let windowUtils = this._windowUtils;
 | 
						|
    switch (aMessage.name) {
 | 
						|
      case "DOMFullscreen:Entered": {
 | 
						|
        this._lastTransactionId = windowUtils.lastTransactionId;
 | 
						|
        if (!windowUtils.handleFullscreenRequests() &&
 | 
						|
            !content.document.fullscreenElement) {
 | 
						|
          // If we don't actually have any pending fullscreen request
 | 
						|
          // to handle, neither we have been in fullscreen, tell the
 | 
						|
          // parent to just exit.
 | 
						|
          sendAsyncMessage("DOMFullscreen:Exit");
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case "DOMFullscreen:CleanUp": {
 | 
						|
        // If we've exited fullscreen at this point, no need to record
 | 
						|
        // transaction id or call exit fullscreen. This is especially
 | 
						|
        // important for non-e10s, since in that case, it is possible
 | 
						|
        // that no more paint would be triggered after this point.
 | 
						|
        if (content.document.fullscreenElement && windowUtils) {
 | 
						|
          this._lastTransactionId = windowUtils.lastTransactionId;
 | 
						|
          windowUtils.exitFullscreen();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(aEvent) {
 | 
						|
    switch (aEvent.type) {
 | 
						|
      case "MozDOMFullscreen:Request": {
 | 
						|
        sendAsyncMessage("DOMFullscreen:Request");
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case "MozDOMFullscreen:NewOrigin": {
 | 
						|
        sendAsyncMessage("DOMFullscreen:NewOrigin", {
 | 
						|
          originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
 | 
						|
        });
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case "MozDOMFullscreen:Exit": {
 | 
						|
        sendAsyncMessage("DOMFullscreen:Exit");
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case "MozDOMFullscreen:Entered":
 | 
						|
      case "MozDOMFullscreen:Exited": {
 | 
						|
        addEventListener("MozAfterPaint", this);
 | 
						|
        if (!content || !content.document.fullscreenElement) {
 | 
						|
          // If we receive any fullscreen change event, and find we are
 | 
						|
          // actually not in fullscreen, also ask the parent to exit to
 | 
						|
          // ensure that the parent always exits fullscreen when we do.
 | 
						|
          sendAsyncMessage("DOMFullscreen:Exit");
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case "MozAfterPaint": {
 | 
						|
        // Only send Painted signal after we actually finish painting
 | 
						|
        // the transition for the fullscreen change.
 | 
						|
        // Note that this._lastTransactionId is not set when in non-e10s
 | 
						|
        // mode, so we need to check that explicitly.
 | 
						|
        if (!this._lastTransactionId ||
 | 
						|
            aEvent.transactionId > this._lastTransactionId) {
 | 
						|
          removeEventListener("MozAfterPaint", this);
 | 
						|
          sendAsyncMessage("DOMFullscreen:Painted");
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
DOMFullscreenHandler.init();
 | 
						|
 | 
						|
var RefreshBlocker = {
 | 
						|
  PREF: "accessibility.blockautorefresh",
 | 
						|
 | 
						|
  // Bug 1247100 - When a refresh is caused by an HTTP header,
 | 
						|
  // onRefreshAttempted will be fired before onLocationChange.
 | 
						|
  // When a refresh is caused by a <meta> tag in the document,
 | 
						|
  // onRefreshAttempted will be fired after onLocationChange.
 | 
						|
  //
 | 
						|
  // We only ever want to send a message to the parent after
 | 
						|
  // onLocationChange has fired, since the parent uses the
 | 
						|
  // onLocationChange update to clear transient notifications.
 | 
						|
  // Sending the message before onLocationChange will result in
 | 
						|
  // us creating the notification, and then clearing it very
 | 
						|
  // soon after.
 | 
						|
  //
 | 
						|
  // To account for both cases (onRefreshAttempted before
 | 
						|
  // onLocationChange, and onRefreshAttempted after onLocationChange),
 | 
						|
  // we'll hold a mapping of DOM Windows that we see get
 | 
						|
  // sent through both onLocationChange and onRefreshAttempted.
 | 
						|
  // When either run, they'll check the WeakMap for the existence
 | 
						|
  // of the DOM Window. If it doesn't exist, it'll add it. If
 | 
						|
  // it finds it, it'll know that it's safe to send the message
 | 
						|
  // to the parent, since we know that both have fired.
 | 
						|
  //
 | 
						|
  // The DOM Window is removed from blockedWindows when we notice
 | 
						|
  // the nsIWebProgress change state to STATE_STOP for the
 | 
						|
  // STATE_IS_WINDOW case.
 | 
						|
  //
 | 
						|
  // DOM Windows are mapped to a JS object that contains the data
 | 
						|
  // to be sent to the parent to show the notification. Since that
 | 
						|
  // data is only known when onRefreshAttempted is fired, it's only
 | 
						|
  // ever stashed in the map if onRefreshAttempted fires first -
 | 
						|
  // otherwise, null is set as the value of the mapping.
 | 
						|
  blockedWindows: new WeakMap(),
 | 
						|
 | 
						|
  init() {
 | 
						|
    if (Services.prefs.getBoolPref(this.PREF)) {
 | 
						|
      this.enable();
 | 
						|
    }
 | 
						|
 | 
						|
    Services.prefs.addObserver(this.PREF, this);
 | 
						|
  },
 | 
						|
 | 
						|
  uninit() {
 | 
						|
    if (Services.prefs.getBoolPref(this.PREF)) {
 | 
						|
      this.disable();
 | 
						|
    }
 | 
						|
 | 
						|
    Services.prefs.removeObserver(this.PREF, this);
 | 
						|
  },
 | 
						|
 | 
						|
  observe(subject, topic, data) {
 | 
						|
    if (topic == "nsPref:changed" && data == this.PREF) {
 | 
						|
      if (Services.prefs.getBoolPref(this.PREF)) {
 | 
						|
        this.enable();
 | 
						|
      } else {
 | 
						|
        this.disable();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  enable() {
 | 
						|
    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
 | 
						|
                     .createInstance(Ci.nsIWebProgress);
 | 
						|
    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
 | 
						|
 | 
						|
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                              .getInterface(Ci.nsIWebProgress);
 | 
						|
    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
 | 
						|
 | 
						|
    addMessageListener("RefreshBlocker:Refresh", this);
 | 
						|
  },
 | 
						|
 | 
						|
  disable() {
 | 
						|
    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                              .getInterface(Ci.nsIWebProgress);
 | 
						|
    webProgress.removeProgressListener(this._filter);
 | 
						|
 | 
						|
    this._filter.removeProgressListener(this);
 | 
						|
    this._filter = null;
 | 
						|
 | 
						|
    removeMessageListener("RefreshBlocker:Refresh", this);
 | 
						|
  },
 | 
						|
 | 
						|
  send(data) {
 | 
						|
    sendAsyncMessage("RefreshBlocker:Blocked", data);
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Notices when the nsIWebProgress transitions to STATE_STOP for
 | 
						|
   * the STATE_IS_WINDOW case, which will clear any mappings from
 | 
						|
   * blockedWindows.
 | 
						|
   */
 | 
						|
  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
 | 
						|
    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
 | 
						|
        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
 | 
						|
      this.blockedWindows.delete(aWebProgress.DOMWindow);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Notices when the location has changed. If, when running,
 | 
						|
   * onRefreshAttempted has already fired for this DOM Window, will
 | 
						|
   * send the appropriate refresh blocked data to the parent.
 | 
						|
   */
 | 
						|
  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
 | 
						|
    let win = aWebProgress.DOMWindow;
 | 
						|
    if (this.blockedWindows.has(win)) {
 | 
						|
      let data = this.blockedWindows.get(win);
 | 
						|
      if (data) {
 | 
						|
        // We saw onRefreshAttempted before onLocationChange, so
 | 
						|
        // send the message to the parent to show the notification.
 | 
						|
        this.send(data);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      this.blockedWindows.set(win, null);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Notices when a refresh / reload was attempted. If, when running,
 | 
						|
   * onLocationChange has not yet run, will stash the appropriate data
 | 
						|
   * into the blockedWindows map to be sent when onLocationChange fires.
 | 
						|
   */
 | 
						|
  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
 | 
						|
    let win = aWebProgress.DOMWindow;
 | 
						|
    let outerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                           .getInterface(Ci.nsIDOMWindowUtils)
 | 
						|
                           .outerWindowID;
 | 
						|
 | 
						|
    let data = {
 | 
						|
      URI: aURI.spec,
 | 
						|
      originCharset: aURI.originCharset,
 | 
						|
      delay: aDelay,
 | 
						|
      sameURI: aSameURI,
 | 
						|
      outerWindowID,
 | 
						|
    };
 | 
						|
 | 
						|
    if (this.blockedWindows.has(win)) {
 | 
						|
      // onLocationChange must have fired before, so we can tell the
 | 
						|
      // parent to show the notification.
 | 
						|
      this.send(data);
 | 
						|
    } else {
 | 
						|
      // onLocationChange hasn't fired yet, so stash the data in the
 | 
						|
      // map so that onLocationChange can send it when it fires.
 | 
						|
      this.blockedWindows.set(win, data);
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(message) {
 | 
						|
    let data = message.data;
 | 
						|
 | 
						|
    if (message.name == "RefreshBlocker:Refresh") {
 | 
						|
      let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
 | 
						|
      let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                          .getInterface(Ci.nsIDocShell)
 | 
						|
                          .QueryInterface(Ci.nsIRefreshURI);
 | 
						|
 | 
						|
      let URI = Services.io.newURI(data.URI, data.originCharset);
 | 
						|
 | 
						|
      refreshURI.forceRefreshURI(URI, data.delay, true);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener2,
 | 
						|
                                         Ci.nsIWebProgressListener,
 | 
						|
                                         Ci.nsISupportsWeakReference,
 | 
						|
                                         Ci.nsISupports]),
 | 
						|
};
 | 
						|
 | 
						|
RefreshBlocker.init();
 | 
						|
 | 
						|
var UserContextIdNotifier = {
 | 
						|
  init() {
 | 
						|
    addEventListener("DOMWindowCreated", this);
 | 
						|
  },
 | 
						|
 | 
						|
  uninit() {
 | 
						|
    removeEventListener("DOMWindowCreated", this);
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(aEvent) {
 | 
						|
    // When the window is created, we want to inform the tabbrowser about
 | 
						|
    // the userContextId in use in order to update the UI correctly.
 | 
						|
    // Just because we cannot change the userContextId from an active docShell,
 | 
						|
    // we don't need to check DOMContentLoaded again.
 | 
						|
    this.uninit();
 | 
						|
 | 
						|
    // We use the docShell because content.document can have been loaded before
 | 
						|
    // setting the originAttributes.
 | 
						|
    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
 | 
						|
    let userContextId = loadContext.originAttributes.userContextId;
 | 
						|
 | 
						|
    sendAsyncMessage("Browser:WindowCreated", { userContextId });
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
UserContextIdNotifier.init();
 | 
						|
 | 
						|
Services.obs.notifyObservers(this, "tab-content-frameloader-created");
 | 
						|
 | 
						|
addEventListener("unload", () => {
 | 
						|
  RefreshBlocker.uninit();
 | 
						|
});
 | 
						|
 | 
						|
addMessageListener("AllowScriptsToClose", () => {
 | 
						|
  content.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
         .getInterface(Ci.nsIDOMWindowUtils)
 | 
						|
         .allowScriptsToClose();
 | 
						|
});
 | 
						|
 | 
						|
addEventListener("MozAfterPaint", function onFirstPaint() {
 | 
						|
  removeEventListener("MozAfterPaint", onFirstPaint);
 | 
						|
  sendAsyncMessage("Browser:FirstPaint");
 | 
						|
});
 |