Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve the filename in “Open in browser as” mode #78

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 45 additions & 14 deletions extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ chrome.webRequest.onHeadersReceived.addListener(async function(details) {
let desiredCT = ContentHandlers.parseResponseContentType(desiredAction.mime);
setHeader(details.responseHeaders, 'Content-Type',
ContentHandlers.makeUnsniffableContentType(desiredCT.contentType));
setHeader(details.responseHeaders, 'Content-Disposition', 'inline');
setContentDispositionHeader(details.responseHeaders, 'inline');
}
gLastActionIsDownload = desiredAction.action === MimeActions.DOWNLOAD;
if (desiredAction.action === MimeActions.DOWNLOAD) {
Expand All @@ -178,12 +178,7 @@ chrome.webRequest.onHeadersReceived.addListener(async function(details) {
// Relevant code: https://searchfox.org/mozilla-central/rev/a5d613086ab4d0578510aabe8653e58dc8d7e3e2/uriloader/exthandler/nsExternalHelperAppService.cpp#1685-1704
setHeader(details.responseHeaders, 'Content-Type', 'application/prs.oib-ask-once');
}
if (contentDisposition) {
setHeader(details.responseHeaders, 'Content-Disposition',
contentDisposition.replace(/^[^;]*(;?)/, 'attachment$1'));
} else {
setHeader(details.responseHeaders, 'Content-Disposition', 'attachment');
}
setContentDispositionHeader(details.responseHeaders, 'attachment');
}
if (desiredAction.rememberChoice) {
let effectiveMimeType = isSniffingMimeType ? guessedMimeType : mimeType;
Expand Down Expand Up @@ -266,7 +261,7 @@ browser.menus.create({
/**
* Get the value of a header from the list of headers for a given name.
*
* @param {Array} headers responseHeaders of webRequest.onHeadersReceived
* @param {chrome.webRequest.HttpHeader[]} headers responseHeaders of webRequest.onHeadersReceived
* @param {string} headerName The lowercase name of the header to look for.
* @return {string} The value of the header, if found. Empty string otherwise.
*/
Expand All @@ -283,8 +278,10 @@ function getHeader(headers, headerName) {
/**
* Adds or replaces a header
*
* @param {Array} headers responseHeaders of webRequest.onHeadersReceived
* The contents of the array may be modified.
* @param {chrome.webRequest.HttpHeader[]} headers `responseHeaders` of
* `webRequest.onHeadersReceived`. The contents of the array may be modified.
* @param {string} headerName The header to add or replace.
* @param {string} headerValue The new header value.
*/
function setHeader(headers, headerName, headerValue) {
var lowerCaseHeaderName = headerName.toLowerCase();
Expand All @@ -300,10 +297,37 @@ function setHeader(headers, headerName, headerValue) {
});
}

/**
* Adds or modifies the Content-Disposition header. If present,
* it modifies only the target part, leaving the filename intact.
*
* @param {chrome.webRequest.HttpHeader[]} headers `responseHeaders` of
* `webRequest.onHeadersReceived`. The contents of the array may be modified.
* @param {'inline'|'attachment'} target The target part.
*/
function setContentDispositionHeader(headers, target) {
for (var i = headers.length - 1; i >= 0; --i) {
var header = headers[i];
if (header.name.toLowerCase() === 'content-disposition') {
if (header.value) {
header.value = header.value.replace(/^[^;]*/, target);
} else {
header.value = target;
}
return;
}
}
// If there was no Content-Disposition header...
headers.push({
name: 'Content-Disposition',
value: target
});
}

/**
* Derive file name from URL
*
* @param {string} An URL
* @param {string} url A URL
* @return {string} A file name
*/
function getFilenameFromURL(url) {
Expand All @@ -329,11 +353,15 @@ function getFilenameFromURL(url) {
* setupBeforeAsyncTask can be passed a function, which is called if the request was aborted
* before continueAfterAsyncTask is called.
*
* @param {object} details WebRequest event details.
* @return {object} An object with properties "aborted", "setupBeforeAsyncTask" and
* "continueAfterAsyncTask". See the above example.
* @param {chrome.webRequest.WebResponseHeadersDetails} details WebRequest event details.
* @return {{
* aborted: boolean;
* setupBeforeAsyncTask: (callback: (() => void)|null) => void;
* continueAfterAsyncTask: () => boolean;
* }} See the above example.
*/
function createWebRequestAbortionObserver(details) {
/** @type {(() => void)|null} */
var callbackOnPrematureAbort = null;
var isAborted = false;
function onErrorOccurred(errorDetails) {
Expand Down Expand Up @@ -374,6 +402,9 @@ function createWebRequestAbortionObserver(details) {
}


/**
* @param {(() => void)|null} onAborted
*/
function setupBeforeAsyncTask(onAborted) {
callbackOnPrematureAbort = onAborted;
chrome.webRequest.onErrorOccurred.addListener(onErrorOccurred, {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"eslint": "^4.7.2",
"mkdirp": "^0.5.1",
"request": "^2.82.0",
"xml2js": "^0.4.19"
"xml2js": "^0.4.19",
"@types/chrome": "^0.0.212"
}
}