355 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* Copyright 2012 Mozilla Foundation
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
var EXPORTED_SYMBOLS = ["PdfjsChromeUtils"];
 | 
						|
 | 
						|
const Cc = Components.classes;
 | 
						|
const Ci = Components.interfaces;
 | 
						|
const Cr = Components.results;
 | 
						|
const Cu = Components.utils;
 | 
						|
 | 
						|
const PREF_PREFIX = "PDFJSSCRIPT_PREF_PREFIX";
 | 
						|
const PDF_CONTENT_TYPE = "application/pdf";
 | 
						|
 | 
						|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | 
						|
Cu.import("resource://gre/modules/Services.jsm");
 | 
						|
 | 
						|
var Svc = {};
 | 
						|
XPCOMUtils.defineLazyServiceGetter(Svc, "mime",
 | 
						|
                                   "@mozilla.org/mime;1",
 | 
						|
                                   "nsIMIMEService");
 | 
						|
 | 
						|
var DEFAULT_PREFERENCES =
 | 
						|
//#include ../../../web/default_preferences.json
 | 
						|
//#if false
 | 
						|
  "end of DEFAULT_PREFERENCES";
 | 
						|
//#endif
 | 
						|
 | 
						|
var PdfjsChromeUtils = {
 | 
						|
  // For security purposes when running remote, we restrict preferences
 | 
						|
  // content can access.
 | 
						|
  _allowedPrefNames: Object.keys(DEFAULT_PREFERENCES),
 | 
						|
  _ppmm: null,
 | 
						|
  _mmg: null,
 | 
						|
 | 
						|
  /*
 | 
						|
   * Public API
 | 
						|
   */
 | 
						|
 | 
						|
  init() {
 | 
						|
    this._browsers = new WeakSet();
 | 
						|
    if (!this._ppmm) {
 | 
						|
      // global parent process message manager (PPMM)
 | 
						|
      this._ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
 | 
						|
        getService(Ci.nsIMessageBroadcaster);
 | 
						|
      this._ppmm.addMessageListener("PDFJS:Parent:clearUserPref", this);
 | 
						|
      this._ppmm.addMessageListener("PDFJS:Parent:setIntPref", this);
 | 
						|
      this._ppmm.addMessageListener("PDFJS:Parent:setBoolPref", this);
 | 
						|
      this._ppmm.addMessageListener("PDFJS:Parent:setCharPref", this);
 | 
						|
      this._ppmm.addMessageListener("PDFJS:Parent:setStringPref", this);
 | 
						|
      this._ppmm.addMessageListener("PDFJS:Parent:isDefaultHandlerApp", this);
 | 
						|
 | 
						|
      // global dom message manager (MMg)
 | 
						|
      this._mmg = Cc["@mozilla.org/globalmessagemanager;1"].
 | 
						|
        getService(Ci.nsIMessageListenerManager);
 | 
						|
      this._mmg.addMessageListener("PDFJS:Parent:displayWarning", this);
 | 
						|
 | 
						|
      this._mmg.addMessageListener("PDFJS:Parent:addEventListener", this);
 | 
						|
      this._mmg.addMessageListener("PDFJS:Parent:removeEventListener", this);
 | 
						|
      this._mmg.addMessageListener("PDFJS:Parent:updateControlState", this);
 | 
						|
 | 
						|
//#if !MOZCENTRAL
 | 
						|
      // The signature of `Services.obs.addObserver` changed in Firefox 55,
 | 
						|
      // see https://bugzilla.mozilla.org/show_bug.cgi?id=1355216.
 | 
						|
      // PLEASE NOTE: While the third parameter is now optional,
 | 
						|
      // omitting it in prior Firefox versions breaks the addon.
 | 
						|
      var ffVersion = parseInt(Services.appinfo.platformVersion);
 | 
						|
      if (ffVersion <= 55) {
 | 
						|
        Services.obs.addObserver(this, "quit-application", false);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
//#endif
 | 
						|
      // Observer to handle shutdown.
 | 
						|
      Services.obs.addObserver(this, "quit-application");
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  uninit() {
 | 
						|
    if (this._ppmm) {
 | 
						|
      this._ppmm.removeMessageListener("PDFJS:Parent:clearUserPref", this);
 | 
						|
      this._ppmm.removeMessageListener("PDFJS:Parent:setIntPref", this);
 | 
						|
      this._ppmm.removeMessageListener("PDFJS:Parent:setBoolPref", this);
 | 
						|
      this._ppmm.removeMessageListener("PDFJS:Parent:setCharPref", this);
 | 
						|
      this._ppmm.removeMessageListener("PDFJS:Parent:setStringPref", this);
 | 
						|
      this._ppmm.removeMessageListener("PDFJS:Parent:isDefaultHandlerApp",
 | 
						|
                                       this);
 | 
						|
 | 
						|
      this._mmg.removeMessageListener("PDFJS:Parent:displayWarning", this);
 | 
						|
 | 
						|
      this._mmg.removeMessageListener("PDFJS:Parent:addEventListener", this);
 | 
						|
      this._mmg.removeMessageListener("PDFJS:Parent:removeEventListener", this);
 | 
						|
      this._mmg.removeMessageListener("PDFJS:Parent:updateControlState", this);
 | 
						|
 | 
						|
      Services.obs.removeObserver(this, "quit-application");
 | 
						|
 | 
						|
      this._mmg = null;
 | 
						|
      this._ppmm = null;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Called by the main module when preference changes are picked up
 | 
						|
   * in the parent process. Observers don't propagate so we need to
 | 
						|
   * instruct the child to refresh its configuration and (possibly)
 | 
						|
   * the module's registration.
 | 
						|
   */
 | 
						|
  notifyChildOfSettingsChange(enabled) {
 | 
						|
    if (Services.appinfo.processType ===
 | 
						|
        Services.appinfo.PROCESS_TYPE_DEFAULT && this._ppmm) {
 | 
						|
      // XXX kinda bad, we want to get the parent process mm associated
 | 
						|
      // with the content process. _ppmm is currently the global process
 | 
						|
      // manager, which means this is going to fire to every child process
 | 
						|
      // we have open. Unfortunately I can't find a way to get at that
 | 
						|
      // process specific mm from js.
 | 
						|
      this._ppmm.broadcastAsyncMessage("PDFJS:Child:updateSettings",
 | 
						|
                                       { enabled, });
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Events
 | 
						|
   */
 | 
						|
 | 
						|
  observe(aSubject, aTopic, aData) {
 | 
						|
    if (aTopic === "quit-application") {
 | 
						|
      this.uninit();
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage(aMsg) {
 | 
						|
    switch (aMsg.name) {
 | 
						|
      case "PDFJS:Parent:clearUserPref":
 | 
						|
        this._clearUserPref(aMsg.data.name);
 | 
						|
        break;
 | 
						|
      case "PDFJS:Parent:setIntPref":
 | 
						|
        this._setIntPref(aMsg.data.name, aMsg.data.value);
 | 
						|
        break;
 | 
						|
      case "PDFJS:Parent:setBoolPref":
 | 
						|
        this._setBoolPref(aMsg.data.name, aMsg.data.value);
 | 
						|
        break;
 | 
						|
      case "PDFJS:Parent:setCharPref":
 | 
						|
        this._setCharPref(aMsg.data.name, aMsg.data.value);
 | 
						|
        break;
 | 
						|
      case "PDFJS:Parent:setStringPref":
 | 
						|
        this._setStringPref(aMsg.data.name, aMsg.data.value);
 | 
						|
        break;
 | 
						|
      case "PDFJS:Parent:isDefaultHandlerApp":
 | 
						|
        return this.isDefaultHandlerApp();
 | 
						|
      case "PDFJS:Parent:displayWarning":
 | 
						|
        this._displayWarning(aMsg);
 | 
						|
        break;
 | 
						|
 | 
						|
      case "PDFJS:Parent:updateControlState":
 | 
						|
        return this._updateControlState(aMsg);
 | 
						|
      case "PDFJS:Parent:addEventListener":
 | 
						|
        return this._addEventListener(aMsg);
 | 
						|
      case "PDFJS:Parent:removeEventListener":
 | 
						|
        return this._removeEventListener(aMsg);
 | 
						|
    }
 | 
						|
    return undefined;
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Internal
 | 
						|
   */
 | 
						|
 | 
						|
  _findbarFromMessage(aMsg) {
 | 
						|
    let browser = aMsg.target;
 | 
						|
    let tabbrowser = browser.getTabBrowser();
 | 
						|
    let tab = tabbrowser.getTabForBrowser(browser);
 | 
						|
    return tabbrowser.getFindBar(tab);
 | 
						|
  },
 | 
						|
 | 
						|
  _updateControlState(aMsg) {
 | 
						|
    let data = aMsg.data;
 | 
						|
    this._findbarFromMessage(aMsg)
 | 
						|
        .updateControlState(data.result, data.findPrevious);
 | 
						|
  },
 | 
						|
 | 
						|
  handleEvent(aEvent) {
 | 
						|
    // To avoid forwarding the message as a CPOW, create a structured cloneable
 | 
						|
    // version of the event for both performance, and ease of usage, reasons.
 | 
						|
    let type = aEvent.type;
 | 
						|
    let detail = {
 | 
						|
      query: aEvent.detail.query,
 | 
						|
      caseSensitive: aEvent.detail.caseSensitive,
 | 
						|
      highlightAll: aEvent.detail.highlightAll,
 | 
						|
      findPrevious: aEvent.detail.findPrevious,
 | 
						|
    };
 | 
						|
 | 
						|
    let browser = aEvent.currentTarget.browser;
 | 
						|
    if (!this._browsers.has(browser)) {
 | 
						|
      throw new Error("FindEventManager was not bound " +
 | 
						|
                      "for the current browser.");
 | 
						|
    }
 | 
						|
    // Only forward the events if the current browser is a registered browser.
 | 
						|
    let mm = browser.messageManager;
 | 
						|
    mm.sendAsyncMessage("PDFJS:Child:handleEvent", { type, detail, });
 | 
						|
    aEvent.preventDefault();
 | 
						|
  },
 | 
						|
 | 
						|
  _types: ["find",
 | 
						|
           "findagain",
 | 
						|
           "findhighlightallchange",
 | 
						|
           "findcasesensitivitychange"],
 | 
						|
 | 
						|
  _addEventListener(aMsg) {
 | 
						|
    let browser = aMsg.target;
 | 
						|
    if (this._browsers.has(browser)) {
 | 
						|
      throw new Error("FindEventManager was bound 2nd time " +
 | 
						|
                      "without unbinding it first.");
 | 
						|
    }
 | 
						|
 | 
						|
    // Since this jsm is global, we need to store all the browsers
 | 
						|
    // we have to forward the messages for.
 | 
						|
    this._browsers.add(browser);
 | 
						|
 | 
						|
    // And we need to start listening to find events.
 | 
						|
    for (var i = 0; i < this._types.length; i++) {
 | 
						|
      var type = this._types[i];
 | 
						|
      this._findbarFromMessage(aMsg)
 | 
						|
          .addEventListener(type, this, true);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  _removeEventListener(aMsg) {
 | 
						|
    let browser = aMsg.target;
 | 
						|
    if (!this._browsers.has(browser)) {
 | 
						|
      throw new Error("FindEventManager was unbound without binding it first.");
 | 
						|
    }
 | 
						|
 | 
						|
    this._browsers.delete(browser);
 | 
						|
 | 
						|
    // No reason to listen to find events any longer.
 | 
						|
    for (var i = 0; i < this._types.length; i++) {
 | 
						|
      var type = this._types[i];
 | 
						|
      this._findbarFromMessage(aMsg)
 | 
						|
          .removeEventListener(type, this, true);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  _ensurePreferenceAllowed(aPrefName) {
 | 
						|
    let unPrefixedName = aPrefName.split(PREF_PREFIX + ".");
 | 
						|
    if (unPrefixedName[0] !== "" ||
 | 
						|
        this._allowedPrefNames.indexOf(unPrefixedName[1]) === -1) {
 | 
						|
      let msg = "\"" + aPrefName + "\" " +
 | 
						|
                "can't be accessed from content. See PdfjsChromeUtils.";
 | 
						|
      throw new Error(msg);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  _clearUserPref(aPrefName) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.clearUserPref(aPrefName);
 | 
						|
  },
 | 
						|
 | 
						|
  _setIntPref(aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.setIntPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  _setBoolPref(aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.setBoolPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  _setCharPref(aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.setCharPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  _setStringPref(aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
//#if !MOZCENTRAL
 | 
						|
    if (!Services.prefs.setStringPref) {
 | 
						|
      let str = Cc["@mozilla.org/supports-string;1"]
 | 
						|
                  .createInstance(Ci.nsISupportsString);
 | 
						|
      str.data = aPrefValue;
 | 
						|
      Services.prefs.setComplexValue(aPrefName, Ci.nsISupportsString, str);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
//#endif
 | 
						|
    Services.prefs.setStringPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Svc.mime doesn't have profile information in the child, so
 | 
						|
   * we bounce this pdfjs enabled configuration check over to the
 | 
						|
   * parent.
 | 
						|
   */
 | 
						|
  isDefaultHandlerApp() {
 | 
						|
    var handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, "pdf");
 | 
						|
    return (!handlerInfo.alwaysAskBeforeHandling &&
 | 
						|
            handlerInfo.preferredAction === Ci.nsIHandlerInfo.handleInternally);
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Display a notification warning when the renderer isn't sure
 | 
						|
   * a pdf displayed correctly.
 | 
						|
   */
 | 
						|
  _displayWarning(aMsg) {
 | 
						|
    let data = aMsg.data;
 | 
						|
    let browser = aMsg.target;
 | 
						|
 | 
						|
    let tabbrowser = browser.getTabBrowser();
 | 
						|
    let notificationBox = tabbrowser.getNotificationBox(browser);
 | 
						|
 | 
						|
    // Flag so we don't send the message twice, since if the user clicks
 | 
						|
    // "open with different viewer" both the button callback and
 | 
						|
    // eventCallback will be called.
 | 
						|
    let messageSent = false;
 | 
						|
    function sendMessage(download) {
 | 
						|
      let mm = browser.messageManager;
 | 
						|
      mm.sendAsyncMessage("PDFJS:Child:fallbackDownload", { download, });
 | 
						|
    }
 | 
						|
    let buttons = [{
 | 
						|
      label: data.label,
 | 
						|
      accessKey: data.accessKey,
 | 
						|
      callback() {
 | 
						|
        messageSent = true;
 | 
						|
        sendMessage(true);
 | 
						|
      },
 | 
						|
    }];
 | 
						|
    notificationBox.appendNotification(data.message, "pdfjs-fallback", null,
 | 
						|
                                       notificationBox.PRIORITY_INFO_LOW,
 | 
						|
                                       buttons,
 | 
						|
                                       function eventsCallback(eventType) {
 | 
						|
      // Currently there is only one event "removed" but if there are any other
 | 
						|
      // added in the future we still only care about removed at the moment.
 | 
						|
      if (eventType !== "removed") {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      // Don't send a response again if we already responded when the button was
 | 
						|
      // clicked.
 | 
						|
      if (messageSent) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      sendMessage(false);
 | 
						|
    });
 | 
						|
  },
 | 
						|
};
 |