浏览代码

feat: add `isBlobUrl` parameter to GM_getResourceURL (#1438)

* feat: add `isBlobUrl` parameter to GM_getResourceURL

GM_getResourceURL(name) // blob: URL
GM_getResourceURL(name, true) // blob: URL
GM_getResourceURL(name, false) // data: URL
tophf 3 年之前
父节点
当前提交
8cfa224fd8
共有 3 个文件被更改,包括 15 次插入6 次删除
  1. 7 2
      src/injected/content/util-content.js
  2. 6 4
      src/injected/web/gm-api.js
  3. 2 0
      test/injected/gm-resource.test.js

+ 7 - 2
src/injected/content/util-content.js

@@ -47,7 +47,12 @@ export const getFullUrl = url => (
 export const decodeResource = (raw, isBlob) => {
   let res;
   const pos = raw::stringIndexOf(',');
-  const bin = safeAtob(pos < 0 ? raw : raw::slice(pos + 1));
+  const mimeType = pos < 0 ? '' : raw::slice(0, pos);
+  const mimeData = pos < 0 ? raw : raw::slice(pos + 1);
+  if (isBlob === false) {
+    return `data:${mimeType};base64,${mimeData}`;
+  }
+  const bin = safeAtob(mimeData);
   if (isBlob || /[\x80-\xFF]/::regexpTest(bin)) {
     const len = bin.length;
     const bytes = new SafeUint8Array(len);
@@ -55,7 +60,7 @@ export const decodeResource = (raw, isBlob) => {
       bytes[i] = bin::charCodeAt(i);
     }
     res = isBlob
-      ? new SafeBlob([bytes], { type: pos < 0 ? '' : raw::slice(0, pos) })
+      ? new SafeBlob([bytes], { type: mimeType })
       : new SafeTextDecoder()::tdDecode(bytes);
   } else { // pure ASCII
     res = bin;

+ 6 - 4
src/injected/web/gm-api.js

@@ -76,8 +76,8 @@ export function makeGmApi() {
     GM_getResourceText(name) {
       return getResource(this, name);
     },
-    GM_getResourceURL(name) {
-      return getResource(this, name, true);
+    GM_getResourceURL(name, isBlobUrl = true) {
+      return getResource(this, name, !!isBlobUrl);
     },
     GM_registerMenuCommand(cap, func) {
       const { id } = this;
@@ -206,16 +206,18 @@ function webAddElement(parent, tag, attrs, context) {
 function getResource(context, name, isBlob) {
   const { id, resCache, resources } = context;
   const key = resources[name];
+  const bucketKey = isBlob == null ? 0 : 1 + isBlob;
   if (key) {
-    let res = resCache[key];
+    let res = ensureNestedProp(resCache, bucketKey, key, false);
     if (!res) {
       bridge.syncCall('GetResource', { id, isBlob, key }, context, null, response => {
         res = response;
       });
       if (res !== true && isBlob) {
+        // Creating Blob URL in page context to make it accessible for page userscripts
         res = createObjectURL(res);
       }
-      resCache[key] = res;
+      ensureNestedProp(resCache, bucketKey, key, res);
     }
     return res === true ? key : res;
   }

+ 2 - 0
test/injected/gm-resource.test.js

@@ -13,9 +13,11 @@ const blobAsText = async blob => new Promise(resolve => {
 // WARNING: don't include D800-DFFF range which is for surrogate pairs
 const RESOURCE_TEXT = 'abcd\u1234\u2345\u3456\u4567\u5678\u6789\u789A\u89AB\u9ABC\uABCD';
 const DATA = `text/plain,${stringAsBase64(RESOURCE_TEXT)}`;
+const DATA_URL = `data:${DATA.replace(',', ';base64,')}`;
 
 test('@resource decoding', async (t) => {
   t.equal(decodeResource(DATA), RESOURCE_TEXT, 'GM_getResourceText');
   t.equal(await blobAsText(decodeResource(DATA, true)), RESOURCE_TEXT, 'GM_getResourceURL');
+  t.equal(decodeResource(DATA, false), DATA_URL, 'GM_getResourceURL as dataUrl');
   t.end();
 });