summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/parent/ext-topSites.js
blob: 76a815f1ef12ff89806f084a430ba4db4afcadad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* 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/. */

"use strict";

ChromeUtils.defineESModuleGetters(this, {
  NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
  getSearchProvider: "resource://activity-stream/lib/SearchShortcuts.sys.mjs",
});

XPCOMUtils.defineLazyModuleGetters(this, {
  AboutNewTab: "resource:///modules/AboutNewTab.jsm",
  shortURL: "resource://activity-stream/lib/ShortURL.jsm",
});

const SHORTCUTS_PREF =
  "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts";
const TOPSITES_FEED_PREF =
  "browser.newtabpage.activity-stream.feeds.system.topsites";

this.topSites = class extends ExtensionAPI {
  getAPI(context) {
    return {
      topSites: {
        get: async function (options) {
          // We fallback to newtab = false behavior if the user disabled their
          // Top Sites feed.
          let getNewtabSites =
            options.newtab &&
            Services.prefs.getBoolPref(TOPSITES_FEED_PREF, false);
          let links = getNewtabSites
            ? AboutNewTab.getTopSites()
            : await NewTabUtils.activityStreamLinks.getTopSites({
                ignoreBlocked: options.includeBlocked,
                onePerDomain: options.onePerDomain,
                numItems: options.limit,
                includeFavicon: options.includeFavicon,
              });

          if (options.includePinned && !getNewtabSites) {
            let pinnedLinks = NewTabUtils.pinnedLinks.links;
            if (options.includeFavicon) {
              pinnedLinks =
                NewTabUtils.activityStreamProvider._faviconBytesToDataURI(
                  await NewTabUtils.activityStreamProvider._addFavicons(
                    pinnedLinks
                  )
                );
            }
            pinnedLinks.forEach((pinnedLink, index) => {
              if (
                pinnedLink &&
                (!pinnedLink.searchTopSite || options.includeSearchShortcuts)
              ) {
                // Remove any dupes from history.
                links = links.filter(
                  link =>
                    link.url != pinnedLink.url &&
                    (!options.onePerDomain ||
                      NewTabUtils.extractSite(link.url) !=
                        pinnedLink.baseDomain)
                );
                links.splice(index, 0, pinnedLink);
              }
            });
          }

          // Convert links to search shortcuts, if necessary.
          if (
            options.includeSearchShortcuts &&
            Services.prefs.getBoolPref(SHORTCUTS_PREF, false) &&
            !getNewtabSites
          ) {
            // Pinned shortcuts are already returned as searchTopSite links,
            // with a proper label and url. But certain non-pinned links may
            // also be promoted to search shortcuts; here we convert them.
            links = links.map(link => {
              let searchProvider = getSearchProvider(shortURL(link));
              if (searchProvider) {
                link.searchTopSite = true;
                link.label = searchProvider.keyword;
                link.url = searchProvider.url;
              }
              return link;
            });
          }

          // Because we may have added links, we must crop again.
          if (typeof options.limit == "number") {
            links = links.slice(0, options.limit);
          }

          const makeDataURI = url => url && ExtensionUtils.makeDataURI(url);

          return Promise.all(
            links.map(async link => ({
              type: link.searchTopSite ? "search" : "url",
              url: link.url,
              // The newtab page allows the user to set custom site titles, which
              // are stored in `label`, so prefer it.  Search top sites currently
              // don't have titles but `hostname` instead.
              title: link.label || link.title || link.hostname || "",
              // Default top sites don't have a favicon property.  Instead they
              // have tippyTopIcon, a 96x96pt image used on the newtab page.
              // We'll use it as the favicon for now, but ideally default top
              // sites would have real favicons.  Non-default top sites (i.e.,
              // those from the user's history) will have favicons.
              favicon: options.includeFavicon
                ? link.favicon || (await makeDataURI(link.tippyTopIcon)) || null
                : null,
            }))
          );
        },
      },
    };
  }
};