瀏覽代碼

Migrate to manifest v3

simov 3 年之前
父節點
當前提交
bb98e37245
共有 8 個文件被更改,包括 135 次插入277 次删除
  1. 10 10
      background/detect.js
  2. 15 5
      background/index.js
  3. 32 17
      background/inject.js
  4. 15 109
      background/webrequest.js
  5. 12 18
      background/xhr.js
  6. 7 64
      content/index.js
  7. 22 49
      manifest.json
  8. 22 5
      popup/index.js

+ 10 - 10
background/detect.js

@@ -3,25 +3,25 @@ md.detect = ({storage: {state}, inject}) => {
 
   var onwakeup = true
 
-  var code = `
-    JSON.stringify({
-      url: window.location.href,
-      header: document.contentType,
-      loaded: !!window.state,
-    })
-  `
-
   var tab = (id, info, tab) => {
     if (info.status === 'loading') {
       // try
-      chrome.tabs.executeScript(id, {code, runAt: 'document_start'}, (res) => {
+      chrome.scripting.executeScript({
+        target: {tabId: id},
+        func: () =>
+          JSON.stringify({
+            url: window.location.href,
+            header: document.contentType,
+            loaded: !!window.state,
+          })
+      }, (res) => {
         if (chrome.runtime.lastError) {
           // origin not allowed
           return
         }
 
         try {
-          var win = JSON.parse(res)
+          var win = JSON.parse(res[0].result)
         }
         catch (err) {
           // JSON parse error

+ 15 - 5
background/index.js

@@ -1,9 +1,23 @@
 
+importScripts('/vendor/marked.min.js')
+importScripts('/vendor/remark.min.js')
+
+importScripts('/background/compilers/marked.js')
+importScripts('/background/compilers/remark.js')
+
+importScripts('/background/storage.js')
+importScripts('/background/webrequest.js')
+importScripts('/background/detect.js')
+importScripts('/background/inject.js')
+importScripts('/background/messages.js')
+importScripts('/background/mathjax.js')
+importScripts('/background/xhr.js')
+
 ;(() => {
   var storage = md.storage(md)
   var inject = md.inject({storage})
   var detect = md.detect({storage, inject})
-  var webrequest = md.webrequest({storage, detect})
+  var webrequest = md.webrequest({storage})
   var mathjax = md.mathjax()
   var xhr = md.xhr()
 
@@ -17,8 +31,4 @@
 
   chrome.tabs.onUpdated.addListener(detect.tab)
   chrome.runtime.onMessage.addListener(messages)
-
-  if (chrome.webRequest) {
-    webrequest.init()
-  }
 })()

+ 32 - 17
background/inject.js

@@ -1,25 +1,40 @@
 
 md.inject = ({storage: {state}}) => (id) => {
 
-  chrome.tabs.executeScript(id, {
-    code: `
+  chrome.scripting.executeScript({
+    target: {tabId: id},
+    args: [{
+      theme: state.theme,
+      raw: state.raw,
+      themes: state.themes,
+      content: state.content,
+      compiler: state.compiler,
+    }],
+    func: (_args) => {
       document.querySelector('pre').style.visibility = 'hidden'
-      var theme = ${JSON.stringify(state.theme)}
-      var raw = ${state.raw}
-      var themes = ${JSON.stringify(state.themes)}
-      var content = ${JSON.stringify(state.content)}
-      var compiler = '${state.compiler}'
-    `,
-    runAt: 'document_start'
+      args = _args
+    },
+    injectImmediately: true
   })
 
-  chrome.tabs.insertCSS(id, {file: 'content/index.css', runAt: 'document_start'})
-  chrome.tabs.insertCSS(id, {file: 'vendor/prism.min.css', runAt: 'document_start'})
+  chrome.scripting.insertCSS({
+    target: {tabId: id},
+    files: [
+      '/content/index.css',
+      '/vendor/prism.min.css',
+    ]
+  })
+
+  chrome.scripting.executeScript({
+    target: {tabId: id},
+    files: [
+      '/vendor/mithril.min.js',
+      '/vendor/prism.min.js',
+      state.content.emoji && '/content/emoji.js',
+      state.content.mermaid && ['/vendor/mermaid.min.js', '/vendor/mermaid.init.js'],
+      '/content/index.js',
+    ].filter(Boolean).flat(),
+    injectImmediately: true
+  })
 
-  chrome.tabs.executeScript(id, {file: 'vendor/mithril.min.js', runAt: 'document_start'})
-  chrome.tabs.executeScript(id, {file: 'vendor/prism.min.js', runAt: 'document_start'})
-  if (state.content.emoji) {
-    chrome.tabs.executeScript(id, {file: 'content/emoji.js', runAt: 'document_start'})
-  }
-  chrome.tabs.executeScript(id, {file: 'content/index.js', runAt: 'document_start'})
 }

+ 15 - 109
background/webrequest.js

@@ -1,57 +1,13 @@
 
-md.webrequest = ({storage: {state}, detect}) => {
+md.webrequest = ({storage: {state}}) => {
 
-  var permissions = ['webRequest', 'webRequestBlocking']
+  var permissions = ['webRequest']
 
   var filter = {
     urls: ['<all_urls>'],
     types: ['main_frame', 'sub_frame']
   }
 
-  var options = ['blocking', 'responseHeaders']
-
-  var onHeadersReceived = ({method, url, statusCode, responseHeaders}) => {
-    if (method !== 'GET') {
-      return {responseHeaders}
-    }
-
-    var header = responseHeaders.find(({name}) => /^content-type/i.test(name))
-    var origin = detect.match(url)
-
-    if (/Firefox/.test(navigator.userAgent)) {
-      // ff: markdown `content-type` is not allowed
-      if (header && detect.header(header.value)) {
-        var [_, charset] = header.value.split(';')
-        header.value = `text/plain${charset ? `; ${charset}` : ''}`
-      }
-      // ff: missing `content-type` is not allowed
-      else if (!header && origin && statusCode === 200) {
-        header = {name: 'Content-Type', value: 'text/plain'}
-        responseHeaders.push(header)
-      }
-    }
-
-    if (!origin) {
-      return {responseHeaders}
-    }
-
-    if (origin.csp) {
-      responseHeaders = responseHeaders
-        .filter(({name}) => !/content-security-policy/i.test(name))
-    }
-
-    if (origin.encoding && header && (
-      /charset/.test(header.value) ||
-      // ff: won't decode correctly by default
-      /Firefox/.test(navigator.userAgent)
-    )) {
-      var [media] = header.value.split(';')
-      header.value = `${media}; charset=${origin.encoding}`
-    }
-
-    return {responseHeaders}
-  }
-
   var onCompleted = ({ip, tabId}) => {
     if (ip && ip !== '127.0.0.1' && ip !== '::1') {
       setTimeout(() => {
@@ -60,76 +16,26 @@ md.webrequest = ({storage: {state}, detect}) => {
     }
   }
 
-  var events = () => {
-    var headers = false
-    for (var key in state.origins) {
-      if (state.origins[key].csp || state.origins[key].encoding) {
-        headers = true
-        break
-      }
-    }
-
-    // ff: webRequest is required permission
-    if (/Firefox/.test(navigator.userAgent)) {
-      headers = true
-    }
-
-    var completed = false
-    if (state.content.autoreload) {
-      completed = true
-    }
-
-    return {headers, completed}
-  }
-
-  var perm = (headers, completed, done) => {
-    // ff: webRequest is required permission
-    if (/Firefox/.test(navigator.userAgent)) {
-      done()
-    }
-    // request permissions
-    else if ((headers || completed) && !chrome.webRequest) {
-      chrome.permissions.request({permissions}, done)
+  var webrequest = () => {
+    if (state.content.autoreload && !chrome.webRequest) {
+      // request permissions
+      chrome.permissions.request({permissions}, () => {
+        // add listener
+        chrome.webRequest.onCompleted.addListener(onCompleted, filter)
+      })
     }
-    // remove permissions
-    else if (!headers && !completed && chrome.webRequest) {
+    else if (!state.content.autoreload && chrome.webRequest) {
+      // remove listener
+      chrome.webRequest.onCompleted.removeListener(onCompleted)
+      // remove permissions
       chrome.permissions.remove({permissions}, () => {
         chrome.webRequest = null
-        done()
       })
     }
-    else {
-      done()
-    }
-  }
-
-  var webrequest = () => {
-
-    var {headers, completed} = events()
-
-    // remove listeners
-    if (chrome.webRequest) {
-      if (!headers && !/Firefox/.test(navigator.userAgent)) {
-        chrome.webRequest.onHeadersReceived.removeListener(onHeadersReceived)
-      }
-      if (!completed) {
-        chrome.webRequest.onCompleted.removeListener(onCompleted)
-      }
-    }
-
-    perm(headers, completed, () => {
-      // add listeners
-      if (headers && !chrome.webRequest.onHeadersReceived.hasListener(onHeadersReceived)) {
-        chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, options)
-      }
-      if (completed && !chrome.webRequest.onCompleted.hasListener(onCompleted)) {
-        chrome.webRequest.onCompleted.addListener(onCompleted, filter)
-      }
-    })
   }
 
-  webrequest.init = () => {
-    chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, options)
+  // init
+  if (chrome.webRequest) {
     chrome.webRequest.onCompleted.addListener(onCompleted, filter)
   }
 

+ 12 - 18
background/xhr.js

@@ -1,24 +1,18 @@
 
 md.xhr = () => {
-  var done
 
-  var xhr = new XMLHttpRequest()
-  xhr.onreadystatechange = () => {
-    if (xhr.readyState === 4) {
-      done(null, xhr.responseText)
-    }
-  }
-
-  var get = (url, _done) => {
-    done = _done
-    xhr.open('GET', url + '?preventCache=' + Date.now(), true)
-    try {
-      xhr.send()
-    }
-    catch (err) {
-      console.error(err)
-      done(err)
-    }
+  var get = (url, done) => {
+    ;(async () => {
+      await new Promise(async (resolve, reject) => {
+        try {
+          var res = await fetch(url + '?preventCache=' + Date.now())
+          done(null, await res.text())
+        }
+        catch (err) {
+          done(err)
+        }
+      })
+    })()
   }
 
   return {get}

+ 7 - 64
content/index.js

@@ -2,11 +2,11 @@
 var $ = document.querySelector.bind(document)
 
 var state = {
-  theme,
-  raw,
-  themes,
-  content,
-  compiler,
+  theme: args.theme,
+  raw: args.raw,
+  themes: args.themes,
+  content: args.content,
+  compiler: args.compiler,
   html: '',
   markdown: '',
   toc: '',
@@ -100,25 +100,12 @@ function mount () {
             $('body').classList.add('_toc-left')
           }
           if (state.content.mathjax) {
-            dom.push(m('script', {type: 'text/x-mathjax-config'}, mathjax))
             dom.push(m('script', {
-              src: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js'
+              src: chrome.runtime.getURL('/vendor/mathjax/mathjax.init.js')
             }))
-          }
-          if (state.content.mermaid) {
             dom.push(m('script', {
-              src: 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.8.4/mermaid.min.js'
+              src: chrome.runtime.getURL('/vendor/mathjax/tex-mml-chtml.js')
             }))
-            dom.push(m('script', {type: 'text/javascript'}, `
-              ;(() => {
-                var timeout = setInterval(() => {
-                  if (!!(window.mermaid && mermaid.init)) {
-                    clearInterval(timeout)
-                    mermaid.init({}, 'code.language-mmd, code.language-mermaid')
-                  }
-                }, 50)
-              })()
-            `))
           }
         }
       }
@@ -310,47 +297,3 @@ if (state.content.autoreload) {
     state.interval = setInterval(get, state.ms)
   })()
 }
-
-var mathjax = `
-  // TeX-AMS_HTML
-  MathJax.Hub.Config({
-    jax: [
-      'input/TeX',
-      'output/HTML-CSS',
-      'output/PreviewHTML',
-    ],
-    extensions: [
-      'tex2jax.js',
-      'AssistiveMML.js',
-      'a11y/accessibility-menu.js',
-    ],
-    TeX: {
-      extensions: [
-        'AMSmath.js',
-        'AMSsymbols.js',
-        'noErrors.js',
-        'noUndefined.js',
-      ]
-    },
-    tex2jax: {
-      inlineMath: [
-        ['$', '$'],
-        ['\\\\(', '\\\\)'],
-      ],
-      displayMath: [
-        ['$$', '$$'],
-        ['\\\\[', '\\\\]'],
-      ],
-      processEscapes: true
-    },
-    showMathMenu: false,
-    showProcessingMessages: false,
-    messageStyle: 'none',
-    skipStartupTypeset: true, // disable initial rendering
-    positionToHash: false
-  })
-  // set specific container to render, can be delayed too
-  MathJax.Hub.Queue(
-    ['Typeset', MathJax.Hub, '_html']
-  )
-`

+ 22 - 49
manifest.json

@@ -1,10 +1,10 @@
 {
-  "manifest_version": 2,
+  "manifest_version": 3,
   "name"            : "Markdown Viewer",
-  "version"         : "4.0",
+  "version"         : "5.0",
   "description"     : "Markdown Viewer",
 
-  "browser_action": {
+  "action": {
     "default_icon": {
       "19" : "/icons/icon19.png",
       "38" : "/icons/icon38.png"
@@ -16,50 +16,19 @@
   "options_page": "/options/index.html",
 
   "background" : {
-    "scripts": [
-      "/vendor/marked.min.js",
-      "/vendor/remark.min.js",
-
-      "/background/compilers/marked.js",
-      "/background/compilers/remark.js",
-
-      "/background/storage.js",
-      "/background/webrequest.js",
-      "/background/detect.js",
-      "/background/inject.js",
-      "/background/messages.js",
-      "/background/mathjax.js",
-      "/background/xhr.js",
-
-      "/background/index.js"
-    ],
-    "persistent": false
+    "service_worker": "/background/index.js"
   },
 
   "web_accessible_resources": [
-    "/themes/github.css",
-    "/themes/github-dark.css",
-
-    "/themes/godspeed.css",
-    "/themes/new-modern.css",
-    "/themes/torpedo.css",
-    "/themes/vostok.css",
-
-    "/themes/ghostwriter.css",
-    "/themes/radar.css",
-    "/themes/foghorn.css",
-    "/themes/markdown.css",
-    "/themes/markedapp-byword.css",
-    "/themes/solarized-dark.css",
-    "/themes/solarized-light.css",
-
-    "/themes/screen.css",
-    "/themes/markdown5.css",
-    "/themes/markdown6.css",
-    "/themes/markdown7.css",
-    "/themes/markdown8.css",
-    "/themes/markdown9.css",
-    "/themes/markdown-alt.css"
+    {
+      "matches": [
+        "<all_urls>"
+      ],
+      "resources": [
+        "/themes/*",
+        "/vendor/*"
+      ]
+    }
   ],
 
   "icons": {
@@ -72,14 +41,18 @@
 
   "permissions": [
     "storage",
+    "scripting"
+  ],
+
+  "host_permissions": [
     "file:///*"
   ],
 
+  "optional_host_permissions": [
+    "*://*/"
+  ],
+
   "optional_permissions": [
-    "https://*/",
-    "http://*/",
-    "*://*/",
-    "webRequest",
-    "webRequestBlocking"
+    "webRequest"
   ]
 }

+ 22 - 5
popup/index.js

@@ -5,7 +5,28 @@ var state = {
   content: {},
   theme: '',
   themes: {},
-  _themes: [],
+  _themes: [
+    'github',
+    'github-dark',
+    'godspeed',
+    'new-modern',
+    'torpedo',
+    'vostok',
+    'ghostwriter',
+    'radar',
+    'foghorn',
+    'markdown',
+    'markedapp-byword',
+    'solarized-dark',
+    'solarized-light',
+    'screen',
+    'markdown5',
+    'markdown6',
+    'markdown7',
+    'markdown8',
+    'markdown9',
+    'markdown-alt',
+  ],
   raw: false,
   tab: '',
   tabs: ['theme', 'compiler', 'content'],
@@ -107,10 +128,6 @@ var init = (res) => {
   state.theme = res.theme
   state.themes = res.themes
 
-  state._themes = chrome.runtime.getManifest().web_accessible_resources
-    .filter((file) => file.indexOf('/themes/') === 0)
-    .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1'))
-
   state.raw = res.raw
   state.tab = localStorage.getItem('tab') || 'theme'
   state.compilers = res.compilers