330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 | 
						|
/* 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.
 | 
						|
 */
 | 
						|
/* jshint esnext:true */
 | 
						|
/* globals Components, Services, XPCOMUtils, DEFAULT_PREFERENCES */
 | 
						|
 | 
						|
'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');
 | 
						|
 | 
						|
let Svc = {};
 | 
						|
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
 | 
						|
                                   '@mozilla.org/mime;1',
 | 
						|
                                   'nsIMIMEService');
 | 
						|
 | 
						|
//#include ../../../web/default_preferences.js
 | 
						|
 | 
						|
let 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: function () {
 | 
						|
    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:getChromeWindow', this);
 | 
						|
      this._mmg.addMessageListener('PDFJS:Parent:getFindBar', this);
 | 
						|
      this._mmg.addMessageListener('PDFJS:Parent:displayWarning', this);
 | 
						|
 | 
						|
      // observer to handle shutdown
 | 
						|
      Services.obs.addObserver(this, 'quit-application', false);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  uninit: function () {
 | 
						|
    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:getChromeWindow', this);
 | 
						|
      this._mmg.removeMessageListener('PDFJS:Parent:getFindBar', this);
 | 
						|
      this._mmg.removeMessageListener('PDFJS:Parent:displayWarning', this);
 | 
						|
 | 
						|
      Services.obs.removeObserver(this, 'quit-application', false);
 | 
						|
 | 
						|
      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: function () {
 | 
						|
    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:refreshSettings', {});
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Events
 | 
						|
   */
 | 
						|
 | 
						|
  observe: function(aSubject, aTopic, aData) {
 | 
						|
    if (aTopic === 'quit-application') {
 | 
						|
      this.uninit();
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  receiveMessage: function (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;
 | 
						|
 | 
						|
      // CPOW getters
 | 
						|
      case 'PDFJS:Parent:getChromeWindow':
 | 
						|
        return this._getChromeWindow(aMsg);
 | 
						|
      case 'PDFJS:Parent:getFindBar':
 | 
						|
        return this._getFindBar(aMsg);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Internal
 | 
						|
   */
 | 
						|
 | 
						|
  _getChromeWindow: function (aMsg) {
 | 
						|
    // See the child module, our return result here can't be the element
 | 
						|
    // since return results don't get auto CPOW'd.
 | 
						|
    let browser = aMsg.target;
 | 
						|
    let wrapper = new PdfjsWindowWrapper(browser);
 | 
						|
    let suitcase = aMsg.objects.suitcase;
 | 
						|
    suitcase.setChromeWindow(wrapper);
 | 
						|
    return true;
 | 
						|
  },
 | 
						|
 | 
						|
  _getFindBar: function (aMsg) {
 | 
						|
    // We send this over via the window's message manager, so target should
 | 
						|
    // be the dom window.
 | 
						|
    let browser = aMsg.target;
 | 
						|
    let wrapper = new PdfjsFindbarWrapper(browser);
 | 
						|
    let suitcase = aMsg.objects.suitcase;
 | 
						|
    suitcase.setFindBar(wrapper);
 | 
						|
    return true;
 | 
						|
  },
 | 
						|
 | 
						|
  _ensurePreferenceAllowed: function (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: function (aPrefName) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.clearUserPref(aPrefName);
 | 
						|
  },
 | 
						|
 | 
						|
  _setIntPref: function (aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.setIntPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  _setBoolPref: function (aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.setBoolPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  _setCharPref: function (aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    Services.prefs.setCharPref(aPrefName, aPrefValue);
 | 
						|
  },
 | 
						|
 | 
						|
  _setStringPref: function (aPrefName, aPrefValue) {
 | 
						|
    this._ensurePreferenceAllowed(aPrefName);
 | 
						|
    let str = Cc['@mozilla.org/supports-string;1']
 | 
						|
                .createInstance(Ci.nsISupportsString);
 | 
						|
    str.data = aPrefValue;
 | 
						|
    Services.prefs.setComplexValue(aPrefName, Ci.nsISupportsString, str);
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Svc.mime doesn't have profile information in the child, so
 | 
						|
   * we bounce this pdfjs enabled configuration check over to the
 | 
						|
   * parent.
 | 
						|
   */
 | 
						|
  isDefaultHandlerApp: function () {
 | 
						|
    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: function (aMsg) {
 | 
						|
    let json = aMsg.data;
 | 
						|
    let browser = aMsg.target;
 | 
						|
    let cpowCallback = aMsg.objects.callback;
 | 
						|
    let tabbrowser = browser.getTabBrowser();
 | 
						|
    let notificationBox = tabbrowser.getNotificationBox(browser);
 | 
						|
    // Flag so we don't call the response callback twice, since if the user
 | 
						|
    // clicks open with different viewer both the button callback and
 | 
						|
    // eventCallback will be called.
 | 
						|
    let responseSent = false;
 | 
						|
    let buttons = [{
 | 
						|
      label: json.label,
 | 
						|
      accessKey: json.accessKey,
 | 
						|
      callback: function() {
 | 
						|
        responseSent = true;
 | 
						|
        cpowCallback(true);
 | 
						|
      }
 | 
						|
    }];
 | 
						|
    notificationBox.appendNotification(json.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 (responseSent) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      cpowCallback(false);
 | 
						|
    });
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * CPOW security features require chrome objects declare exposed
 | 
						|
 * properties via __exposedProps__. We don't want to expose things
 | 
						|
 * directly on the findbar, so we wrap the findbar in a smaller
 | 
						|
 * object here that supports the features pdf.js needs.
 | 
						|
 */
 | 
						|
function PdfjsFindbarWrapper(aBrowser) {
 | 
						|
  let tabbrowser = aBrowser.getTabBrowser();
 | 
						|
  let tab;
 | 
						|
//#if MOZCENTRAL
 | 
						|
  tab = tabbrowser.getTabForBrowser(aBrowser);
 | 
						|
//#else
 | 
						|
  if (tabbrowser.getTabForBrowser) {
 | 
						|
    tab = tabbrowser.getTabForBrowser(aBrowser);
 | 
						|
  } else {
 | 
						|
    // _getTabForBrowser is depreciated in Firefox 35, see
 | 
						|
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1039500.
 | 
						|
    tab = tabbrowser._getTabForBrowser(aBrowser);
 | 
						|
  }
 | 
						|
//#endif
 | 
						|
  this._findbar = tabbrowser.getFindBar(tab);
 | 
						|
}
 | 
						|
 | 
						|
PdfjsFindbarWrapper.prototype = {
 | 
						|
  __exposedProps__: {
 | 
						|
    addEventListener: 'r',
 | 
						|
    removeEventListener: 'r',
 | 
						|
    updateControlState: 'r',
 | 
						|
  },
 | 
						|
  _findbar: null,
 | 
						|
 | 
						|
  updateControlState: function (aResult, aFindPrevious) {
 | 
						|
    this._findbar.updateControlState(aResult, aFindPrevious);
 | 
						|
  },
 | 
						|
 | 
						|
  addEventListener: function (aType, aListener, aUseCapture, aWantsUntrusted) {
 | 
						|
    this._findbar.addEventListener(aType, aListener, aUseCapture,
 | 
						|
                                   aWantsUntrusted);
 | 
						|
  },
 | 
						|
 | 
						|
  removeEventListener: function (aType, aListener, aUseCapture) {
 | 
						|
    this._findbar.removeEventListener(aType, aListener, aUseCapture);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
function PdfjsWindowWrapper(aBrowser) {
 | 
						|
  this._window = aBrowser.ownerDocument.defaultView;
 | 
						|
}
 | 
						|
 | 
						|
PdfjsWindowWrapper.prototype = {
 | 
						|
  __exposedProps__: {
 | 
						|
    valueOf: 'r',
 | 
						|
  },
 | 
						|
  _window: null,
 | 
						|
 | 
						|
  valueOf: function () {
 | 
						|
    return this._window.valueOf();
 | 
						|
  }
 | 
						|
};
 |