1

Since this question Block downloading by Content-Type via Chrome Extension didn't work for me, I am opening a new question. I use the following code to block download based on content-type header:

chrome.webRequest.onHeadersReceived.addListener(function(details) {
    preventDownload = false;
    details.responseHeaders.push({name:"X-Content-Options",value: "no-sniff"});  // Hack 1
    details.statusLine = "HTTP/1.1 302 Moved Temporarily"; // Hack 2
    for (var i = 0; i < details.responseHeaders.length; ++i) 
    {
       if (details.responseHeaders[i].name == 'Content-Type')
        {
            var contentType = details.responseHeaders[i].value;
            if (contentType.indexOf("application/xyz")!=-1)
            {
                preventDownload = true;
            details.responseHeaders[i].value = 'text/plain'; //Hack 3
            }
            else
            {
                return {responseHeaders: details.responseHeaders};
            }
        }

    }
    if(preventDownload)
    {
        if (details.frameId === 0) // Top frame, yay!
        { 
            var scheme = /^https/.test(details.url) ? "https" : "http";
            chrome.tabs.update(details.tabId, {
            url: scheme + "://robwu.nl/204"});
            return;   //return {cancel: true}; should be used but it displays block page
        }
        return {cancel: true};
    }
    return {responseHeaders: details.responseHeaders};
}, {urls: ["<all_urls>"],types: ["main_frame", "sub_frame"]}, ['blocking', 'responseHeaders']);

I succeed in preventing the download, but an error Web block page appears. I need to keep user on previous page without reloading to displaying this error page OR somehow move back from this service page after it displayed.

I have used a hack in above code but it does not block the download.

Community
  • 1
  • 1
adnan kamili
  • 8,967
  • 7
  • 65
  • 125

1 Answers1

3

details is an object supplied to your extension with information about the request. Changing its value does indeed not have any effect on the request.

Since Chrome 35.0.1911.0, you can simply redirect to a page that replies with status code 204 to prevent the previous page from unloading:

chrome.webRequest.onHeadersReceived.addListener(function(details) {
    // ... your code that checks whether the request should be blocked ...
    //  (omitted for brevity)
    var scheme = /^https/.test(details.url) ? "https" : "http";
    return {redirectUrl: scheme + "://robwu.nl/204" };
}, {
    urls: ["<all_urls>"],
    types: ["main_frame", "sub_frame"]
}, ["responseHeaders", "blocking"]);

If you're using an older Chrome version (e.g 34-), then the following method can be used instead:

Further, to prevent the file from being downloaded, you need to instruct Chrome to render the page in the tab. This can be done by modifying the headers.

  1. Change Content-Type to text/plain (because it is a light format).
  2. Add X-Content-Type-Options: nosniff (to prevent MIME-sniffing).
  3. Remove the Content-Disposition header (to prevent some kinds of downloads).

With these headers, Chrome will try to render the response in the tab. Here's where the method from the other answer comes in: By calling chrome.tabs.update that points to a resource that replies with HTTP status code 204, the navigation is cancelled without leaving the current page.

chrome.webRequest.onHeadersReceived.addListener(function(details) {
    // ... your code that checks whether the request should be blocked ...
    //  (omitted for brevity)

    if (details.frameId === 0) { // Top frame, yay!
        // Prevent current page from unloading:
        var scheme = /^https/.test(details.url) ? "https" : "http";
        chrome.tabs.update(details.tabId, {
            url: scheme + "://robwu.nl/204"
        });

        // Prevent file from being downloaded via the headers:
        var responseHeaders = details.responseHeaders.filter(function(header) {
            var name = header.name.toLowerCase();
            return name !== 'content-type' &&
                   name !== 'x-content-type-options' &&
                   name !== 'content-disposition';
        }).concat([{
            // Change content type to something non-downloadable
            name: 'Content-Type',
            value: 'text/plain'
        }, {
            // Disable MIME-type sniffing:
            name: 'X-Content-Type-Options',
            value: 'nosniff'
        }]);
        return {
            responseHeaders: responseHeaders
        };
    }
    // else not the top frame...
    return {cancel: true};
}, {
    urls: ["<all_urls>"],
    types: ["main_frame", "sub_frame"]
}, ["responseHeaders", "blocking"]);

Note: This is a hacky method. Feel free to use it for personal use, but please do not upload such kinds of extensions to the Chrome Web Store.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Thanks it worked out of the box. Why doesn't changing the status line has any effect. Also when can we expect redirectUrl support for onheadersrecieved event, so that we don't need above hack. – adnan kamili Mar 23 '14 at 17:08
  • @adnankamili As said, `details` is just informative. Almost every parameter in the Chrome extension API is just a plain JavaScript object. I expect that redirectUrl for onHeadersReceived lands in Chrome 35 or 36 (follow progress on http://crbug.com/280464). Chrome's release cycle takes 6 weeks, to go from canary/dev -> beta -> stable. So, even if the patch lands today, it takes 7-12 weeks before the feature becomes widely available. If you want to get features faster, just install Chrome from the beta or dev channel (http://www.chromium.org/getting-involved/dev-channel). – Rob W Mar 23 '14 at 17:16
  • can the above be achieved by injecting a content script in onheadersrecieved event. – adnan kamili Mar 23 '14 at 18:45
  • @adnankamili What do you mean by "the above"? – Rob W Mar 23 '14 at 18:46
  • @adnan Only a very small subset of the request details can be changed, consult the documentation of [chrome.webRequest](https://developer.chrome.com/extensions/webRequest) for more info. As for the other question: Give it a try... – Rob W Mar 23 '14 at 18:59
  • I suppose you have added the feature redirectUrl: chrome.runtime.getURL(url), in which build of chrome is it expected to land - 35 (stable) I suppose – adnan kamili Apr 14 '14 at 12:44
  • @adnankamili The `redirectUrl` feature has landed in Chrome 35 indeed (currently available on [the beta channel](https://www.google.com/intl/en/chrome/browser/beta.html)). – Rob W Apr 14 '14 at 12:48
  • Great! I hope you won't mind updating your answer then? – adnan kamili Apr 14 '14 at 12:53
  • Is there any way to check version of chrome in addon so that we can use the old one as a fallback method in an else block – adnan kamili Apr 14 '14 at 13:10
  • @adnankamili You can detect the Chrome version using http://stackoverflow.com/a/19295499/938089. However, feature detection is generally more advised, see https://github.com/Rob--W/pdf.js/commit/ae32f31eb4c4d27b2e9230a6fa62f0a64d967b3a for an example. – Rob W Apr 14 '14 at 13:12
  • you meant o say this should work: if (Features.webRequestRedirectUrl) { return { redirectUrl: viewerUrl }; } – adnan kamili Apr 14 '14 at 13:16
  • @adnankamili Please read the source code from my previous comment and try to understand it before asking any further questions. – Rob W Apr 14 '14 at 13:17