summaryrefslogtreecommitdiffstats
path: root/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm')
-rw-r--r--browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm357
1 files changed, 357 insertions, 0 deletions
diff --git a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
new file mode 100644
index 000000000..7a082b6af
--- /dev/null
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -0,0 +1,357 @@
+/* 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 */
+
+'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 = 'pdfjs';
+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 =
+{
+ "showPreviousViewOnLoad": true,
+ "defaultZoomValue": "",
+ "sidebarViewOnLoad": 0,
+ "enableHandToolOnLoad": false,
+ "enableWebGL": false,
+ "pdfBugEnabled": false,
+ "disableRange": false,
+ "disableStream": false,
+ "disableAutoFetch": false,
+ "disableFontFace": false,
+ "disableTextLayer": false,
+ "useOnlyCssZoom": false,
+ "externalLinkTarget": 0,
+ "enhanceTextSelection": false,
+ "renderInteractiveForms": false,
+ "disablePageLabels": false
+}
+
+
+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: function () {
+ 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);
+
+ // 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: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', 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;
+
+
+ case 'PDFJS:Parent:updateControlState':
+ return this._updateControlState(aMsg);
+ case 'PDFJS:Parent:addEventListener':
+ return this._addEventListener(aMsg);
+ case 'PDFJS:Parent:removeEventListener':
+ return this._removeEventListener(aMsg);
+ }
+ },
+
+ /*
+ * Internal
+ */
+
+ _findbarFromMessage: function(aMsg) {
+ let browser = aMsg.target;
+ let tabbrowser = browser.getTabBrowser();
+ let tab = tabbrowser.getTabForBrowser(browser);
+ return tabbrowser.getFindBar(tab);
+ },
+
+ _updateControlState: function (aMsg) {
+ let data = aMsg.data;
+ this._findbarFromMessage(aMsg)
+ .updateControlState(data.result, data.findPrevious);
+ },
+
+ handleEvent: function(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: type, detail: detail });
+ aEvent.preventDefault();
+ },
+
+ _types: ['find',
+ 'findagain',
+ 'findhighlightallchange',
+ 'findcasesensitivitychange'],
+
+ _addEventListener: function (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: function (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: 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 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: download });
+ }
+ let buttons = [{
+ label: data.label,
+ accessKey: data.accessKey,
+ callback: function() {
+ 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);
+ });
+ }
+};
+
+