tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UrlbarProviderInputHistory.sys.mjs (8881B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /**
      6 * This module exports a provider that offers input history (aka adaptive
      7 * history) results. These results map typed search strings to Urlbar results.
      8 * That way, a user can find a particular result again by typing the same
      9 * string.
     10 */
     11 
     12 import {
     13  UrlbarProvider,
     14  UrlbarUtils,
     15 } from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs";
     16 
     17 const lazy = {};
     18 
     19 ChromeUtils.defineESModuleGetters(lazy, {
     20  PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
     21  UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs",
     22  UrlbarProviderOpenTabs:
     23    "moz-src:///browser/components/urlbar/UrlbarProviderOpenTabs.sys.mjs",
     24  UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs",
     25 });
     26 
     27 ChromeUtils.defineLazyGetter(lazy, "SQL_ADAPTIVE_QUERY", () => {
     28  // Constants to support an alternative frecency algorithm.
     29  const PAGES_USE_ALT_FRECENCY =
     30    lazy.PlacesUtils.history.isAlternativeFrecencyEnabled;
     31  const PAGES_FRECENCY_FIELD = PAGES_USE_ALT_FRECENCY
     32    ? "alt_frecency"
     33    : "frecency";
     34  return `/* do not warn (bug 487789) */
     35   SELECT h.url,
     36          h.title,
     37          EXISTS(SELECT 1 FROM moz_bookmarks WHERE fk = h.id) AS bookmarked,
     38          ( SELECT title FROM moz_bookmarks WHERE fk = h.id AND title NOTNULL
     39            ORDER BY lastModified DESC LIMIT 1
     40          ) AS bookmark_title,
     41          ( SELECT GROUP_CONCAT(t.title ORDER BY t.title)
     42            FROM moz_bookmarks b
     43            JOIN moz_bookmarks t ON t.id = +b.parent AND t.parent = :parent
     44            WHERE b.fk = h.id
     45          ) AS tags,
     46          t.open_count,
     47          t.userContextId,
     48          h.last_visit_date
     49   FROM (
     50     SELECT ROUND(MAX(use_count) * (1 + (input = :search_string)), 1) AS rank,
     51            place_id
     52     FROM moz_inputhistory
     53     WHERE input BETWEEN :search_string AND :search_string || X'FFFF'
     54     GROUP BY place_id
     55   ) AS i
     56   JOIN moz_places h ON h.id = i.place_id
     57   LEFT JOIN moz_openpages_temp t
     58          ON t.url = h.url
     59          AND (t.userContextId = :userContextId OR (t.userContextId <> -1 AND :userContextId IS NULL))
     60   WHERE AUTOCOMPLETE_MATCH(NULL, h.url,
     61                            IFNULL(bookmark_title, h.title), tags,
     62                            h.visit_count, h.typed, bookmarked,
     63                            t.open_count,
     64                            :matchBehavior, :searchBehavior,
     65                            NULL)
     66   ORDER BY rank DESC, ${PAGES_FRECENCY_FIELD} DESC
     67   LIMIT :maxResults`;
     68 });
     69 
     70 /**
     71 * Class used to create the provider.
     72 */
     73 export class UrlbarProviderInputHistory extends UrlbarProvider {
     74  /**
     75   * @returns {Values<typeof UrlbarUtils.PROVIDER_TYPE>}
     76   */
     77  get type() {
     78    return UrlbarUtils.PROVIDER_TYPE.PROFILE;
     79  }
     80 
     81  /**
     82   * Whether this provider should be invoked for the given context.
     83   * If this method returns false, the providers manager won't start a query
     84   * with this provider, to save on resources.
     85   *
     86   * @param {UrlbarQueryContext} queryContext The query context object
     87   */
     88  async isActive(queryContext) {
     89    return (
     90      (lazy.UrlbarPrefs.get("suggest.history") ||
     91        lazy.UrlbarPrefs.get("suggest.bookmark") ||
     92        lazy.UrlbarPrefs.get("suggest.openpage")) &&
     93      !queryContext.searchMode
     94    );
     95  }
     96 
     97  /**
     98   * Starts querying.
     99   *
    100   * @param {UrlbarQueryContext} queryContext
    101   * @param {(provider: UrlbarProvider, result: UrlbarResult) => void} addCallback
    102   *   Callback invoked by the provider to add a new result.
    103   */
    104  async startQuery(queryContext, addCallback) {
    105    let instance = this.queryInstance;
    106 
    107    let conn = await lazy.PlacesUtils.promiseLargeCacheDBConnection();
    108    if (instance != this.queryInstance) {
    109      return;
    110    }
    111 
    112    let [query, params] = this._getAdaptiveQuery(queryContext);
    113    let rows = await conn.executeCached(query, params);
    114    if (instance != this.queryInstance) {
    115      return;
    116    }
    117 
    118    for (let row of rows) {
    119      const url = row.getResultByName("url");
    120      const openPageCount = row.getResultByName("open_count") || 0;
    121      const historyTitle = row.getResultByName("title") || "";
    122      const bookmarked = row.getResultByName("bookmarked");
    123      const bookmarkTitle = bookmarked
    124        ? row.getResultByName("bookmark_title")
    125        : null;
    126      const tags = row.getResultByName("tags") || "";
    127      let lastVisitPRTime = row.getResultByName("last_visit_date");
    128      let lastVisit = lastVisitPRTime
    129        ? lazy.PlacesUtils.toDate(lastVisitPRTime).getTime()
    130        : undefined;
    131 
    132      let resultTitle = historyTitle;
    133      if (openPageCount > 0 && lazy.UrlbarPrefs.get("suggest.openpage")) {
    134        if (url == queryContext.currentPage) {
    135          // Don't suggest switching to the current page.
    136          continue;
    137        }
    138        let userContextId = row.getResultByName("userContextId") || 0;
    139        let result = new lazy.UrlbarResult({
    140          type: UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
    141          source: UrlbarUtils.RESULT_SOURCE.TABS,
    142          payload: {
    143            url,
    144            title: resultTitle,
    145            icon: UrlbarUtils.getIconForUrl(url),
    146            userContextId,
    147            lastVisit,
    148            action: lazy.UrlbarPrefs.get("secondaryActions.switchToTab")
    149              ? UrlbarUtils.createTabSwitchSecondaryAction(userContextId)
    150              : undefined,
    151          },
    152          highlights: {
    153            url: UrlbarUtils.HIGHLIGHT.TYPED,
    154            title: UrlbarUtils.HIGHLIGHT.TYPED,
    155          },
    156        });
    157        addCallback(this, result);
    158        continue;
    159      }
    160 
    161      let resultSource;
    162      if (bookmarked && lazy.UrlbarPrefs.get("suggest.bookmark")) {
    163        resultSource = UrlbarUtils.RESULT_SOURCE.BOOKMARKS;
    164        resultTitle = bookmarkTitle || historyTitle;
    165      } else if (lazy.UrlbarPrefs.get("suggest.history")) {
    166        resultSource = UrlbarUtils.RESULT_SOURCE.HISTORY;
    167      } else {
    168        continue;
    169      }
    170 
    171      let resultTags = tags.split(",").filter(tag => {
    172        let lowerCaseTag = tag.toLocaleLowerCase();
    173        return queryContext.tokens.some(token =>
    174          lowerCaseTag.includes(token.lowerCaseValue)
    175        );
    176      });
    177 
    178      let isBlockable = resultSource == UrlbarUtils.RESULT_SOURCE.HISTORY;
    179 
    180      let result = new lazy.UrlbarResult({
    181        type: UrlbarUtils.RESULT_TYPE.URL,
    182        source: resultSource,
    183        payload: {
    184          url,
    185          title: resultTitle,
    186          tags: resultTags,
    187          icon: UrlbarUtils.getIconForUrl(url),
    188          isBlockable,
    189          blockL10n: isBlockable
    190            ? { id: "urlbar-result-menu-remove-from-history" }
    191            : undefined,
    192          helpUrl: isBlockable
    193            ? Services.urlFormatter.formatURLPref("app.support.baseURL") +
    194              "awesome-bar-result-menu"
    195            : undefined,
    196          lastVisit,
    197        },
    198        highlights: {
    199          url: UrlbarUtils.HIGHLIGHT.TYPED,
    200          title: UrlbarUtils.HIGHLIGHT.TYPED,
    201          tags: UrlbarUtils.HIGHLIGHT.TYPED,
    202        },
    203      });
    204 
    205      addCallback(this, result);
    206    }
    207  }
    208 
    209  onEngagement(queryContext, controller, details) {
    210    let { result } = details;
    211    if (
    212      details.selType == "dismiss" &&
    213      result.type == UrlbarUtils.RESULT_TYPE.URL
    214    ) {
    215      // Even if removing history normally also removes input history, that
    216      // doesn't happen if the page is bookmarked, so we do remove input history
    217      // regardless for this specific search term.
    218      UrlbarUtils.removeInputHistory(
    219        result.payload.url,
    220        queryContext.searchString
    221      ).catch(console.error);
    222      // Remove browsing history for the page.
    223      lazy.PlacesUtils.history.remove(result.payload.url).catch(console.error);
    224      controller.removeResult(result);
    225    }
    226  }
    227 
    228  /**
    229   * Obtains the query to search for adaptive results.
    230   *
    231   * @param {UrlbarQueryContext} queryContext
    232   *   The current queryContext.
    233   * @returns {Array} Contains the optimized query with which to search the
    234   *  database and an object containing the params to bound.
    235   */
    236  _getAdaptiveQuery(queryContext) {
    237    return [
    238      lazy.SQL_ADAPTIVE_QUERY,
    239      {
    240        parent: lazy.PlacesUtils.tagsFolderId,
    241        search_string: queryContext.lowerCaseSearchString,
    242        matchBehavior: Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE,
    243        searchBehavior: lazy.UrlbarPrefs.get("defaultBehavior"),
    244        userContextId: lazy.UrlbarPrefs.get("switchTabs.searchAllContainers")
    245          ? lazy.UrlbarProviderOpenTabs.getUserContextIdForOpenPagesTable(
    246              null,
    247              queryContext.isPrivate
    248            )
    249          : queryContext.userContextId,
    250        maxResults: queryContext.maxResults,
    251      },
    252    ];
    253  }
    254 }