如何用另一个Chrome扩展替换/升级



我的团队拥有一个Chrome扩展,另一个团队也有类似的扩展,但很快就会被弃用,所以我们将接管他们所有的现有用户。问题来了——有没有一种方法可以将用户从他们的扩展无缝迁移到我们的扩展?也就是说,有没有办法从用户端自动升级到我们的扩展?

没有无缝的方法,因为明显的安全原因,扩展无法安装其他扩展(即使有"management"权限),但这里有一个可能的升级路径。

我将调用"旧"扩展名A,并假定其ID为"extension_a_id";调用"新"扩展名B,并假定它的ID为`extension_B_ID"。

  1. 确保扩展可以相互通信。除非您在清单中明确定义了"externally_connectable",否则默认情况下应该是这样;如果这样做,请确保扩展名B包含"extension_a_id" ID,反之亦然。

  2. 在以下所有步骤中,更新扩展B以包含对来自A的请求作出响应的代码。在您合理确定大多数安装库都有这个版本之前(可能要等一周左右),不要继续。

    //// Extension B (background code) ////
    chrome.runtime.onMessageExternal.addListener(
      function(message, sender, sendResponse) {
        if(sender.id && sender.id == "extension_A_id") {
          /* ..processing code.. */
        }
      }
    );
    
  3. 在扩展插件A中,添加一个检查插件B是否已安装。通过ping它来完成:

    //// Extension A ////
    chrome.runtime.onStartup.addListener(function() {
      isNewInstalled(function(response) {
        if(response) {
          transferPreferences(response.versionB); // See step 5
        } else {
          // Nag user to install, see step 4
        }
      });
    });        
    function isNewInstalled(callback) {
      chrome.runtime.sendMessage(
        "extension_B_id",
        {areYouThere: true},
        passResponseOrFalse(callback)
      );
    }
    function passResponseOrFalse(callback) {
      return function(response) {
        // It's important to evaluate chrome.runtime.lastError
        //   to prevent uncatchable exception, see http://stackoverflow.com/q/28431505
        if(chrome.runtime.lastError || !response) {
          callback(false);
        } else {
          callback(response);
        }
      }
    }
    //// Extension B (processing code) ////
    // Send version number just in case
    if(message.areYouThere) sendResponse({
      versionB: chrome.runtime.getManifest().version
    });
    
  4. 如果在步骤3中没有安装扩展B,请催促用户安装:显示一个页面,解释为什么需要升级并链接到CWS列表。此步骤需要用户输入。

  5. 如果在步骤3中已经安装了扩展B,则转移用户选项并自行卸载:

    //// Extension A ////
    function transferPreferences(versionB) {
      /* ..validate version of B.. */
      var prefs = {};
      /* ..fill prefs with data that needs to be transfered (JSON-encodable).. */
      chrome.runtime.sendMessage(
        "extension_B_id",
        {
          versionA: chrome.runtime.getManifest().version,
          preferences: prefs
        },
        passResponseOrFalse(goodbyeCruelWorld)
      );
    }
    function goodbyeCruelWorld(response) {
      if(response.processed) {
        // Does not require management permission
        chrome.management.uninstallSelf();
      } else {
        console.warn("It is not my time to die yet.");
      }
    }
    //// Extension B, processing code ////
    if(message.preferences) {
      /* ..validate message.versionA and process message.preferences.. */
      sendResponse({processed: true});
    }
    
  6. 当安装了扩展B时,向(可能安装的)扩展A发送消息,表示传输可以立即开始:

    //// Extension B, background code ////
    chrome.runtime.onInstalled.addListener(function(details) {
      /* ..maybe check details.reason and new version.. */
      chrome.runtime.sendMessage(
        "extension_A_id",
        {iAmHere: true, versionB: chrome.runtime.getManifest().version},
        ignoreResponse
      );
    });
    function ignoreResponse(response) {
      // Again, evaluate to avoid exceptions;
      //   if needed, this is the place to check if A is still installed
      return chrome.runtime.lastError;
    }
    //// Extension A, background code ////
    chrome.runtime.onMessageExternal.addListener(
      function(message, sender, sendResponse) {
        if(sender.id && sender.id == "extension_B_id") {
          if(message.iAmHere) {
            sendResponse({
              versionA: chrome.runtime.getManifest().version
            });
            transferPreferences(message.versionB);
          }
        }
      }
    );
    
  7. 将更新发布到具有以上所有内容的扩展名B。

结果:

  • 安装了B的用户不会注意到任何事情,因为ignoreResponse会吞噬消息错误
  • 同时安装了这两个软件的用户将在B更新后立即启动传输,并将悄悄完成
  • 只有A的用户在每次重新启动扩展时都会被提示安装B,然后传输将自动启动

最后一个问题是不要用A的偏好来破坏B的偏好;留给读者练习(也取决于实现)。

最新更新