From 3d7f01d9ca9cc919c764a4574bb756b41d1abb67 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 31 May 2012 11:16:06 -0700 Subject: [PATCH] Add global pref to enable/disable. Control pdf.js in application preferences. Add Artur's mochi tests. --- extensions/firefox/.gitignore | 1 - extensions/firefox/chrome-mozcentral.manifest | 3 +- .../firefox/components/PdfStreamConverter.js | 23 +++- extensions/firefox/content/PdfJs.jsm | 105 ++++++++++++++++++ make.js | 4 + test/mozcentral/Makefile.in | 1 + test/mozcentral/browser_pdfjs_main.js | 67 ++++++++++- test/mozcentral/browser_pdfjs_savedialog.js | 54 +++++++++ 8 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 extensions/firefox/content/PdfJs.jsm create mode 100644 test/mozcentral/browser_pdfjs_savedialog.js diff --git a/extensions/firefox/.gitignore b/extensions/firefox/.gitignore index 6eec9a7f2..c64b8b910 100644 --- a/extensions/firefox/.gitignore +++ b/extensions/firefox/.gitignore @@ -1,4 +1,3 @@ -content/ metadata.inc chrome.manifest.inc locale/ diff --git a/extensions/firefox/chrome-mozcentral.manifest b/extensions/firefox/chrome-mozcentral.manifest index a2a6757c3..1c7abc845 100644 --- a/extensions/firefox/chrome-mozcentral.manifest +++ b/extensions/firefox/chrome-mozcentral.manifest @@ -1,3 +1,2 @@ resource pdf.js content/ -component {d0c5195d-e798-49d4-b1d3-9324328b2291} components/PdfStreamConverter.js -contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {d0c5195d-e798-49d4-b1d3-9324328b2291} +resource pdf.js.components components/ diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 62d22162b..dd8f2c190 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -9,6 +9,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; +const MOZ_CENTRAL = PDFJSSCRIPT_MOZ_CENTRAL; const PDFJS_EVENT_ID = 'pdf.js.message'; const PDF_CONTENT_TYPE = 'application/pdf'; const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX'; @@ -19,11 +20,12 @@ const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/NetUtil.jsm'); -Cu.import('resource://gre/modules/AddonManager.jsm'); let appInfo = Cc['@mozilla.org/xre/app-info;1'] .getService(Ci.nsIXULAppInfo); let privateBrowsing, inPrivateBrowsing; +let mimeService = Cc['@mozilla.org/mime;1'] + .getService(Ci.nsIMIMEService); if (appInfo.ID === FIREFOX_ID) { privateBrowsing = Cc['@mozilla.org/privatebrowsing;1'] @@ -71,6 +73,23 @@ function getDOMWindow(aChannel) { return win; } +function isEnabled() { + if (MOZ_CENTRAL) { + var enabled = getBoolPref(PREF_PREFIX + '.enabled', false); + if (!enabled) + return false; + // To also be considered enabled the "Preview in Firefox" option must be + // selected in the Application preferences. + var handlerInfo = mimeService. + getFromTypeAndExtension('application/pdf', 'pdf'); + return handlerInfo && (handlerInfo.alwaysAskBeforeHandling == false && + handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally); + } + // Always returns true for the extension since enabling/disabling is handled + // by the add-on manager. + return true; +} + function getLocalizedStrings(path) { var stringBundle = Cc['@mozilla.org/intl/stringbundle;1']. getService(Ci.nsIStringBundleService). @@ -248,6 +267,8 @@ PdfStreamConverter.prototype = { // nsIStreamConverter::asyncConvertData asyncConvertData: function(aFromType, aToType, aListener, aCtxt) { + if (!isEnabled()) + throw Cr.NS_ERROR_NOT_IMPLEMENTED; // Ignoring HTTP POST requests -- pdf.js has to repeat the request. var skipConversion = false; try { diff --git a/extensions/firefox/content/PdfJs.jsm b/extensions/firefox/content/PdfJs.jsm new file mode 100644 index 000000000..2b26fce6f --- /dev/null +++ b/extensions/firefox/content/PdfJs.jsm @@ -0,0 +1,105 @@ +var EXPORTED_SYMBOLS = ["PdfJs"]; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cm = Components.manager; +const Cu = Components.utils; + +const PREF_PREFIX = 'pdfjs'; +const PREF_ENABLED = PREF_PREFIX + '.enabled'; +const PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged'; + +Cu.import('resource://gre/modules/Services.jsm'); +Cu.import('resource://pdf.js.components/PdfStreamConverter.js'); + +let mimeService = Cc["@mozilla.org/mime;1"] + .getService(Ci.nsIMIMEService); + +function getBoolPref(pref, def) { + try { + return Services.prefs.getBoolPref(pref); + } catch (ex) { + return def; + } +} + +// Register/unregister a class as a component. +let Factory = { + registrar: null, + aClass: null, + register: function(aClass) { + if (this.aClass) { + dump('Cannot register more than one class'); + return; + } + this.registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + this.aClass = aClass; + var proto = aClass.prototype; + this.registrar.registerFactory(proto.classID, proto.classDescription, + proto.contractID, this); + }, + unregister: function() { + if (!this.aClass) { + dump('Class was never registered.'); + return; + } + var proto = this.aClass.prototype; + this.registrar.unregisterFactory(proto.classID, this); + this.aClass = null; + }, + // nsIFactory::createInstance + createInstance: function(outer, iid) { + if (outer !== null) + throw Cr.NS_ERROR_NO_AGGREGATION; + return (new (this.aClass)).QueryInterface(iid); + } +}; + +let PdfJs = { + _registered: false, + init: function() { + if (this.enabled) + this._register(); + else + this._unregister(); + + // Listen for when pdf.js is completely disabled or a different pdf handler + // is chosen. + Services.prefs.addObserver(PREF_ENABLED, this, false); + Services.obs.addObserver(this, PDFJS_HANDLER_CHANGED, false); + }, + observe: function(subject, topic, data) { + if (topic != 'nsPref:changed' && topic != PDFJS_HANDLER_CHANGED) + return; + + if (this.enabled) + this._register(); + else + this._unregister(); + }, + // pdf.js is only enabled if we're both selected as the pdf viewer and if the + // global switch enabling us is true. + get enabled() { + var handlerInfo = mimeService. + getFromTypeAndExtension('application/pdf', 'pdf'); + + var selectedAsHandler = handlerInfo && (handlerInfo.alwaysAskBeforeHandling == false && + handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally); + return getBoolPref(PREF_ENABLED, false) && selectedAsHandler; + }, + _register: function() { + if (this._registered) + return; + + Factory.register(PdfStreamConverter); + this._registered = true; + }, + _unregister: function() { + if (!this._registered) + return; + + Factory.unregister(); + this._registered = false; + } +}; diff --git a/make.js b/make.js index 227abed4b..62881ae19 100755 --- a/make.js +++ b/make.js @@ -358,6 +358,7 @@ target.firefox = function() { sed('-i', /PDFJSSCRIPT_STREAM_CONVERTER_ID/, FIREFOX_STREAM_CONVERTER_ID, FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js'); sed('-i', /PDFJSSCRIPT_PREF_PREFIX/, FIREFOX_PREF_PREFIX, FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js'); + sed('-i', /PDFJSSCRIPT_MOZ_CENTRAL/, 'false', FIREFOX_BUILD_DIR + 'components/PdfStreamConverter.js'); // Update localized metadata var localizedMetadata = cat(EXTENSION_SRC_DIR + '/firefox/metadata.inc'); @@ -421,6 +422,8 @@ target.mozcentral = function() { mkdir('-p', MOZCENTRAL_CONTENT_DIR + BUILD_DIR); mkdir('-p', MOZCENTRAL_CONTENT_DIR + '/web'); + cp(FIREFOX_CONTENT_DIR + 'PdfJs.jsm', MOZCENTRAL_CONTENT_DIR) + // Copy extension files cd('extensions/firefox'); cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + MOZCENTRAL_EXTENSION_DIR); @@ -461,6 +464,7 @@ target.mozcentral = function() { sed('-i', /PDFJSSCRIPT_STREAM_CONVERTER_ID/, MOZCENTRAL_STREAM_CONVERTER_ID, MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js'); sed('-i', /PDFJSSCRIPT_PREF_PREFIX/, MOZCENTRAL_PREF_PREFIX, MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js'); + sed('-i', /PDFJSSCRIPT_MOZ_CENTRAL/, 'true', MOZCENTRAL_EXTENSION_DIR + 'components/PdfStreamConverter.js'); // List all files for mozilla-central cd(MOZCENTRAL_EXTENSION_DIR); diff --git a/test/mozcentral/Makefile.in b/test/mozcentral/Makefile.in index 77b7a838d..93ec187e2 100644 --- a/test/mozcentral/Makefile.in +++ b/test/mozcentral/Makefile.in @@ -13,6 +13,7 @@ include $(topsrcdir)/config/rules.mk _BROWSER_TEST_FILES = \ browser_pdfjs_main.js \ + browser_pdfjs_savedialog.js \ file_pdfjs_test.pdf \ $(NULL) diff --git a/test/mozcentral/browser_pdfjs_main.js b/test/mozcentral/browser_pdfjs_main.js index 884650892..23229914b 100644 --- a/test/mozcentral/browser_pdfjs_main.js +++ b/test/mozcentral/browser_pdfjs_main.js @@ -6,7 +6,17 @@ const RELATIVE_DIR = "browser/extensions/pdfjs/test/"; const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; function test() { - var tab; + var tab, oldAction; + + oldAction = changeMimeHandler(); + + const Cc = Components.classes; + const Ci = Components.interfaces; + let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); + + info('Pref action: ' + handlerInfo.preferredAction); waitForExplicitFinish(); registerCleanupFunction(function() { @@ -23,13 +33,62 @@ function test() { // Runs tests after all 'load' event handlers have fired off setTimeout(function() { - runTests(document, window); + runTests(document, window, function() { + revertMimeHandler(oldAction); + finish(); + }); }, 0); }, true); } -function runTests(document, window) { +function changeMimeHandler() { + let oldAction; + + const Cc = Components.classes; + const Ci = Components.interfaces; + let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); + + oldAction = handlerInfo.preferredAction; + + // Change and save mime handler settings + handlerInfo.alwaysAskBeforeHandling = false; + handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally; + handlerService.store(handlerInfo); + + Services.obs.notifyObservers(null, 'pdfjs:handlerChanged', null); + + // Refresh data + mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); + + // + // Test: Mime handler was updated + // + is(handlerInfo.alwaysAskBeforeHandling, false, 'always-ask prompt change successful'); + is(handlerInfo.preferredAction, Ci.nsIHandlerInfo.handleInternally, 'mime handler change successful'); + + return oldAction; +} + +function revertMimeHandler(oldAction) { + const Cc = Components.classes; + const Ci = Components.interfaces; + let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); + + // Change and save mime handler settings + handlerInfo.alwaysAskBeforeHandling = true; + handlerInfo.preferredAction = oldAction; + handlerService.store(handlerInfo); +} + + +function runTests(document, window, callback) { + // // Overall sanity tests // @@ -67,5 +126,5 @@ function runTests(document, window) { viewBookmark.click(); ok(viewBookmark.href.length > 0, 'viewBookmark button has href'); - finish(); + callback(); } diff --git a/test/mozcentral/browser_pdfjs_savedialog.js b/test/mozcentral/browser_pdfjs_savedialog.js new file mode 100644 index 000000000..1014473f0 --- /dev/null +++ b/test/mozcentral/browser_pdfjs_savedialog.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const RELATIVE_DIR = "browser/extensions/pdfjs/test/"; +const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; + +function test() { + var tab; + + const Cc = Components.classes; + const Ci = Components.interfaces; + let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); + let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); + + // + // Test: Default mime handler + // + is(handlerInfo.alwaysAskBeforeHandling, true, 'mime handler: default is always-ask prompt'); + + // + // Test: "Open with" dialog comes up + // + addWindowListener('chrome://mozapps/content/downloads/unknownContentType.xul', finish); + + waitForExplicitFinish(); + registerCleanupFunction(function() { + gBrowser.removeTab(tab); + }); + + tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); + var newTabBrowser = gBrowser.getBrowserForTab(tab); +} + + +function addWindowListener(aURL, aCallback) { + Services.wm.addListener({ + onOpenWindow: function(aXULWindow) { + info("window opened, waiting for focus"); + Services.wm.removeListener(this); + + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + is(domwindow.document.location.href, aURL, "should have seen the right window open"); + domwindow.close(); + aCallback(); + }, domwindow); + }, + onCloseWindow: function(aXULWindow) { }, + onWindowTitleChange: function(aXULWindow, aNewTitle) { } + }); +}