diff --git a/extensions/chromium/feature-detect.js b/extensions/chromium/feature-detect.js new file mode 100644 index 000000000..121672c0a --- /dev/null +++ b/extensions/chromium/feature-detect.js @@ -0,0 +1,121 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* +Copyright 2014 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'; + +var Features = { + featureDetectLastUA: '', + // Whether ftp: in XMLHttpRequest is allowed + extensionSupportsFTP: false, + // Whether redirectUrl at onHeadersReceived is supported. + webRequestRedirectUrl: false, +}; + +chrome.storage.local.get(Features, function(features) { + Features = features; + if (features.featureDetectLastUA === navigator.userAgent) { + // Browser not upgraded, so the features did probably not change. + return; + } + var inconclusiveTestCount = 0; + + if (!features.extensionSupportsFTP) { + features.extensionSupportsFTP = featureTestFTP(); + } + + if (!features.webRequestRedirectUrl) { + ++inconclusiveTestCount; + // Relatively expensive (and asynchronous) test: + featureTestRedirectOnHeadersReceived(function(result) { + // result = 'yes', 'no' or 'maybe'. + if (result !== 'maybe') { + --inconclusiveTestCount; + } + features.webRequestRedirectUrl = result === 'yes'; + checkTestCompletion(); + }); + } + + checkTestCompletion(); + + function checkTestCompletion() { + // Only stamp the feature detection results when all tests have finished. + if (inconclusiveTestCount === 0) { + Features.featureDetectLastUA = navigator.userAgent; + } + chrome.storage.local.set(Features); + } +}); + +// Tests whether the extension can perform a FTP request. +// Feature is supported since Chromium 35.0.1888.0 (r256810). +function featureTestFTP() { + var x = new XMLHttpRequest(); + // The URL does not need to exist, as long as the scheme is ftp:. + x.open('GET', 'ftp://ftp.mozilla.org/'); + try { + x.send(); + // Previous call did not throw error, so the feature is supported! + // Immediately abort the request so that the network is not hit at all. + x.abort(); + return true; + } catch (e) { + return false; + } +} + +// Tests whether redirectUrl at the onHeadersReceived stage is functional. +// Feature is supported since Chromium 35.0.1911.0 (r259546). +function featureTestRedirectOnHeadersReceived(callback) { + // The following URL is really going to be accessed via the network. + // It is the only way to feature-detect this feature, because the + // onHeadersReceived event is only triggered for http(s) requests. + var url = 'http://example.com/?feature-detect-' + chrome.runtime.id; + function onHeadersReceived(details) { + // If supported, the request is redirected. + // If not supported, the return value is ignored. + return { + redirectUrl: chrome.runtime.getURL('/manifest.json') + }; + } + chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, { + types: ['xmlhttprequest'], + urls: [url] + }, ['blocking']); + + var x = new XMLHttpRequest(); + x.open('get', url); + x.onloadend = function() { + chrome.webRequest.onHeadersReceived.removeListener(onHeadersReceived); + if (!x.responseText) { + // Network error? Anyway, can't tell with certainty whether the feature + // is supported. + callback('maybe'); + } else if (/^\s*\{/.test(x.responseText)) { + // If the response starts with "{", assume that the redirection to the + // manifest file succeeded, so the feature is supported. + callback('yes'); + } else { + // Did not get the content of manifest.json, so the redirect seems not to + // be followed. The feature is not supported. + callback('no'); + } + }; + x.send(); +} diff --git a/extensions/chromium/pdfHandler-v2.js b/extensions/chromium/pdfHandler-v2.js index 711c35d6f..9bfbc099a 100644 --- a/extensions/chromium/pdfHandler-v2.js +++ b/extensions/chromium/pdfHandler-v2.js @@ -15,7 +15,7 @@ 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, URL, getViewerURL */ +/* globals chrome, URL, getViewerURL, Features */ (function() { 'use strict'; @@ -158,7 +158,8 @@ limitations under the License. var streamInfo = getStream(sender.tab.id, pdfUrl) || {}; sendResponse({ streamUrl: streamInfo.streamUrl, - contentLength: streamInfo.contentLength + contentLength: streamInfo.contentLength, + extensionSupportsFTP: Features.extensionSupportsFTP }); } }); diff --git a/extensions/chromium/pdfHandler.html b/extensions/chromium/pdfHandler.html index ffb6c1ae6..c98851363 100644 --- a/extensions/chromium/pdfHandler.html +++ b/extensions/chromium/pdfHandler.html @@ -15,6 +15,7 @@ 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 99cdbae0f..5f5465b57 100644 --- a/extensions/chromium/pdfHandler.js +++ b/extensions/chromium/pdfHandler.js @@ -15,7 +15,7 @@ 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 */ +/* globals chrome, Features */ 'use strict'; @@ -114,8 +114,11 @@ chrome.webRequest.onHeadersReceived.addListener( var viewerUrl = getViewerURL(details.url); // Replace frame with viewer - // TODO: When http://crbug.com/280464 is fixed, use - // return { redirectUrl: viewerUrl }; + if (Features.webRequestRedirectUrl) { + return { redirectUrl: viewerUrl }; + } + // Aww.. redirectUrl is not yet supported, so we have to use a different + // method as fallback (Chromium <35). if (details.frameId === 0) { // Main frame. Just replace the tab and be done! @@ -172,6 +175,27 @@ chrome.webRequest.onHeadersReceived.addListener( }, ['blocking','responseHeaders']); +chrome.webRequest.onBeforeRequest.addListener( + function onBeforeRequestForFTP(details) { + if (!Features.extensionSupportsFTP) { + chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequestForFTP); + return; + } + if (isPdfDownloadable(details)) { + return; + } + var viewerUrl = getViewerURL(details.url); + return { redirectUrl: viewerUrl }; + }, + { + urls: [ + 'ftp://*/*.pdf', + 'ftp://*/*.PDF' + ], + types: ['main_frame', 'sub_frame'] + }, + ['blocking']); + chrome.webRequest.onBeforeRequest.addListener( function(details) { if (isPdfDownloadable(details)) { diff --git a/web/viewer.js b/web/viewer.js index 82a662749..6239bd9af 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1880,9 +1880,10 @@ function webViewerLoad(evt) { // PDFView.setTitleUsingUrl(file); // return; // } -// if (isFTPFile) { -// // Stream not found, and it's loaded from FTP. Reload the page, because -// // it is not possible to get resources over ftp using XMLHttpRequest. +// if (isFTPFile && !response.extensionSupportsFTP) { +// // Stream not found, and it's loaded from FTP. +// // When the browser does not support loading ftp resources over +// // XMLHttpRequest, just reload the page. // // NOTE: This will not lead to an infinite redirect loop, because // // if the file exists, then the streamsPrivate API will capture the // // stream and send back the response. If the stream does not exist, then