diff --git a/extensions/chromium/pdfHandler.js b/extensions/chromium/pdfHandler.js index 6cb82ef57..56da078a4 100644 --- a/extensions/chromium/pdfHandler.js +++ b/extensions/chromium/pdfHandler.js @@ -220,3 +220,51 @@ chrome.webRequest.onBeforeRequest.addListener( types: ['main_frame', 'sub_frame'] }, ['blocking']); + +chrome.extension.isAllowedFileSchemeAccess(function(isAllowedAccess) { + if (isAllowedAccess) { + return; + } + // If the user has not granted access to file:-URLs, then the webRequest API + // will not catch the request. It is still visible through the webNavigation + // API though, and we can replace the tab with the viewer. + // The viewer will detect that it has no access to file:-URLs, and prompt the + // user to activate file permissions. + chrome.webNavigation.onBeforeNavigate.addListener(function(details) { + if (details.frameId === 0 && !isPdfDownloadable(details)) { + chrome.tabs.update(details.tabId, { + url: getViewerURL(details.url) + }); + } + }, { + url: [{ + urlPrefix: 'file://', + pathSuffix: '.pdf' + }, { + urlPrefix: 'file://', + pathSuffix: '.PDF' + }] + }); +}); + +chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { + if (message && message.action === 'isAllowedFileSchemeAccess') { + chrome.extension.isAllowedFileSchemeAccess(sendResponse); + return true; + } + if (message && message.action === 'openExtensionsPageForFileAccess') { + var url = 'chrome://extensions/?id=' + chrome.runtime.id; + if (message.data.newTab) { + chrome.tabs.create({ + windowId: sender.tab.windowId, + index: sender.tab.index + 1, + url: url, + openerTabId: sender.tab.id + }); + } else { + chrome.tabs.update(sender.tab.id, { + url: url + }); + } + } +}); diff --git a/web/chromecom.js b/web/chromecom.js index 3c4a17e3c..35e40a508 100644 --- a/web/chromecom.js +++ b/web/chromecom.js @@ -14,7 +14,7 @@ * limitations under the License. */ -/* globals chrome, PDFJS, PDFViewerApplication */ +/* globals chrome, PDFJS, PDFViewerApplication, OverlayManager */ 'use strict'; var ChromeCom = (function ChromeComClosure() { @@ -120,11 +120,89 @@ var ChromeCom = (function ChromeComClosure() { ' non-local page for security reasons.'); return; } + isAllowedFileSchemeAccess(function(isAllowedAccess) { + if (isAllowedAccess) { + PDFViewerApplication.open(file, 0); + } else { + requestAccessToLocalFile(file); + } + }); + return; } PDFViewerApplication.open(file, 0); }); }; + function isAllowedFileSchemeAccess(callback) { + ChromeCom.request('isAllowedFileSchemeAccess', null, callback); + } + + function isRuntimeAvailable() { + try { + // When the extension is reloaded, the extension runtime is destroyed and + // the extension APIs become unavailable. + if (chrome.runtime && chrome.runtime.getManifest()) { + return true; + } + } catch (e) {} + return false; + } + + function reloadIfRuntimeIsUnavailable() { + if (!isRuntimeAvailable()) { + location.reload(); + } + } + + var chromeFileAccessOverlayPromise; + function requestAccessToLocalFile(fileUrl) { + var onCloseOverlay = null; + if (top !== window) { + // When the extension reloads after receiving new permissions, the pages + // have to be reloaded to restore the extension runtime. Auto-reload + // frames, because users should not have to reload the whole page just to + // update the viewer. + // Top-level frames are closed by Chrome upon reload, so there is no need + // for detecting unload of the top-level frame. Should this ever change + // (crbug.com/511670), then the user can just reload the tab. + window.addEventListener('focus', reloadIfRuntimeIsUnavailable); + onCloseOverlay = function() { + window.removeEventListener('focus', reloadIfRuntimeIsUnavailable); + reloadIfRuntimeIsUnavailable(); + OverlayManager.close('chromeFileAccessOverlay'); + }; + } + if (!chromeFileAccessOverlayPromise) { + chromeFileAccessOverlayPromise = OverlayManager.register( + 'chromeFileAccessOverlay', onCloseOverlay, true); + } + chromeFileAccessOverlayPromise.then(function() { + var iconPath = chrome.runtime.getManifest().icons[48]; + document.getElementById('chrome-pdfjs-logo-bg').style.backgroundImage = + 'url(' + chrome.runtime.getURL(iconPath) + ')'; + + var link = document.getElementById('chrome-link-to-extensions-page'); + link.href = 'chrome://extensions/?id=' + chrome.runtime.id; + link.onclick = function(e) { + // Direct navigation to chrome:// URLs is blocked by Chrome, so we + // have to ask the background page to open chrome://extensions/?id=... + e.preventDefault(); + // Open in the current tab by default, because toggling the file access + // checkbox causes the extension to reload, and Chrome will close all + // tabs upon reload. + ChromeCom.request('openExtensionsPageForFileAccess', { + newTab: e.ctrlKey || e.metaKey || e.button === 1 || window !== top + }); + }; + + // Show which file is being opened to help the user with understanding + // why this permission request is shown. + document.getElementById('chrome-url-of-local-file').textContent = fileUrl; + + OverlayManager.open('chromeFileAccessOverlay'); + }); + } + // This port is used for several purposes: // 1. When disconnected, the background page knows that the frame has unload. // 2. When the referrer was saved in history.state.chromecomState, it is sent diff --git a/web/viewer-snippet-chrome-overlays.html b/web/viewer-snippet-chrome-overlays.html new file mode 100644 index 000000000..f4d23e8e8 --- /dev/null +++ b/web/viewer-snippet-chrome-overlays.html @@ -0,0 +1,24 @@ +
diff --git a/web/viewer.css b/web/viewer.css index 4602351a4..7489b9ebd 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -1371,6 +1371,10 @@ html[dir='rtl'] .attachmentsItem > button { vertical-align: middle; } +.dialog :link { + color: white; +} + #passwordOverlay > .dialog { text-align: center; } diff --git a/web/viewer.html b/web/viewer.html index e807f3386..4c420732d 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -388,6 +388,9 @@ See https://github.com/adobe-type-tools/cmap-resources + + +