Bug 1298211 - Implement chrome.topSites, r=aswan
authorShane Caraveo <scaraveo@mozilla.com>
Wed, 19 Oct 2016 13:52:27 -0700
changeset 318772 cbf9f5b47d561b51a014976f1f475629199c9417
parent 318771 806999bc0e965b71ac937e714fb0b2ab49750b44
child 318773 3b93af82598a9b975f166fc0da5607ad80ad2a8d
push id33363
push usermixedpuppy@gmail.com
push dateThu, 20 Oct 2016 16:20:31 +0000
treeherderautoland@cbf9f5b47d56 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1298211
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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"