在选项页面中使用 <form> & browser.storage.local.get() 在内容脚本中不起作用

Using a <form> in an options page & browser.storage.local.get() does not work in content script

我正在使用 Firefox Add-on,用户可以在其中的选项页面上设置一些值。当用户访问站点时,将执行内容脚本,然后需要读取先前在选项页面上设置的值。

我按照 MDN 指南“Implement a settings page”'Adding settings' 标题下的每个步骤进行操作,并使用给定的示例代码创建了所有文件。虽然设置、更改和读取选项页面上的值工作正常,但我无法在内容脚本文件(在指南中名为 borderify.js)中执行相同操作。在指南的具体代码示例中,无论在选项页面上设置了什么,它始终使用默认的蓝色边框。

我检查了内容脚本的 wiki 页面,但它只是说:'In addition to the standard DOM APIs, content scripts can use the following WebExtension APIs: [...] Everything from storage.'

所有这些都使用全新的 Firefox 配置文件和版本 50.0.2 进行了测试。

有人可以帮我解决这个问题吗?

manifest.json

{

  "manifest_version": 2,
  "name": "Settings example",
  "version": "1.0",

  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["borderify.js"]
    }
  ],

  "applications": {
    "gecko": {
      "id": "settings-example@mozilla.org"
    }
  },

  "options_ui": {
    "page": "options.html"
  },

  "permissions": ["storage"]

}

options.html

<html>
  <head>
    <meta charset="utf-8">
  </head>

  <body>

    <form>
    <label>Border color<input type="text" id="color" ></label>
    <button type="submit">Save</button>
    </form>

    <script src="options.js"></script>

  </body>

</html>

options.js

function saveOptions(e) {
  browser.storage.local.set({
    color: document.querySelector("#color").value
  });
}

function restoreOptions() {

  function setCurrentChoice(result) {
    document.querySelector("#color").value = result.color || "blue";
  }

  function onError(error) {
    console.log(`Error: ${error}`);
  }

  var getting = browser.storage.local.get("color");
  getting.then(setCurrentChoice, onError);
}

document.addEventListener("DOMContentLoaded", restoreOptions);
document.querySelector("form").addEventListener("submit", saveOptions);

borderify.js

function onError(error) {
  console.log(`Error: ${error}`);
}

function onGot(item) {
  var color = "blue";
  if (item.color) {
    color = item.color;
  }
  document.body.style.border = "10px solid " + color;
}

var getting = browser.storage.local.get("color");
getting.then(onGot, onError);

有两个问题阻止此代码工作:

  1. 52 之前的 Firefox 版本中的一个错误导致在内容脚本中实现来自 browser.storage.local.get() 的 Promise 时返回对象数组。这个 Promise 应该用一个对象来实现。
  2. 在选项页面中使用 <form>submit 事件而不在事件处理程序中执行 preventDefault()

对于 Firefox 版本 <52

browser.storage.local.get() 中的 Firefox 错误

在您的内容脚本中,当来自 browser.storage.local.get() 的 Promise 实现时,您会收到一个包含一个对象的数组,而不是一个对象。数组中的对象正确地具有 color 属性。如果您在 options.js 中使用与 browser.storage.local.get() 相同的代码,您会正确地获得一个对象。在 Firefox Developer Edition (52.0a2) 和 Nightly (53.0a1) 中,对象被正确接收。

在所有情况下都returns正确使用chrome.storage.local.get()API对象。我建议使用 API 而不是 browser.storage.local.get().

如果您决定继续使用 browser.storage.local.get(),那么您将需要考虑当前在 Firefox < 52 中获取对象数组(在 item[0] 中使用正确的对象)并正确获取Firefox 中的一个对象 >= 52.

在选项页面中使用 <form>submit 事件需要 preventDefault()

这是 MDN 页面“Implement a settings page”上的代码中的一个问题。我已更新该页面上的代码以反映下述问题。

问题是您在 options.html[ 中使用 <form>submit 事件=72=] 不阻止 e.preventDefault().

的默认操作

如果您不阻止默认操作,Firefox 会尝试将页面提交给原始 URL。在选项页面上,这显然失败了。但是,它以一种导致 Firefox 重新加载选项页面的方式失败。这会破坏您的选项页面之前存在的范围,导致对 storage.local.set() 的调用在实际设置值之前中止。 storage.local.set() 是异步的,因此它在被中止之前启动,但还不足以实际更改 StorageArea 中的值。

如果您将 e.preventDefault(); 添加到 options.js 中的 saveOptions(),您的扩展程序将正常工作,如下所示:

function saveOptions(e) {
  e.preventDefault();
  browser.storage.local.set({
    color: document.querySelector("#color").value
  });
}