Bug 1298211 - Implement chrome.topSites, r=aswan draft
authorShane Caraveo <scaraveo@mozilla.com>
Wed, 19 Oct 2016 13:52:27 -0700
changeset 427129 2e1e1e8890e1b195bd318d0635e56c73ac838c4f
parent 424520 22be4ae74653b25186665f22e52a50e7027fd36b
child 534392 b210275d545c0a69eda3b9e13fcd7ece78441e07
push id32934
push usermixedpuppy@gmail.com
push dateWed, 19 Oct 2016 20:53:15 +0000
reviewersaswan
bugs1298211
milestone52.0a1
Bug 1298211 - Implement chrome.topSites, r=aswan MozReview-Commit-ID: I043WQoDbrf
toolkit/components/extensions/ext-topSites.js
toolkit/components/extensions/extensions-toolkit.manifest
toolkit/components/extensions/jar.mn
toolkit/components/extensions/schemas/jar.mn
toolkit/components/extensions/schemas/top_sites.json
toolkit/components/extensions/test/xpcshell/test_ext_topSites.js
toolkit/components/extensions/test/xpcshell/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/ext-topSites.js
@@ -0,0 +1,24 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
+                                  "resource://gre/modules/NewTabUtils.jsm");
+
+extensions.registerSchemaAPI("topSites", "addon_parent", context => {
+  return {
+    topSites: {
+      get: function() {
+        let urls = NewTabUtils.links.getLinks()
+                              .filter(link => !!link)
+                              .map(link => {
+                                return {
+                                  url: link.url,
+                                  title: link.title
+                                };
+                              });
+        return Promise.resolve(urls);
+      },
+    },
+  };
+});
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -8,16 +8,17 @@ category webextension-scripts notificati
 category webextension-scripts i18n chrome://extensions/content/ext-i18n.js
 category webextension-scripts idle chrome://extensions/content/ext-idle.js
 category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
 category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
 category webextension-scripts runtime chrome://extensions/content/ext-runtime.js
 category webextension-scripts extension chrome://extensions/content/ext-extension.js
 category webextension-scripts storage chrome://extensions/content/ext-storage.js
 category webextension-scripts test chrome://extensions/content/ext-test.js
+category webextension-scripts topSites chrome://extensions/content/ext-topSites.js
 
 # scripts specific for content process.
 category webextension-scripts-content extension chrome://extensions/content/ext-c-extension.js
 category webextension-scripts-content i18n chrome://extensions/content/ext-i18n.js
 category webextension-scripts-content runtime chrome://extensions/content/ext-c-runtime.js
 
 # scripts that must run in the same process as addon code.
 category webextension-scripts-addon extension chrome://extensions/content/ext-c-extension.js
@@ -34,10 +35,11 @@ category webextension-schemas extension_
 category webextension-schemas i18n chrome://extensions/content/schemas/i18n.json
 category webextension-schemas idle chrome://extensions/content/schemas/idle.json
 category webextension-schemas management chrome://extensions/content/schemas/management.json
 category webextension-schemas native_host_manifest chrome://extensions/content/schemas/native_host_manifest.json
 category webextension-schemas notifications chrome://extensions/content/schemas/notifications.json
 category webextension-schemas runtime chrome://extensions/content/schemas/runtime.json
 category webextension-schemas storage chrome://extensions/content/schemas/storage.json
 category webextension-schemas test chrome://extensions/content/schemas/test.json
+category webextension-schemas top_sites chrome://extensions/content/schemas/top_sites.json
 category webextension-schemas web_navigation chrome://extensions/content/schemas/web_navigation.json
 category webextension-schemas web_request chrome://extensions/content/schemas/web_request.json
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -13,10 +13,11 @@ toolkit.jar:
     content/extensions/ext-i18n.js
     content/extensions/ext-idle.js
     content/extensions/ext-webRequest.js
     content/extensions/ext-webNavigation.js
     content/extensions/ext-runtime.js
     content/extensions/ext-extension.js
     content/extensions/ext-storage.js
     content/extensions/ext-test.js
+    content/extensions/ext-topSites.js
     content/extensions/ext-c-extension.js
     content/extensions/ext-c-runtime.js
--- a/toolkit/components/extensions/schemas/jar.mn
+++ b/toolkit/components/extensions/schemas/jar.mn
@@ -15,10 +15,11 @@ toolkit.jar:
     content/extensions/schemas/idle.json
     content/extensions/schemas/management.json
     content/extensions/schemas/manifest.json
     content/extensions/schemas/native_host_manifest.json
     content/extensions/schemas/notifications.json
     content/extensions/schemas/runtime.json
     content/extensions/schemas/storage.json
     content/extensions/schemas/test.json
+    content/extensions/schemas/top_sites.json
     content/extensions/schemas/web_navigation.json
     content/extensions/schemas/web_request.json
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/schemas/top_sites.json
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[
+  {
+    "namespace": "manifest",
+    "types": [
+      {
+        "$extend": "Permission",
+        "choices": [{
+          "type": "string",
+          "enum": [
+            "topSites"
+          ]
+        }]
+      }
+    ]
+  },
+  {
+    "namespace": "topSites",
+    "description": "Use the chrome.topSites API to access the top sites that are displayed on the new tab page. ",
+    "permissions": ["topSites"],
+    "types": [
+      {
+        "id": "MostVisitedURL",
+        "type": "object",
+        "description": "An object encapsulating a most visited URL, such as the URLs on the new tab page.",
+        "properties": {
+          "url": {
+            "type": "string",
+            "description": "The most visited URL."
+          },
+          "title": {
+            "type": "string",
+            "optional": true,
+            "description": "The title of the page."
+          }
+        }
+      }
+    ],
+    "functions": [
+      {
+        "name": "get",
+        "type": "function",
+        "description": "Gets a list of top sites.",
+        "async": "callback",
+        "parameters": [
+          {
+            "name": "callback",
+            "type": "function",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": {
+                  "$ref": "MostVisitedURL"
+                }
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_topSites.js
@@ -0,0 +1,82 @@
+Cu.import("resource://gre/modules/NewTabUtils.jsm");
+
+
+function TestProvider(getLinksFn) {
+  this.getLinks = getLinksFn;
+  this._observers = new Set();
+}
+
+TestProvider.prototype = {
+  addObserver: function (observer) {
+    this._observers.add(observer);
+  },
+  notifyLinkChanged: function (link, index=-1, deleted=false) {
+    this._notifyObservers("onLinkChanged", link, index, deleted);
+  },
+  notifyManyLinksChanged: function () {
+    this._notifyObservers("onManyLinksChanged");
+  },
+  _notifyObservers: function (observerMethodName, ...args) {
+    args.unshift(this);
+    for (let obs of this._observers) {
+      if (obs[observerMethodName])
+        obs[observerMethodName].apply(NewTabUtils.links, args);
+    }
+  },
+};
+
+function makeLinks(links) {
+  // Important: To avoid test failures due to clock jitter on Windows XP, call
+  // Date.now() once here, not each time through the loop.
+  let frecency = 0;
+  let now = Date.now() * 1000;
+  let places = [];
+  links.map((link, i) => {
+    places.push({
+      url: link.url,
+      title: link.title,
+      lastVisitDate: now - i,
+      frecency: frecency++
+    });
+  });
+  return places;
+}
+
+add_task(function* test_topSites() {
+  let expect = [{url: "http://example.com/", title: "site#-1"},
+                {url: "http://example0.com/", title: "site#0"},
+                {url: "http://example1.com/", title: "site#1"},
+                {url: "http://example2.com/", title: "site#2"},
+                {url: "http://example3.com/", title: "site#3"}];
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": [
+        "topSites"
+      ]
+    },
+    background() {
+      browser.topSites.get(result => {
+        browser.test.sendMessage("done", result);
+      });
+    }
+  });
+
+
+  let expectedLinks = makeLinks(expect);
+  let provider = new TestProvider(done => done(expectedLinks));
+
+  NewTabUtils.initWithoutProviders();
+  NewTabUtils.links.addProvider(provider);
+
+  yield NewTabUtils.links.populateCache();
+
+  yield extension.startup();
+
+  let result = yield extension.awaitMessage("done");
+  Assert.deepEqual(expect, result, "got topSites");
+
+  yield extension.unload();
+
+  NewTabUtils.links.removeProvider(provider);
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -50,15 +50,16 @@ skip-if = release_or_beta
 [test_ext_runtime_sendMessage_errors.js]
 [test_ext_runtime_sendMessage_no_receiver.js]
 [test_ext_runtime_sendMessage_self.js]
 [test_ext_schemas.js]
 [test_ext_schemas_api_injection.js]
 [test_ext_schemas_allowed_contexts.js]
 [test_ext_simple.js]
 [test_ext_storage.js]
+[test_ext_topSites.js]
 [test_getAPILevelForWindow.js]
 [test_ext_legacy_extension_context.js]
 [test_ext_legacy_extension_embedding.js]
 [test_locale_converter.js]
 [test_locale_data.js]
 [test_native_messaging.js]
 skip-if = os == "android"