diff --git a/extensions/chromium/chrome.tabs.executeScriptInFrame.js b/extensions/chromium/chrome.tabs.executeScriptInFrame.js new file mode 100644 index 000000000..83dff85b7 --- /dev/null +++ b/extensions/chromium/chrome.tabs.executeScriptInFrame.js @@ -0,0 +1,256 @@ +/** + * (c) 2013 Rob Wu + * Released under the MIT license + * https://github.com/Rob--W/chrome-api/chrome.tabs.executeScriptInFrame + * + * Implements the chrome.tabs.executeScriptInFrame API. + * This API is similar to the chrome.tabs.executeScript method, except + * that it also recognizes the "frameId" property. + * This frameId can be obtained through the webNavigation or webRequest API. + * + * When an error occurs, chrome.runtime.lastError is set. + * + * Required permissions: + * webRequest + * webRequestBlocking + * Host permissions for the tab + * + * In addition, the following field must also be set in manifest.json: + * "web_accessible_resources": ["getFrameId"] + */ + +(function() { +/* jshint browser:true, maxlen:100 */ +/* globals chrome, console */ +'use strict'; +chrome.tabs.executeScriptInFrame = executeScript; + +// This URL is used to communicate the frameId. The resource is never visited, so it should +// be a non-existent location. Do not use *, ", ' or line breaks in the file name. +var URL_WHAT_IS_MY_FRAME_ID = chrome.extension.getURL('getFrameId'); +// The callback will be called within ... ms: +// Don't set a too low value. +var MAXIMUM_RESPONSE_TIME_MS = 1000; + +// Callbacks are stored here until they're invoked. +// Key = dummyUrl, value = callback function +var callbacks = {}; + +chrome.webRequest.onBeforeRequest.addListener(function showFrameId(details) { + // Positive integer frameId >= 0 + // Since an image is used as a data transport, we add 1 to get a non-zero height. + var frameId = details.frameId + 1; + // Assume that the frameId fits in two bytes - which is a very reasonable assumption. + var width = String.fromCharCode(frameId & 0xFF, frameId & 0xFF00); + var height = '\x01\x00'; + // Convert data to base64 to avoid loss of bytes + var image = 'data:image/gif;base64,' + btoa( + // 4749 4638 3961 (GIF header) + 'GIF89a' + + // Logical Screen Width (LSB) + width + + // Logical Screen Height (LSB) + height + + // "No Global Color Table follows" + '\x00' + + // Background color + '\xff' + + // No aspect information is given + '\x00' + + // (image descriptor) + // Image Separator + '\x2c' + + // Image Position (Left & Top) + '\x00\x00\x00\x00' + + // Image Width (LSB) + width + + // Image Height (LSB) + height + + // Local Color Table is not present + '\x00' + + // (End of image descriptor) + // Image data + '\x02\x02\x44\x01\x00' + + // GIF trailer + '\x3b' + ); + return {redirectUrl: image}; +}, { + urls: [URL_WHAT_IS_MY_FRAME_ID + '*'], + types: ['image'] +}, ['blocking']); + +chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { + if (message && message.executeScriptCallback) { + var callback = callbacks[message.identifier]; + if (callback) { + if (message.hello) { + clearTimeout(callback.timer); + return; + } + delete callbacks[message.identifier]; + // Result within an array to be consistent with the chrome.tabs.executeScript API. + callback([message.evalResult]); + } else { + console.warn('Callback not found for response in tab ' + sender.tab.id); + } + } +}); + +/** + * Execute content script in a specific frame. + * + * @param tabId {integer} required + * @param details.frameId {integer} required + * @param details.code {string} Code or file is required (not both) + * @param details.file {string} Code or file is required (not both) + * @param details.runAt {optional string} One of "document_start", "document_end", "document_idle" + * @param callback {optional function(optional array of any result)} When an error occurs, result + * is not set. + */ +function executeScript(tabId, details, callback) { + console.assert(typeof details === 'object', 'details must be an object (argument 0)'); + var frameId = details.frameId; + console.assert(typeof tabId === 'number', 'details.tabId must be a number'); + console.assert(typeof frameId === 'number', 'details.frameId must be a number'); + var sourceType = 'code' in details ? 'code' : 'file'; + console.assert(sourceType in details, 'No source code or file specified'); + var sourceValue = details[sourceType]; + console.assert(typeof sourceValue === 'string', 'details.' + sourceType + ' must be a string'); + var runAt = details.runAt; + if (!callback) callback = function() {/* no-op*/}; + console.assert(typeof callback === 'function', 'callback must be a function'); + + if (frameId === 0) { + // No need for heavy lifting if we want to inject the script in the main frame + var injectDetails = { + allFrames: false, + runAt: runAt + }; + injectDetails[sourceType] = sourceValue; + chrome.tabs.executeScript(tabId, injectDetails, callback); + return; + } + + var identifier = Math.random().toString(36); + + if (sourceType === 'code') { + executeScriptInFrame(); + } else { // sourceType === 'file' + (function() { + var x = new XMLHttpRequest(); + x.open('GET', chrome.extension.getURL(sourceValue), true); + x.onload = function() { + sourceValue = x.responseText; + executeScriptInFrame(); + }; + x.onerror = function executeScriptResourceFetchError() { + var message = 'Failed to load file: "' + sourceValue + '".'; + console.error('executeScript: ' + message); + chrome.runtime.lastError = chrome.extension.lastError = { message: message }; + try { + callback(); + } finally { + chrome.runtime.lastError = chrome.extension.lastError = undefined; + } + }; + x.send(); + })(); + } + + + function executeScriptInFrame() { + callbacks[identifier] = callback; + chrome.tabs.executeScript(tabId, { + code: '(' + DETECT_FRAME + ')(' + + 'window,' + + JSON.stringify(identifier) + ',' + + frameId + ',' + + JSON.stringify(sourceValue) + ')', + allFrames: true, + runAt: 'document_start' + }, function(results) { + if (results) { + callback.timer = setTimeout(executeScriptTimedOut, MAXIMUM_RESPONSE_TIME_MS); + } else { + // Failed :( + delete callbacks[identifier]; + callback(); + } + }); + } + function executeScriptTimedOut() { + var callback = callbacks[identifier]; + if (!callback) { + return; + } + delete callbacks[identifier]; + var message = 'Failed to execute script: Frame ' + frameId + ' not found in tab ' + tabId; + console.error('executeScript: ' + message); + chrome.runtime.lastError = chrome.extension.lastError = { message: message }; + try { + callback(); + } finally { + chrome.runtime.lastError = chrome.extension.lastError = undefined; + } + } +} + +/** + * Code executed as a content script. + */ +var DETECT_FRAME = '' + function checkFrame(window, identifier, frameId, code) { + var i; + if ('__executeScript_frameId__' in window) { + evalAsContentScript(); + } else { + // Do NOT use new Image(), because of http://crbug.com/245296 in Chrome 27-29 + i = window.document.createElement('img'); + i.onload = function() { + window.__executeScript_frameId__ = this.naturalWidth - 1; + evalAsContentScript(); + }; + // Trigger webRequest event to get frameId + // (append extra characters to bust the cache) + i.src = 'URL_WHAT_IS_MY_FRAME_ID?' + Math.random().toString(36).slice(-6); + } + + for (i = 0 ; i < window.frames.length; ++i) { + try { + var frame = window.frames[i]; + var scheme = frame.location.protocol; + if (scheme !== 'https:' && scheme !== 'http:' && scheme !== 'file:') { + checkFrame(frame, identifier, frameId, code); + } + } catch (e) { + // blocked by same origin policy, so it's not a javascript: / about:blank + // URL. chrome.tabs.executeScript will run the script for the frame. + } + } + function evalAsContentScript() { + if (window.__executeScript_frameId__ !== frameId) { + return; + } + // Send an early message to make sure that any blocking code + // in the evaluated code does not cause the time-out in the background page + // to be triggered + chrome.runtime.sendMessage({ + executeScriptCallback: true, + hello: true, + identifier: identifier + }); + var result = null; + try { + // jshint evil:true + result = window.eval(code); + } finally { + chrome.runtime.sendMessage({ + executeScriptCallback: true, + evalResult: result, + identifier: identifier + }); + } + } +}.toString().replace('URL_WHAT_IS_MY_FRAME_ID', URL_WHAT_IS_MY_FRAME_ID); + +})(); diff --git a/extensions/chromium/extension-router.js b/extensions/chromium/extension-router.js new file mode 100644 index 000000000..6ee6c29fb --- /dev/null +++ b/extensions/chromium/extension-router.js @@ -0,0 +1,54 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* +Copyright 2013 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. +*/ +/* globals chrome */ + +'use strict'; +(function ExtensionRouterClosure() { + var VIEWER_URL = chrome.extension.getURL('content/web/viewer.html'); + var CRX_BASE_URL = chrome.extension.getURL('/'); + + // TODO(rob): Use declarativeWebRequest once declared URL-encoding is + // supported, see http://crbug.com/273589 + // (or rewrite the query string parser in viewer.js to get it to + // recognize the non-URL-encoded PDF URL.) + chrome.webRequest.onBeforeRequest.addListener(function(details) { + // This listener converts chrome-extension://.../http://...pdf to + // chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf + var url = details.url.substring(CRX_BASE_URL.length); + var matchingUrl = /^(?:https?|file|ftp|chrome-extension)(:|%3A)/.exec(url); + if (matchingUrl) { + // location.hash is restored when "#" is missing from URL. + url = url.split('#')[0]; + if (matchingUrl[1] === ':') { + url = encodeURIComponent(url); + } + url = VIEWER_URL + '?file=' + url; + console.log('Redirecting ' + details.url + ' to ' + url); + return { redirectUrl: url }; + } + }, { + types: ['main_frame', 'sub_frame'], + urls: [ + CRX_BASE_URL + 'http*', // and https + CRX_BASE_URL + 'file*', + CRX_BASE_URL + 'ftp*', + CRX_BASE_URL + 'chrome-extension*' + ] + }, ['blocking']); + console.log('Set up extension URL router.'); +})(); diff --git a/extensions/chromium/hide-xhtml-error.css b/extensions/chromium/hide-xhtml-error.css deleted file mode 100644 index b917c6b8c..000000000 --- a/extensions/chromium/hide-xhtml-error.css +++ /dev/null @@ -1,3 +0,0 @@ -parsererror { - display: none; -} diff --git a/extensions/chromium/insertviewer.js b/extensions/chromium/insertviewer.js deleted file mode 100644 index 957c21eb6..000000000 --- a/extensions/chromium/insertviewer.js +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- 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. -*/ -/* globals chrome */ - -'use strict'; - -if (!chrome.runtime) { - // Chrome 21- - chrome.runtime = chrome.extension; -} else if (!chrome.runtime.onMessage) { - // Chrome 22-25 - chrome.runtime.onMessage = chrome.extension.onMessage; -} - -var VIEWER_URL = chrome.runtime.getURL('content/web/viewer.html'); -var BASE_URL = VIEWER_URL.replace(/[^\/]+$/, ''); - -function getViewerURL(pdf_url) { - return VIEWER_URL + '?file=' + encodeURIComponent(pdf_url); -} - -function showViewer(url) { - if (document.documentElement === null) { - // If the root element hasn't been rendered yet, delay the next operation. - // Otherwise, document.readyState will get stuck in "interactive". - setTimeout(showViewer, 0, url); - return; - } - // Cancel page load and empty document. - window.stop(); - document.body.textContent = ''; - - replaceDocumentWithViewer(url); -} -function makeLinksAbsolute(doc) { - normalize('href', 'link[href]'); - normalize('src', 'style[src],script[src]'); - - function normalize(attribute, selector) { - var nodes = doc.querySelectorAll(selector); - for (var i=0; i elements (added back later). - // I assumed that no inline script tags exist. - var scripts = [], script; - - // new Worker('chrome-extension://..../pdf.js') fails, despite having - // the correct permissions. Fix it: - script = document.createElement('script'); - script.onload = loadNextScript; - script.src = chrome.runtime.getURL('patch-worker.js'); - scripts.push(script); - - while (x.response.scripts.length) { - script = x.response.scripts[0]; - var newScript = document.createElement('script'); - newScript.onload = loadNextScript; - newScript.src = script.src; - script.parentNode.removeChild(script); - scripts.push(newScript); - } - - // Replace document with viewer - var docEl = document.adoptNode(x.response.documentElement); - document.replaceChild(docEl, document.documentElement); - // Force Chrome to render content - // (without this line, the layout is broken and querySelector - // fails to find elements, even when they appear in the doc) - document.body.innerHTML += ''; - - // Load all scripts - loadNextScript(); - - function loadNextScript() { - if (scripts.length > 0) - document.head.appendChild(scripts.shift()); - else - renderPDF(url); - } - }; - x.send(); -} -function renderPDF(url) { - var args = { - BASE_URL: BASE_URL, - pdf_url: url - }; - // The following technique is explained at - // http://stackoverflow.com/a/9517879/938089 - var script = document.createElement('script'); - script.textContent = - '(function(args) {' + - ' PDFJS.imageResourcesPath = args.BASE_URL + PDFJS.imageResourcesPath;' + - ' PDFJS.workerSrc = args.BASE_URL + PDFJS.workerSrc;' + - ' window.DEFAULT_URL = args.pdf_url;' + - '})(' + JSON.stringify(args) + ');'; - document.head.appendChild(script); - - // Trigger domready - if (document.readyState === 'complete') { - var event = document.createEvent('Event'); - event.initEvent('DOMContentLoaded', true, true); - document.dispatchEvent(event); - } -} - - -// Activate the content script only once per frame (until reload) -if (!window.hasRun) { - window.hasRun = true; - chrome.runtime.onMessage.addListener(function listener(message) { - if (message && message.type === 'showPDFViewer' && - message.url === location.href) { - chrome.runtime.onMessage.removeListener(listener); - showViewer(message.url); - } - }); -} diff --git a/extensions/chromium/manifest.json b/extensions/chromium/manifest.json index 83acfc501..b30aa40d5 100644 --- a/extensions/chromium/manifest.json +++ b/extensions/chromium/manifest.json @@ -15,17 +15,20 @@ "webNavigation" ], "content_scripts": [{ - "matches": [ - "*://*/*.pdf*", - "*://*/*.PDF*" - ], - "css": ["hide-xhtml-error.css"] + "matches": ["file://*/*"], + "js": ["nothing.js"] }], "background": { "page": "pdfHandler.html" }, + "incognito": "split", "web_accessible_resources": [ - "patch-worker.js", - "content/*" + "getFrameId", + "content/web/viewer.html", + "http:/*", + "https:/*", + "ftp:/*", + "file:/*", + "chrome-extension:/*" ] } diff --git a/extensions/chromium/nothing.js b/extensions/chromium/nothing.js new file mode 100644 index 000000000..ce8f90c40 --- /dev/null +++ b/extensions/chromium/nothing.js @@ -0,0 +1 @@ +// This file has no code, and is used to deal with http://crbug.com/302548 diff --git a/extensions/chromium/pdfHandler-local.js b/extensions/chromium/pdfHandler-local.js deleted file mode 100644 index c0ebe0b1d..000000000 --- a/extensions/chromium/pdfHandler-local.js +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- 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. -*/ -/* globals chrome, isPdfDownloadable */ - -'use strict'; - -// The onHeadersReceived event is not generated for local resources. -// Fortunately, local PDF files will have the .pdf extension, so there's -// no need to detect the Content-Type -// Unfortunately, the omnibox won't show the URL. -// Unfortunately, this method will not work for pages in incognito mode, -// unless "incognito":"split" is used AND http:/crbug.com/224094 is fixed. - -// Keeping track of incognito tab IDs will become obsolete when -// "incognito":"split" can be used. -var incognitoTabIds = []; -chrome.windows.getAll({ populate: true }, function(windows) { - windows.forEach(function(win) { - if (win.incognito) { - win.tabs.forEach(function(tab) { - incognitoTabIds.push(tab.id); - }); - } - }); -}); -chrome.tabs.onCreated.addListener(function(tab) { - if (tab.incognito) incognitoTabIds.push(tab.id); -}); -chrome.tabs.onRemoved.addListener(function(tabId) { - var index = incognitoTabIds.indexOf(tabId); - if (index !== -1) incognitoTabIds.splice(index, 1); -}); - -chrome.webRequest.onBeforeRequest.addListener( - function(details) { - if (isPdfDownloadable(details)) // Defined in pdfHandler.js - return; - - if (incognitoTabIds.indexOf(details.tabId) !== -1) - return; // Doesn't work in incognito mode, so don't redirect. - - var viewerPage = 'content/web/viewer.html'; - var url = chrome.runtime.getURL(viewerPage) + - '?file=' + encodeURIComponent(details.url); - return { redirectUrl: url }; - }, - { - urls: [ - 'file://*/*.pdf', - 'file://*/*.PDF' - ], - types: ['main_frame', 'sub_frame'] - }, - ['blocking']); diff --git a/extensions/chromium/pdfHandler.html b/extensions/chromium/pdfHandler.html index 821f4c884..dcb70cb31 100644 --- a/extensions/chromium/pdfHandler.html +++ b/extensions/chromium/pdfHandler.html @@ -14,5 +14,6 @@ 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. --> + - + diff --git a/extensions/chromium/pdfHandler.js b/extensions/chromium/pdfHandler.js index 4956c35a8..c61373d28 100644 --- a/extensions/chromium/pdfHandler.js +++ b/extensions/chromium/pdfHandler.js @@ -19,6 +19,12 @@ limitations under the License. 'use strict'; +var VIEWER_URL = chrome.extension.getURL('content/web/viewer.html'); + +function getViewerURL(pdf_url) { + return VIEWER_URL + '?file=' + encodeURIComponent(pdf_url); +} + /** * @param {Object} details First argument of the webRequest.onHeadersReceived * event. The property "url" is read. @@ -36,62 +42,6 @@ function isPdfDownloadable(details) { return cdHeader && /^attachment/i.test(cdHeader.value); } -/** - * Insert the content script in a tab which renders the PDF viewer. - * @param {number} tabId ID of the tab used by the Chrome APIs. - * @param {string} url URL of the PDF file. Used to detect whether the viewer - * should be activated in a specific (i)frame. - */ -function insertPDFJSForTab(tabId, url) { - chrome.tabs.executeScript(tabId, { - file: 'insertviewer.js', - allFrames: true, - runAt: 'document_start' - }, function() { - chrome.tabs.sendMessage(tabId, { - type: 'showPDFViewer', - url: url - }); - }); -} - -/** - * Try to render the PDF viewer when (a frame within) a tab unloads. - * This indicates that a PDF file may be loading. - * @param {number} tabId ID of the tab used by the Chrome APIs. - * @param {string} url The URL of the pdf file. - */ -function activatePDFJSForTab(tabId, url) { - if (!chrome.webNavigation) { - // Opera... does not support the webNavigation API. - activatePDFJSForTabFallbackForOpera(tabId, url); - return; - } - var listener = function webNavigationEventListener(details) { - if (details.tabId === tabId) { - insertPDFJSForTab(tabId, url); - chrome.webNavigation.onCommitted.removeListener(listener); - } - }; - var urlFilter = { - url: [{ urlEquals: url.split('#', 1)[0] }] - }; - chrome.webNavigation.onCommitted.addListener(listener, urlFilter); -} - -/** - * Fallback for Opera. - * @see activatePDFJSForTab - **/ -function activatePDFJSForTabFallbackForOpera(tabId, url) { - chrome.tabs.onUpdated.addListener(function listener(_tabId) { - if (tabId === _tabId) { - insertPDFJSForTab(tabId, url); - chrome.tabs.onUpdated.removeListener(listener); - } - }); -} - /** * Get the header from the list of headers for a given name. * @param {Array} headers responseHeaders of webRequest.onHeadersReceived @@ -159,23 +109,58 @@ chrome.webRequest.onHeadersReceived.addListener( return getHeadersWithContentDispositionAttachment(details); } - // Replace frame's content with the PDF viewer. - // This approach maintains the friendly URL in the location bar. - activatePDFJSForTab(details.tabId, details.url); + var viewerUrl = getViewerURL(details.url); - return { - responseHeaders: [ - // Set Cache-Control header to avoid downloading a file twice - // NOTE: This does not behave as desired, Chrome's network stack is - // oblivious for Cache control header modifications. - {name:'Cache-Control',value:'max-age=600'}, - // Temporary render response as XHTML. - // Since PDFs are never valid XHTML, the garbage is not going to be - // rendered. insertviewer.js will quickly replace the document with - // the PDF.js viewer. - {name:'Content-Type',value:'application/xhtml+xml; charset=US-ASCII'}, - ] - }; + // Replace frame with viewer + // TODO: When http://crbug.com/280464 is fixed, use + // return { redirectUrl: viewerUrl }; + + if (details.frameId === 0) { + // Main frame. Just replace the tab and be done! + chrome.tabs.update(details.tabId, { + url: viewerUrl + }); + return { cancel: true }; + } else { + // Sub frame. Requires some more work... + // The navigation will be cancelled at the end of the webRequest cycle. + chrome.webNavigation.onErrorOccurred.addListener(function listener(nav) { + if (nav.tabId !== details.tabId || nav.frameId !== details.frameId) { + return; + } + chrome.webNavigation.onErrorOccurred.removeListener(listener); + + // Locate frame and insert viewer + chrome.tabs.executeScriptInFrame(details.tabId, { + frameId: details.frameId, + code: 'location.href = ' + JSON.stringify(viewerUrl) + ';' + }, function(result) { + if (!result) { + console.warn('Frame not found! Opening viewer in new tab...'); + chrome.tabs.create({ + url: viewerUrl + }); + } + }); + }, { + url: [{ urlEquals: details.url.split('#', 1)[0] }] + }); + // Prevent frame from rendering by using X-Frame-Options. + // Do not use { cancel: true }, because that makes the frame inaccessible + // to the content script that has to replace the frame's URL. + return { + responseHeaders: [{ + name: 'X-Content-Type-Options', + value: 'nosniff' + }, { + name: 'X-Frame-Options', + value: 'deny' + }] + }; + } + + // Immediately abort the request, because the frame that initiated the + // request will be replaced with the PDF Viewer (within a split second). }, { urls: [ @@ -184,3 +169,24 @@ chrome.webRequest.onHeadersReceived.addListener( types: ['main_frame', 'sub_frame'] }, ['blocking','responseHeaders']); + +chrome.webRequest.onBeforeRequest.addListener( + function(details) { + if (isPdfDownloadable(details)) + return; + + // NOTE: The manifest file has declared an empty content script + // at file://*/* to make sure that the viewer can load the PDF file + // through XMLHttpRequest. Necessary to deal with http://crbug.com/302548 + var viewerUrl = getViewerURL(details.url); + + return { redirectUrl: viewerUrl }; + }, + { + urls: [ + 'file://*/*.pdf', + 'file://*/*.PDF' + ], + types: ['main_frame', 'sub_frame'] + }, + ['blocking']); diff --git a/web/viewer-snippet-chrome-extension.html b/web/viewer-snippet-chrome-extension.html new file mode 100644 index 000000000..e97f2f62c --- /dev/null +++ b/web/viewer-snippet-chrome-extension.html @@ -0,0 +1,5 @@ + + + + + diff --git a/web/viewer.html b/web/viewer.html index 3eb22ab06..6bac6b583 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -25,6 +25,9 @@ limitations under the License. + + + @@ -53,7 +56,7 @@ limitations under the License. - + diff --git a/web/viewer.js b/web/viewer.js index cbf166cfc..b2a1df078 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -670,15 +670,19 @@ var PDFView = { }, /** - * For the firefox extension we prefix the full url on anchor links so they - * don't come up as resource:// urls and so open in new tab/window works. - * @param {String} anchor The anchor hash include the #. + * Prefix the full url on anchor links to make sure that links are resolved + * relative to the current URL instead of the one defined in . + * @param {String} anchor The anchor hash, including the #. */ getAnchorUrl: function getAnchorUrl(anchor) { -//#if !(FIREFOX || MOZCENTRAL) +//#if !(FIREFOX || MOZCENTRAL || CHROME) return anchor; //#else +//#if CHROME +// return location.href.split('#')[0] + anchor; +//#else // return this.url.split('#')[0] + anchor; +//#endif //#endif }, @@ -1459,15 +1463,36 @@ var DocumentOutlineView = function documentOutlineView(outline) { } }; +//#if CHROME +//(function rewriteUrlClosure() { +// // Run this code outside DOMContentLoaded to make sure that the URL +// // is rewritten as soon as possible. +// if (location.origin + '/' !== chrome.extension.getURL('/')) { +// DEFAULT_URL = window.location.href.split('#')[0]; +// } else { +// var params = PDFView.parseQueryString(document.location.search.slice(1)); +// DEFAULT_URL = params.file || DEFAULT_URL; +// +// // Example: chrome-extension://.../http://example.com/file.pdf +// var humanReadableUrl = '/' + DEFAULT_URL + location.hash; +// history.replaceState(history.state, '', humanReadableUrl); +// } +//})(); +//#endif + document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { PDFView.initialize(); -//#if !(FIREFOX || MOZCENTRAL) +//#if !(FIREFOX || MOZCENTRAL || CHROME) var params = PDFView.parseQueryString(document.location.search.substring(1)); var file = params.file || DEFAULT_URL; //#else +//#if CHROME +//var file = DEFAULT_URL; +//#else //var file = window.location.href.split('#')[0]; //#endif +//#endif //#if CHROME //if (location.protocol !== 'chrome-extension:') {