javascriptgoogle-chromepdfgoogle-chrome-extensionchrome-extension-manifest-v3

Chrome extension: How to show custom UI for a PDF file in Manifest V3?


This question has been already answered Chrome extension: How to show custom UI for a PDF file?

However, the answer seems outdated as it uses manifest V2. How to achieve same in manifest V3 ?

I tried

rules.json

With

 [
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "transform": {
          "scheme": "chrome-extension",
          "host": "mbojebcnkfasdfalsdfkjalsdkjfa",
          "path": "/viewer.html",
          "queryTransform": {
            "addOrReplaceParams": [
              {
                "key": "file",
                "value": "{url}"
              }
            ]
          }
        }
      }
    },
    "condition": {
      "urlFilter": "*.pdf",
      "resourceTypes": ["main_frame", "sub_frame"]
    }
  }
]

And this,

{
  "manifest_version": 3,
  "name": "Selection Popup",
  "version": "1.0",
  "description": "Shows a popup near text selection.",
  "permissions": [
    "scripting",
    "contextMenus",
    "activeTab",
    "storage",
    "tabs",
    "declarativeNetRequest",
    "declarativeNetRequestWithHostAccess"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules.json"
      }
    ]
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "options_page": "popup.html",
  "content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],
      "js": [
        "content.js"
      ],
      "css": [
        "styles.css"
      ]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": [
        "popup.html",
        "script.js",
        "pdf.worker.min.js", "pdf.mjs"
      ],
      "matches": [
        "<all_urls>"
      ]
    }
  ],
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

But when I open a local pdf file, nothing happens, it still uses the Chrome Default PDF viewer.

Goal is to achieve what this extension does:

https://chromewebstore.google.com/detail/pdf-viewer/oemmndcbldboiebfnladdacbdfmadadm


Solution

    1. To redirect a URL, it must be in the extension's host permissions. Since you want to process PDFs on all hosts, you need "host_permissions": ["<all_urls>"]

    2. To redirect to your own page while preserving the original URL and avoid hardcoding the id of the extension you need updateDynamicRules (see this answer) in the background script:

      chrome.runtime.onInstalled.addListener(e => {
        if (e.reason !== 'update' && e.reason !== 'install') return;
        const EXT_PAGE = chrome.runtime.getURL('/viewer.html');
        const ID = 1;
        chrome.declarativeNetRequest.updateDynamicRules({
          removeRuleIds: [ID],
          addRules: [{
            id: ID,
            action: {
              type: 'redirect',
              redirect: {regexSubstitution: EXT_PAGE + '#\\0'},
            },
            condition: {
              regexFilter: /^.+\.pdf(\?.*)?$/.source,
              resourceTypes: ['main_frame', 'sub_frame'],
            },
          }],
        });
      });
      

      Now your extension page (viewer.html) can read this URL:

      const origUrl = location.hash.slice(1);
      

      As of Chrome 127, we can also match by content type in declarativeNetRequest, but it's not yet enabled in the stable version, so I'm not demonstrating it.

    3. To redirect to a page in your extension, it must be listed in web_accessible_resources:

        "web_accessible_resources": [{
          "resources": ["viewer.html"],
          "matches": ["<all_urls>"]
        }],
      
    4. A published extension from the web store can't open local files by default. The user must enable file access manually in the extension's details in chrome://extensions page.

    5. Remove declarativeNetRequest from permissions because it unnecessarily adds a warning at installation and you already have declarativeNetRequestWithHostAccess + host_permissions.

    6. Remove declarative_net_request from manifest.json.

    7. You probably don't need content_scripts running in all pages unconditionally.