[Pkg-mozext-commits] [adblock-plus] 75/98: Issue 5050 - Make legacy extension use WebExtensions I/O

David Prévot taffit at moszumanska.debian.org
Tue Oct 24 01:30:22 UTC 2017


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository adblock-plus.

commit 0b5000d06b63ef690f55c7b7834233065247b1c1
Author: Wladimir Palant <trev at adblockplus.org>
Date:   Thu Mar 30 14:31:43 2017 +0200

    Issue 5050 - Make legacy extension use WebExtensions I/O
---
 chrome/content/ui/utils.js  |   2 +-
 lib/io.js                   | 363 ++++++++++++--------------------------------
 lib/{io.js => legacyIO.js}  |   6 +-
 lib/prefs.json              |   1 -
 lib/utils.js                |  10 +-
 webextension/.eslintrc.json |   7 +
 webextension/background.js  |  41 +++++
 webextension/io.js          | 109 +++++++++++++
 webextension/manifest.json  |   9 ++
 9 files changed, 268 insertions(+), 280 deletions(-)

diff --git a/chrome/content/ui/utils.js b/chrome/content/ui/utils.js
index 7e0c371..5f8d515 100644
--- a/chrome/content/ui/utils.js
+++ b/chrome/content/ui/utils.js
@@ -39,7 +39,7 @@ var {Filter, InvalidFilter, CommentFilter, ActiveFilter, RegExpFilter,
      ElemHideException, ElemHideEmulationFilter} = require("filterClasses");
 var {FilterNotifier} = require("filterNotifier");
 var {FilterStorage} = require("filterStorage");
-var {IO} = require("io");
+var {IO} = require("legacyIO");
 var {defaultMatcher, Matcher, CombinedMatcher} = require("matcher");
 var {Prefs} = require("prefs");
 var {RequestNotifier} = require("requestNotifier");
diff --git a/lib/io.js b/lib/io.js
index 4df5f2d..e566264 100644
--- a/lib/io.js
+++ b/lib/io.js
@@ -15,325 +15,160 @@
  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/**
- * @fileOverview Module containing file I/O helpers.
- */
-
-let {Services} = Cu.import("resource://gre/modules/Services.jsm", null);
-let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", null);
-let {OS} = Cu.import("resource://gre/modules/osfile.jsm", null);
-let {Task} = Cu.import("resource://gre/modules/Task.jsm", null);
+"use strict";
 
-let {Prefs} = require("prefs");
+let {IO: LegacyIO} = require("legacyIO");
 let {Utils} = require("utils");
 
-let firstRead = true;
-const BUFFER_SIZE = 0x80000;  // 512kB
+let webextension = require("webextension");
+let messageID = 0;
+let messageCallbacks = new Map();
 
-let IO = exports.IO =
+webextension.then(port =>
 {
-  /**
-   * Retrieves the platform-dependent line break string.
-   */
-  get lineBreak()
+  port.onMessage.addListener(message =>
   {
-    let lineBreak = (Services.appinfo.OS == "WINNT" ? "\r\n" : "\n");
-    Object.defineProperty(this, "lineBreak", {value: lineBreak});
-    return lineBreak;
-  },
+    let {id} = message;
+    let callbacks = messageCallbacks.get(id);
+    if (callbacks)
+    {
+      messageCallbacks.delete(id);
 
-  /**
-   * Tries to interpret a file path as an absolute path or a path relative to
-   * user's profile. Returns a file or null on failure.
-   */
-  resolveFilePath: function(/**String*/ path) /**nsIFile*/
-  {
-    if (!path)
-      return null;
+      if (message.success)
+        callbacks.resolve(message.result);
+      else
+        callbacks.reject(message.result);
+    }
+  });
+});
 
-    try {
-      // Assume an absolute path first
-      return new FileUtils.File(path);
-    } catch (e) {}
+function callWebExt(method, ...args)
+{
+  return webextension.then(port =>
+  {
+    return new Promise((resolve, reject) =>
+    {
+      let id = ++messageID;
+      messageCallbacks.set(id, {resolve, reject});
+      port.postMessage({id, method, args});
+    });
+  });
+}
 
-    try {
-      // Try relative path now
-      return FileUtils.getFile("ProfD", path.split("/"));
-    } catch (e) {}
+function attachCallback(promise, callback, fallback)
+{
+  promise.then(result =>
+  {
+    callback(null, result);
+  }).catch(error =>
+  {
+    if (fallback && error == "NoSuchFile")
+      fallback();
+    else
+      callback(error);
+  });
+}
 
-    return null;
-  },
+exports.IO =
+{
+  resolveFilePath: LegacyIO.resolveFilePath,
 
   /**
    * Reads strings from a file asynchronously, calls listener.process() with
    * each line read and with a null parameter once the read operation is done.
    * The callback will be called when the operation is done.
    */
-  readFromFile: function(/**nsIFile*/ file, /**Object*/ listener, /**Function*/ callback)
+  readFromFile(/**nsIFile*/ file, /**Object*/ listener, /**Function*/ callback)
   {
-    try
-    {
-      let processing = false;
-      let buffer = "";
-      let loaded = false;
-      let error = null;
-
-      let onProgress = function*(data)
+    attachCallback(
+      callWebExt("readFromFile", file.leafName).then(contents =>
       {
-        let index = (processing ? -1 : Math.max(data.lastIndexOf("\n"), data.lastIndexOf("\r")));
-        if (index >= 0)
+        return new Promise((resolve, reject) =>
         {
-          // Protect against reentrance in case the listener processes events.
-          processing = true;
-          try
-          {
-            let oldBuffer = buffer;
-            buffer = data.substr(index + 1);
-            data = data.substr(0, index + 1);
-            let lines = data.split(/[\r\n]+/);
-            lines.pop();
-            lines[0] = oldBuffer + lines[0];
-            for (let i = 0; i < lines.length; i++)
-            {
-              let promise = listener.process(lines[i]);
-              if (promise)
-                yield promise;
-            }
-          }
-          finally
-          {
-            processing = false;
-            data = buffer;
-            buffer = "";
-            yield* onProgress(data);
+          let lineIndex = 0;
 
-            if (loaded)
-            {
-              loaded = false;
-              onSuccess();
-            }
-
-            if (error)
+          function processBatch()
+          {
+            while (lineIndex < contents.length)
             {
-              let param = error;
-              error = null;
-              onError(param);
+              listener.process(contents[lineIndex++]);
+              if (lineIndex % 1000 == 0)
+              {
+                Utils.runAsync(processBatch);
+                return;
+              }
             }
-          }
-        }
-        else
-          buffer += data;
-      };
 
-      let onSuccess = function()
-      {
-        if (processing)
-        {
-          // Still processing data, delay processing this event.
-          loaded = true;
-          return;
-        }
-
-        // We are ignoring return value of listener.process() here because
-        // turning this callback into a generator would be complicated, and
-        // delaying isn't really necessary for the last two calls.
-        if (buffer !== "")
-          listener.process(buffer);
-        listener.process(null);
-
-        callback(null);
-      };
-
-      let onError = function(e)
-      {
-        if (processing)
-        {
-          // Still processing data, delay processing this event.
-          error = e;
-          return;
-        }
-
-        callback(e);
-      };
-
-      let decoder = new TextDecoder();
-      Task.spawn(function*()
-      {
-        if (firstRead && Services.vc.compare(Utils.platformVersion, "23.0a1") <= 0)
-        {
-          // See https://issues.adblockplus.org/ticket/530 - the first file
-          // opened cannot be closed due to Gecko bug 858723. Make sure that
-          // our patterns.ini file doesn't stay locked by opening a dummy file
-          // first.
-          try
-          {
-            let dummyPath = IO.resolveFilePath(Prefs.data_directory + "/dummy").path;
-            let dummy = yield OS.File.open(dummyPath, {write: true, truncate: true});
-            yield dummy.close();
-          }
-          catch (e)
-          {
-            // Dummy might be locked already, we don't care
+            listener.process(null);
+            resolve();
           }
-        }
-        firstRead = false;
 
-        let f = yield OS.File.open(file.path, {read: true});
-        while (true)
-        {
-          let array = yield f.read(BUFFER_SIZE);
-          if (!array.length)
-            break;
-
-          let data = decoder.decode(array, {stream: true});
-          yield* onProgress(data);
-        }
-        yield f.close();
-      }.bind(this)).then(onSuccess, onError);
-    }
-    catch (e)
-    {
-      callback(e);
-    }
+          processBatch();
+        });
+      }),
+      callback,
+      () => LegacyIO.readFromFile(file, listener, callback)
+    );
   },
 
   /**
    * Writes string data to a file in UTF-8 format asynchronously. The callback
    * will be called when the write operation is done.
    */
-  writeToFile: function(/**nsIFile*/ file, /**Iterator*/ data, /**Function*/ callback)
+  writeToFile(/**nsIFile*/ file, /**Iterator*/ data, /**Function*/ callback)
   {
-    try
-    {
-      let encoder = new TextEncoder();
-
-      Task.spawn(function*()
-      {
-        // This mimics OS.File.writeAtomic() but writes in chunks.
-        let tmpPath = file.path + ".tmp";
-        let f = yield OS.File.open(tmpPath, {write: true, truncate: true});
-
-        let buf = [];
-        let bufLen = 0;
-        let lineBreak = this.lineBreak;
-
-        function writeChunk()
-        {
-          let array = encoder.encode(buf.join(lineBreak) + lineBreak);
-          buf = [];
-          bufLen = 0;
-          return f.write(array);
-        }
-
-        for (let line of data)
-        {
-          buf.push(line);
-          bufLen += line.length;
-          if (bufLen >= BUFFER_SIZE)
-            yield writeChunk();
-        }
-
-        if (bufLen)
-          yield writeChunk();
-
-        // OS.File.flush() isn't exposed prior to Gecko 27, see bug 912457.
-        if (typeof f.flush == "function")
-          yield f.flush();
-        yield f.close();
-        yield OS.File.move(tmpPath, file.path, {noCopy: true});
-      }.bind(this)).then(callback.bind(null, null), callback);
-    }
-    catch (e)
-    {
-      callback(e);
-    }
+    attachCallback(
+      callWebExt("writeToFile", file.leafName, Array.from(data)),
+      callback
+    );
   },
 
   /**
    * Copies a file asynchronously. The callback will be called when the copy
    * operation is done.
    */
-  copyFile: function(/**nsIFile*/ fromFile, /**nsIFile*/ toFile, /**Function*/ callback)
+  copyFile(/**nsIFile*/ fromFile, /**nsIFile*/ toFile, /**Function*/ callback)
   {
-    try
-    {
-      let promise = OS.File.copy(fromFile.path, toFile.path);
-      promise.then(callback.bind(null, null), callback);
-    }
-    catch (e)
-    {
-      callback(e);
-    }
+    attachCallback(
+      callWebExt("copyFile", fromFile.leafName, toFile.leafName),
+      callback,
+      () => LegacyIO.copyFile(fromFile, toFile, callback)
+    );
   },
 
   /**
    * Renames a file within the same directory, will call callback when done.
    */
-  renameFile: function(/**nsIFile*/ fromFile, /**String*/ newName, /**Function*/ callback)
+  renameFile(/**nsIFile*/ fromFile, /**String*/ newName, /**Function*/ callback)
   {
-    try
-    {
-      let toFile = fromFile.clone();
-      toFile.leafName = newName;
-      let promise = OS.File.move(fromFile.path, toFile.path);
-      promise.then(callback.bind(null, null), callback);
-    }
-    catch(e)
-    {
-      callback(e);
-    }
+    attachCallback(
+      callWebExt("renameFile", fromFile.leafName, newName),
+      callback,
+      () => LegacyIO.renameFile(fromFile, newName, callback)
+    );
   },
 
   /**
    * Removes a file, will call callback when done.
    */
-  removeFile: function(/**nsIFile*/ file, /**Function*/ callback)
+  removeFile(/**nsIFile*/ file, /**Function*/ callback)
   {
-    try
-    {
-      let promise = OS.File.remove(file.path);
-      promise.then(callback.bind(null, null), callback);
-    }
-    catch(e)
-    {
-      callback(e);
-    }
+    attachCallback(
+      callWebExt("removeFile", file.leafName),
+      callback,
+      () => LegacyIO.removeFile(file, callback)
+    );
   },
 
   /**
    * Gets file information such as whether the file exists.
    */
-  statFile: function(/**nsIFile*/ file, /**Function*/ callback)
+  statFile(/**nsIFile*/ file, /**Function*/ callback)
   {
-    try
-    {
-      let promise = OS.File.stat(file.path);
-      promise.then(function onSuccess(info)
-      {
-        callback(null, {
-          exists: true,
-          isDirectory: info.isDir,
-          isFile: !info.isDir,
-          lastModified: info.lastModificationDate.getTime()
-        });
-      }, function onError(e)
-      {
-        if (e.becauseNoSuchFile)
-        {
-          callback(null, {
-            exists: false,
-            isDirectory: false,
-            isFile: false,
-            lastModified: 0
-          });
-        }
-        else
-          callback(e);
-      });
-    }
-    catch(e)
-    {
-      callback(e);
-    }
+    attachCallback(
+      callWebExt("statFile", file.leafName),
+      callback,
+      () => LegacyIO.statFile(file, callback)
+    );
   }
-}
+};
diff --git a/lib/io.js b/lib/legacyIO.js
similarity index 98%
copy from lib/io.js
copy to lib/legacyIO.js
index 4df5f2d..5549d96 100644
--- a/lib/io.js
+++ b/lib/legacyIO.js
@@ -94,11 +94,7 @@ let IO = exports.IO =
             lines.pop();
             lines[0] = oldBuffer + lines[0];
             for (let i = 0; i < lines.length; i++)
-            {
-              let promise = listener.process(lines[i]);
-              if (promise)
-                yield promise;
-            }
+              listener.process(lines[i]);
           }
           finally
           {
diff --git a/lib/prefs.json b/lib/prefs.json
index ca1a9fe..4cdd539 100644
--- a/lib/prefs.json
+++ b/lib/prefs.json
@@ -35,7 +35,6 @@
     "notificationurl": "https://notification.adblockplus.org/notification.json",
     "notificationdata": {},
     "subscriptions_antiadblockurl": "https://easylist-downloads.adblockplus.org/antiadblockfilters.txt",
-    "please_kill_startup_performance": false,
     "suppress_first_run_page": false,
     "notifications_showui": false,
     "notifications_ignoredcategories": []
diff --git a/lib/utils.js b/lib/utils.js
index 7558615..6df64b4 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -326,18 +326,10 @@ let Utils = exports.Utils =
   },
 
   /**
-   * Pauses code execution and allows events to be processed. Warning:
-   * other extension code might execute, the extension might even shut down.
+   * DEPRECATED, do not use!
    */
   yield: function()
   {
-    let {Prefs} = require("prefs");
-    if (Prefs.please_kill_startup_performance)
-    {
-      this.yield = function() {};
-      return;
-    }
-    return new Promise((resolve, reject) => Utils.runAsync(resolve));
   },
 
   /**
diff --git a/webextension/.eslintrc.json b/webextension/.eslintrc.json
new file mode 100644
index 0000000..ea12297
--- /dev/null
+++ b/webextension/.eslintrc.json
@@ -0,0 +1,7 @@
+{
+  "extends": "eslint-config-eyeo",
+  "root": true,
+  "env": {
+    "webextensions": true
+  }
+}
diff --git a/webextension/background.js b/webextension/background.js
new file mode 100644
index 0000000..88feb91
--- /dev/null
+++ b/webextension/background.js
@@ -0,0 +1,41 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2017 eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+/* global IO */
+
+let port = browser.runtime.connect();
+
+port.onMessage.addListener(message =>
+{
+  IO[message.method](...message.args).then(result =>
+  {
+    port.postMessage({
+      id: message.id,
+      success: true,
+      result
+    });
+  }).catch(error =>
+  {
+    port.postMessage({
+      id: message.id,
+      success: false,
+      result: String(error)
+    });
+  });
+});
diff --git a/webextension/io.js b/webextension/io.js
new file mode 100644
index 0000000..adfee2f
--- /dev/null
+++ b/webextension/io.js
@@ -0,0 +1,109 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-2017 eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+(function(exports)
+{
+  const keyPrefix = "file:";
+
+  function fileToKey(fileName)
+  {
+    return keyPrefix + fileName;
+  }
+
+  function loadFile(file)
+  {
+    let key = fileToKey(file);
+
+    return browser.storage.local.get(key).then(items =>
+    {
+      if (items.hasOwnProperty(key))
+        return items[key];
+
+      throw "NoSuchFile";
+    });
+  }
+
+  function saveFile(file, data)
+  {
+    return browser.storage.local.set({
+      [fileToKey(file)]: {
+        content: Array.from(data),
+        lastModified: Date.now()
+      }
+    });
+  }
+
+  function removeFile(file)
+  {
+    return browser.storage.local.remove(fileToKey(file));
+  }
+
+  exports.IO =
+  {
+    readFromFile(file)
+    {
+      return loadFile(file).then(entry =>
+      {
+        return entry.content;
+      });
+    },
+
+    writeToFile(file, data)
+    {
+      return saveFile(file, data);
+    },
+
+    copyFile(fromFile, toFile)
+    {
+      return loadFile(fromFile).then(entry =>
+      {
+        return saveFile(toFile, entry.content);
+      });
+    },
+
+    renameFile(fromFile, newName)
+    {
+      return loadFile(fromFile).then(entry =>
+      {
+        return browser.storage.local.set({
+          [fileToKey(newName)]: entry
+        });
+      }).then(() =>
+      {
+        return removeFile(fromFile);
+      });
+    },
+
+    removeFile(file)
+    {
+      return removeFile(file);
+    },
+
+    statFile(file)
+    {
+      return loadFile(file).then(entry =>
+      {
+        return {
+          exists: true,
+          lastModified: entry.lastModified
+        };
+      });
+    }
+  };
+})(this);
diff --git a/webextension/manifest.json b/webextension/manifest.json
new file mode 100644
index 0000000..46575e7
--- /dev/null
+++ b/webextension/manifest.json
@@ -0,0 +1,9 @@
+{
+  "manifest_version": 2,
+  "name": "Web Extensions I/O backend",
+  "version": "1.0",
+  "permissions": ["storage"],
+  "background": {
+    "scripts": ["background.js", "io.js"]
+  }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/adblock-plus.git



More information about the Pkg-mozext-commits mailing list