[History] Refactor History to use mojom
Convert browsing history handler to use mojom instead of older webUI message handlers. Update unit tests to use new handler. Fix dangling ptrs. Also update browser tests to call handler. Remove unused methods from test browser proxy. Bug: 386401009 Change-Id: Ibcb7de1e25d7aaffbbe12069f65fecab42d30846 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6091897 Reviewed-by: John Lee <johntlee@chromium.org> Commit-Queue: Marlon Facey <mfacey@chromium.org> Reviewed-by: Ken Buchanan <kenrb@chromium.org> Cr-Commit-Position: refs/heads/main@{#1419930}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
bdbdceaddc
commit
c8d8f1ca8f
chrome
browser
browser_exposed_mojom_targets.gnitest
BUILD.gn
data
webui
history
BUILD.gnhistory_app_test.tshistory_drawer_test.tshistory_item_test.tshistory_list_focus_test.tshistory_list_test.tshistory_metrics_test.tshistory_overflow_menu_test.tshistory_routing_test.tshistory_routing_with_query_param_test.tshistory_supervised_user_test.tshistory_toolbar_test.tstest_browser_service.tstest_util.ts
ui/webui/resources/cr_components/history
@ -4376,6 +4376,7 @@ static_library("browser") {
|
||||
"//ui/webui",
|
||||
"//ui/webui/resources/cr_components/app_management:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/help_bubble:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history_clusters:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history_embeddings:mojo_bindings",
|
||||
]
|
||||
|
@ -121,6 +121,7 @@
|
||||
#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/history/history.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h"
|
||||
@ -488,6 +489,9 @@ void PopulateChromeWebUIFrameBinders(
|
||||
most_visited::mojom::MostVisitedPageHandlerFactory, NewTabPageUI,
|
||||
NewTabPageThirdPartyUI>(map);
|
||||
|
||||
RegisterWebUIControllerInterfaceBinder<history::mojom::PageHandler,
|
||||
HistoryUI>(map);
|
||||
|
||||
auto* history_clusters_service =
|
||||
HistoryClustersServiceFactory::GetForBrowserContext(
|
||||
render_frame_host->GetProcess()->GetBrowserContext());
|
||||
|
@ -22,6 +22,7 @@ import './product_specifications_lists.js';
|
||||
import {HelpBubbleMixin} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
|
||||
import type {HelpBubbleMixinInterface} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin.js';
|
||||
import {HistoryResultType} from 'chrome://resources/cr_components/history/constants.js';
|
||||
import type {HistoryEntry, HistoryQuery, PageCallbackRouter, PageHandlerRemote, QueryState} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {HistoryEmbeddingsBrowserProxyImpl} from 'chrome://resources/cr_components/history_embeddings/browser_proxy.js';
|
||||
import type {Suggestion} from 'chrome://resources/cr_components/history_embeddings/filter_chips.js';
|
||||
import type {HistoryEmbeddingsMoreActionsClickEvent} from 'chrome://resources/cr_components/history_embeddings/history_embeddings.js';
|
||||
@ -45,7 +46,7 @@ import {getTemplate} from './app.html.js';
|
||||
import type {BrowserService} from './browser_service.js';
|
||||
import {BrowserServiceImpl} from './browser_service.js';
|
||||
import {HistoryPageViewHistogram} from './constants.js';
|
||||
import type {ForeignSession, QueryResult, QueryState} from './externs.js';
|
||||
import type {ForeignSession} from './externs.js';
|
||||
import type {HistoryListElement} from './history_list.js';
|
||||
import type {HistoryToolbarElement} from './history_toolbar.js';
|
||||
import {convertDateToQueryValue} from './query_manager.js';
|
||||
@ -142,6 +143,12 @@ export interface HistoryAppElement {
|
||||
};
|
||||
}
|
||||
|
||||
export interface QueryResult {
|
||||
info?: HistoryQuery;
|
||||
value?: HistoryEntry[];
|
||||
sessionList?: ForeignSession[];
|
||||
}
|
||||
|
||||
const HistoryAppElementBase = mixinBehaviors(
|
||||
[IronScrollTargetBehavior],
|
||||
HelpBubbleMixin(FindShortcutMixin(
|
||||
@ -290,6 +297,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
|
||||
footerInfo: FooterInfo;
|
||||
private browserService_: BrowserService = BrowserServiceImpl.getInstance();
|
||||
private callbackRouter_: PageCallbackRouter;
|
||||
private enableHistoryEmbeddings_: boolean;
|
||||
private eventTracker_: EventTracker = new EventTracker();
|
||||
private hasDrawer_: boolean;
|
||||
@ -299,6 +307,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
private lastSelectedTab_: number;
|
||||
private contentPage_: string;
|
||||
private tabsContentPage_: string;
|
||||
private pageHandler_: PageHandlerRemote;
|
||||
private pendingDelete_: boolean;
|
||||
private queryResult_: QueryResult;
|
||||
private queryState_: QueryState;
|
||||
@ -311,6 +320,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
private tabsNames_: string[];
|
||||
private toolbarShadow_: boolean;
|
||||
private historyClustersViewStartTime_: Date|null = null;
|
||||
private onHasOtherFormsChangedListenerId_: number|null = null;
|
||||
private scrollTarget_: HTMLElement;
|
||||
private queryStateAfterDate_?: Date;
|
||||
private hasHistoryEmbeddingsResults_: boolean;
|
||||
@ -325,11 +335,13 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.pageHandler_ = BrowserServiceImpl.getInstance().handler;
|
||||
this.callbackRouter_ = BrowserServiceImpl.getInstance().callbackRouter;
|
||||
|
||||
this.queryResult_ = {
|
||||
info: undefined,
|
||||
results: undefined,
|
||||
sessionList: undefined,
|
||||
value: [],
|
||||
sessionList: [],
|
||||
};
|
||||
|
||||
listenForPrivilegedLinkClicks();
|
||||
@ -347,10 +359,6 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
this.addWebUiListener(
|
||||
'sign-in-state-changed',
|
||||
(signedIn: boolean) => this.onSignInStateChanged_(signedIn));
|
||||
this.addWebUiListener(
|
||||
'has-other-forms-changed',
|
||||
(hasOtherForms: boolean) =>
|
||||
this.onHasOtherFormsChanged_(hasOtherForms));
|
||||
this.addWebUiListener(
|
||||
'foreign-sessions-changed',
|
||||
(sessionList: ForeignSession[]) =>
|
||||
@ -364,6 +372,11 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
this.eventTracker_.add(
|
||||
mediaQuery, 'change',
|
||||
(e: MediaQueryListEvent) => this.hasDrawer_ = e.matches);
|
||||
|
||||
this.onHasOtherFormsChangedListenerId_ =
|
||||
this.callbackRouter_.onHasOtherFormsChanged.addListener(
|
||||
(hasOtherForms: boolean) =>
|
||||
this.onHasOtherFormsChanged_(hasOtherForms));
|
||||
}
|
||||
|
||||
override ready() {
|
||||
@ -415,6 +428,9 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
this.historyEmbeddingsResizeObserver_.disconnect();
|
||||
this.historyEmbeddingsResizeObserver_ = undefined;
|
||||
}
|
||||
assert(this.onHasOtherFormsChangedListenerId_);
|
||||
this.callbackRouter_.removeListener(this.onHasOtherFormsChangedListenerId_);
|
||||
this.onHasOtherFormsChangedListenerId_ = null;
|
||||
}
|
||||
|
||||
private fire_(eventName: string, detail?: any) {
|
||||
@ -548,7 +564,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
|
||||
private onQueryFinished_() {
|
||||
this.$.history.historyResult(
|
||||
this.queryResult_.info!, this.queryResult_.results!);
|
||||
this.queryResult_.info!, this.queryResult_.value!);
|
||||
if (document.body.classList.contains('loading')) {
|
||||
document.body.classList.remove('loading');
|
||||
this.onFirstRender_();
|
||||
@ -765,7 +781,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
if (!this.selectedPage_ || TABBED_PAGES.includes(this.selectedPage_)) {
|
||||
this.selectedPage_ = TABBED_PAGES[this.selectedTab_];
|
||||
}
|
||||
this.browserService_!.setLastSelectedTab(this.selectedTab_);
|
||||
this.pageHandler_.setLastSelectedTab(this.selectedTab_);
|
||||
}
|
||||
|
||||
private maybeUpdateSelectedHistoryTab_() {
|
||||
@ -918,7 +934,7 @@ export class HistoryAppElement extends HistoryAppElementBase {
|
||||
private onHistoryEmbeddingsItemRemoveClick_(
|
||||
e: HistoryEmbeddingsMoreActionsClickEvent) {
|
||||
const historyEmbeddingsItem = e.detail;
|
||||
this.browserService_.removeVisits([{
|
||||
this.pageHandler_.removeVisits([{
|
||||
url: historyEmbeddingsItem.url.url,
|
||||
timestamps: [historyEmbeddingsItem.lastUrlVisitTimestamp],
|
||||
}]);
|
||||
|
@ -2,67 +2,66 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import type {PageHandlerRemote} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {PageCallbackRouter, PageHandler} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {sendWithPromise} from 'chrome://resources/js/cr.js';
|
||||
|
||||
import {RESULTS_PER_PAGE} from './constants.js';
|
||||
import type {ForeignSession, HistoryEntry, HistoryQuery} from './externs.js';
|
||||
import type {ForeignSession} from './externs.js';
|
||||
|
||||
export type RemoveVisitsRequest = Array<{
|
||||
url: string,
|
||||
timestamps: number[],
|
||||
}>;
|
||||
|
||||
export interface QueryResult {
|
||||
info: HistoryQuery;
|
||||
value: HistoryEntry[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a singleton object, history.BrowserService, which
|
||||
* provides access to chrome.send APIs.
|
||||
*/
|
||||
|
||||
export interface BrowserService {
|
||||
handler: PageHandlerRemote;
|
||||
callbackRouter: PageCallbackRouter;
|
||||
getForeignSessions(): Promise<ForeignSession[]>;
|
||||
removeBookmark(url: string): void;
|
||||
removeVisits(removalList: RemoveVisitsRequest): Promise<void>;
|
||||
setLastSelectedTab(lastSelectedTab: number): void;
|
||||
openForeignSessionAllTabs(sessionTag: string): void;
|
||||
openForeignSessionTab(sessionTag: string, tabId: number, e: MouseEvent): void;
|
||||
deleteForeignSession(sessionTag: string): void;
|
||||
openClearBrowsingData(): void;
|
||||
recordHistogram(histogram: string, value: number, max: number): void;
|
||||
recordAction(action: string): void;
|
||||
recordTime(histogram: string, time: number): void;
|
||||
recordLongTime(histogram: string, time: number): void;
|
||||
navigateToUrl(url: string, target: string, e: MouseEvent): void;
|
||||
otherDevicesInitialized(): void;
|
||||
queryHistoryContinuation(): Promise<QueryResult>;
|
||||
queryHistory(searchTerm: string, beginTime?: number): Promise<QueryResult>;
|
||||
startTurnOnSyncFlow(): void;
|
||||
}
|
||||
|
||||
export class BrowserServiceImpl implements BrowserService {
|
||||
handler: PageHandlerRemote;
|
||||
callbackRouter: PageCallbackRouter;
|
||||
|
||||
constructor(handler: PageHandlerRemote, callbackRouter: PageCallbackRouter) {
|
||||
this.handler = handler;
|
||||
this.callbackRouter = callbackRouter;
|
||||
}
|
||||
|
||||
static getInstance(): BrowserService {
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
const handler = PageHandler.getRemote();
|
||||
const callbackRouter = new PageCallbackRouter();
|
||||
handler.setPage(callbackRouter.$.bindNewPipeAndPassRemote());
|
||||
return instance = new BrowserServiceImpl(handler, callbackRouter);
|
||||
}
|
||||
|
||||
static setInstance(obj: BrowserService) {
|
||||
instance = obj;
|
||||
}
|
||||
|
||||
getForeignSessions() {
|
||||
return sendWithPromise('getForeignSessions');
|
||||
}
|
||||
|
||||
removeBookmark(url: string) {
|
||||
chrome.send('removeBookmark', [url]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Promise that is resolved when items are deleted
|
||||
* successfully or rejected when deletion fails.
|
||||
*/
|
||||
removeVisits(removalList: RemoveVisitsRequest) {
|
||||
return sendWithPromise('removeVisits', removalList);
|
||||
}
|
||||
|
||||
setLastSelectedTab(lastSelectedTab: number) {
|
||||
chrome.send('setLastSelectedTab', [lastSelectedTab]);
|
||||
}
|
||||
|
||||
openForeignSessionAllTabs(sessionTag: string) {
|
||||
chrome.send('openForeignSessionAllTabs', [sessionTag]);
|
||||
}
|
||||
@ -83,10 +82,6 @@ export class BrowserServiceImpl implements BrowserService {
|
||||
chrome.send('deleteForeignSession', [sessionTag]);
|
||||
}
|
||||
|
||||
openClearBrowsingData() {
|
||||
chrome.send('clearBrowsingData');
|
||||
}
|
||||
|
||||
recordHistogram(histogram: string, value: number, max: number) {
|
||||
chrome.send('metricsHandler:recordInHistogram', [histogram, value, max]);
|
||||
}
|
||||
@ -123,26 +118,9 @@ export class BrowserServiceImpl implements BrowserService {
|
||||
chrome.send('otherDevicesInitialized');
|
||||
}
|
||||
|
||||
queryHistoryContinuation() {
|
||||
return sendWithPromise('queryHistoryContinuation');
|
||||
}
|
||||
|
||||
queryHistory(searchTerm: string, beginTime?: number) {
|
||||
return sendWithPromise(
|
||||
'queryHistory', searchTerm, RESULTS_PER_PAGE, beginTime);
|
||||
}
|
||||
|
||||
startTurnOnSyncFlow() {
|
||||
chrome.send('startTurnOnSyncFlow');
|
||||
}
|
||||
|
||||
static getInstance(): BrowserService {
|
||||
return instance || (instance = new BrowserServiceImpl());
|
||||
}
|
||||
|
||||
static setInstance(obj: BrowserService) {
|
||||
instance = obj;
|
||||
}
|
||||
}
|
||||
|
||||
let instance: BrowserService|null = null;
|
||||
|
@ -6,54 +6,6 @@
|
||||
* @fileoverview Externs for objects sent from C++ to chrome://history.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The type of the debug object nested in the history result object. The
|
||||
* definition is based on chrome/browser/ui/webui/browsing_history_handler.cc:
|
||||
* HistoryEntryToValue()
|
||||
*/
|
||||
export interface HistoryEntryDebug {
|
||||
isUrlInLocalDatabase: boolean;
|
||||
visitCount: number;
|
||||
typedCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the history result object. The definition is based on
|
||||
* chrome/browser/ui/webui/browsing_history_handler.cc: HistoryEntryToValue()
|
||||
*/
|
||||
export interface HistoryEntry {
|
||||
allTimestamps: number[];
|
||||
remoteIconUrlForUma: string;
|
||||
isUrlInRemoteUserData: boolean;
|
||||
blockedVisit: boolean;
|
||||
dateRelativeDay: string;
|
||||
dateShort: string;
|
||||
dateTimeOfDay: string;
|
||||
deviceName: string;
|
||||
deviceType: string;
|
||||
domain: string;
|
||||
fallbackFaviconText: string;
|
||||
hostFilteringBehavior: number;
|
||||
snippet: string;
|
||||
starred: boolean;
|
||||
time: number;
|
||||
title: string;
|
||||
url: string;
|
||||
selected: boolean;
|
||||
readableTimestamp: string;
|
||||
debug?: HistoryEntryDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the history results info object. The definition is based on
|
||||
* chrome/browser/ui/webui/browsing_history_handler.cc:
|
||||
* BrowsingHistoryHandler::QueryComplete()
|
||||
*/
|
||||
export interface HistoryQuery {
|
||||
finished: boolean;
|
||||
term: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the foreign session tab object. This definition is based on
|
||||
* chrome/browser/ui/webui/foreign_session_handler.cc:
|
||||
@ -91,16 +43,3 @@ export interface ForeignSession {
|
||||
timestamp: number;
|
||||
windows: ForeignSessionWindow[];
|
||||
}
|
||||
|
||||
export interface QueryState {
|
||||
incremental: boolean;
|
||||
querying: boolean;
|
||||
searchTerm: string;
|
||||
after?: string;
|
||||
}
|
||||
|
||||
export interface QueryResult {
|
||||
info?: HistoryQuery;
|
||||
results?: HistoryEntry[];
|
||||
sessionList?: ForeignSession[];
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import './app.js';
|
||||
export {PageCallbackRouter as ProductSpecificationsCallbackRouter} from 'chrome://resources/cr_components/commerce/product_specifications.mojom-webui.js';
|
||||
export {ProductSpecificationsBrowserProxyImpl} from 'chrome://resources/cr_components/commerce/product_specifications_browser_proxy.js';
|
||||
export {ShoppingServiceBrowserProxyImpl} from 'chrome://resources/cr_components/commerce/shopping_service_browser_proxy.js';
|
||||
export {HistoryEntry, QueryResult} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
export {BrowserProxyImpl} from 'chrome://resources/cr_components/history_clusters/browser_proxy.js';
|
||||
export {ClusterAction, PageCallbackRouter, PageHandlerRemote, RelatedSearchAction, VisitAction, VisitType} from 'chrome://resources/cr_components/history_clusters/history_clusters.mojom-webui.js';
|
||||
export {MetricsProxy, MetricsProxyImpl} from 'chrome://resources/cr_components/history_clusters/metrics_proxy.js';
|
||||
@ -20,9 +21,9 @@ export {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialo
|
||||
export {CrRouter} from 'chrome://resources/js/cr_router.js';
|
||||
export {getTrustedHTML} from 'chrome://resources/js/static_types.js';
|
||||
export {ensureLazyLoaded, HistoryAppElement, listenForPrivilegedLinkClicks} from './app.js';
|
||||
export {BrowserService, BrowserServiceImpl, QueryResult, RemoveVisitsRequest} from './browser_service.js';
|
||||
export {BrowserService, BrowserServiceImpl, RemoveVisitsRequest} from './browser_service.js';
|
||||
export {HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from './constants.js';
|
||||
export {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryEntry, HistoryQuery} from './externs.js';
|
||||
export {ForeignSession, ForeignSessionTab, ForeignSessionWindow} from './externs.js';
|
||||
export {HISTORY_EMBEDDINGS_ANSWERS_PROMO_SHOWN_KEY, HISTORY_EMBEDDINGS_PROMO_SHOWN_KEY, HistoryEmbeddingsPromoElement} from './history_embeddings_promo.js';
|
||||
export {HistoryItemElement} from './history_item.js';
|
||||
export {ActionMenuModel, HistoryListElement} from './history_list.js';
|
||||
|
@ -12,6 +12,7 @@ import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
|
||||
import 'chrome://resources/js/icon.js';
|
||||
|
||||
import {HistoryResultType} from 'chrome://resources/cr_components/history/constants.js';
|
||||
import type {HistoryEntry} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
|
||||
import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
|
||||
import {FocusRowMixin} from 'chrome://resources/cr_elements/focus_row_mixin.js';
|
||||
@ -22,7 +23,6 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||
import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
|
||||
import {BrowserServiceImpl} from './browser_service.js';
|
||||
import type {HistoryEntry} from './externs.js';
|
||||
import {getTemplate} from './history_item.html.js';
|
||||
|
||||
export interface HistoryItemElement {
|
||||
@ -268,7 +268,7 @@ export class HistoryItemElement extends HistoryItemElementBase {
|
||||
}
|
||||
|
||||
const browserService = BrowserServiceImpl.getInstance();
|
||||
browserService.removeBookmark(this.item.url);
|
||||
browserService.handler.removeBookmark(this.item.url);
|
||||
browserService.recordAction('BookmarkStarClicked');
|
||||
|
||||
this.fire_('remove-bookmark-stars', this.item.url);
|
||||
|
@ -8,21 +8,21 @@ import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
|
||||
import './shared_style.css.js';
|
||||
import './history_item.js';
|
||||
|
||||
import type {HistoryEntry, HistoryQuery, PageCallbackRouter, PageHandlerRemote, QueryState} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
|
||||
import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
|
||||
import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
|
||||
import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
|
||||
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
|
||||
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
|
||||
import {assert} from 'chrome://resources/js/assert.js';
|
||||
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||
import {getDeepActiveElement} from 'chrome://resources/js/util.js';
|
||||
import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
|
||||
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
|
||||
import type {BrowserService} from './browser_service.js';
|
||||
import {BrowserServiceImpl} from './browser_service.js';
|
||||
import {BROWSING_GAP_TIME} from './constants.js';
|
||||
import type {HistoryEntry, HistoryQuery, QueryState} from './externs.js';
|
||||
import type {HistoryItemElement} from './history_item.js';
|
||||
import {getTemplate} from './history_list.html.js';
|
||||
|
||||
@ -56,7 +56,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const HistoryListElementBase = WebUiListenerMixin(I18nMixin(PolymerElement));
|
||||
const HistoryListElementBase = I18nMixin(PolymerElement);
|
||||
|
||||
export class HistoryListElement extends HistoryListElementBase {
|
||||
static get is() {
|
||||
@ -124,10 +124,15 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
}
|
||||
|
||||
private historyData_: HistoryEntry[];
|
||||
private browserService_: BrowserService = BrowserServiceImpl.getInstance();
|
||||
private callbackRouter_: PageCallbackRouter =
|
||||
BrowserServiceImpl.getInstance().callbackRouter;
|
||||
private canDeleteHistory_: boolean =
|
||||
loadTimeData.getBoolean('allowDeletingHistory');
|
||||
private actionMenuModel_: ActionMenuModel|null = null;
|
||||
private lastOffsetHeight_: number = 0;
|
||||
private pageHandler_: PageHandlerRemote =
|
||||
BrowserServiceImpl.getInstance().handler;
|
||||
private resizeObserver_: ResizeObserver = new ResizeObserver(() => {
|
||||
if (this.lastOffsetHeight_ === 0) {
|
||||
this.lastOffsetHeight_ = this.scrollTarget.offsetHeight;
|
||||
@ -151,11 +156,14 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
queryState: QueryState;
|
||||
scrollTarget: HTMLElement = document.documentElement;
|
||||
scrollOffset: number = 0;
|
||||
private onHistoryDeletedListenerId_: number|null = null;
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.setAttribute('aria-roledescription', this.i18n('ariaRoleDescription'));
|
||||
this.addWebUiListener('history-deleted', () => this.onHistoryDeleted_());
|
||||
this.onHistoryDeletedListenerId_ =
|
||||
this.callbackRouter_.onHistoryDeleted.addListener(
|
||||
this.onHistoryDeleted_.bind(this));
|
||||
}
|
||||
|
||||
override ready() {
|
||||
@ -169,6 +177,13 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
'remove-bookmark-stars', e => this.onRemoveBookmarkStars_(e));
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
assert(this.onHistoryDeletedListenerId_);
|
||||
this.callbackRouter_.removeListener(this.onHistoryDeletedListenerId_);
|
||||
this.onHistoryDeletedListenerId_ = null;
|
||||
}
|
||||
|
||||
private fire_(eventName: string, detail?: any) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(eventName, {bubbles: true, composed: true, detail}));
|
||||
@ -300,10 +315,9 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
return;
|
||||
}
|
||||
|
||||
const browserService = BrowserServiceImpl.getInstance();
|
||||
browserService.recordAction('RemoveSelected');
|
||||
this.browserService_.recordAction('RemoveSelected');
|
||||
if (this.queryState.searchTerm !== '') {
|
||||
browserService.recordAction('SearchResultRemove');
|
||||
this.browserService_.recordAction('SearchResultRemove');
|
||||
}
|
||||
this.$.dialog.get().showModal();
|
||||
|
||||
@ -404,7 +418,7 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
// Event listeners:
|
||||
|
||||
private onDialogConfirmClick_() {
|
||||
BrowserServiceImpl.getInstance().recordAction('ConfirmRemoveSelected');
|
||||
this.browserService_.recordAction('ConfirmRemoveSelected');
|
||||
|
||||
this.deleteSelected_();
|
||||
const dialog = this.$.dialog.getIfExists();
|
||||
@ -413,7 +427,7 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
}
|
||||
|
||||
private onDialogCancelClick_() {
|
||||
BrowserServiceImpl.getInstance().recordAction('CancelRemoveSelected');
|
||||
this.browserService_.recordAction('CancelRemoveSelected');
|
||||
|
||||
const dialog = this.$.dialog.getIfExists();
|
||||
assert(dialog);
|
||||
@ -466,7 +480,7 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
}
|
||||
|
||||
private onMoreFromSiteClick_() {
|
||||
BrowserServiceImpl.getInstance().recordAction('EntryMenuShowMoreFromSite');
|
||||
this.browserService_.recordAction('EntryMenuShowMoreFromSite');
|
||||
|
||||
assert(this.$.sharedMenu.getIfExists());
|
||||
this.fire_(
|
||||
@ -482,19 +496,17 @@ export class HistoryListElement extends HistoryListElementBase {
|
||||
}));
|
||||
|
||||
this.pendingDelete = true;
|
||||
return BrowserServiceImpl.getInstance().removeVisits(removalList);
|
||||
return this.pageHandler_.removeVisits(removalList);
|
||||
}
|
||||
|
||||
private onRemoveBookmarkClick_() {
|
||||
const browserService = BrowserServiceImpl.getInstance();
|
||||
browserService.removeBookmark(this.actionMenuModel_!.item.url);
|
||||
this.pageHandler_.removeBookmark(this.actionMenuModel_!.item.url);
|
||||
this.fire_('remove-bookmark-stars', this.actionMenuModel_!.item.url);
|
||||
this.closeMenu_();
|
||||
}
|
||||
|
||||
private onRemoveFromHistoryClick_() {
|
||||
const browserService = BrowserServiceImpl.getInstance();
|
||||
browserService.recordAction('EntryMenuRemoveFromHistory');
|
||||
this.browserService_.recordAction('EntryMenuRemoveFromHistory');
|
||||
|
||||
assert(!this.pendingDelete);
|
||||
assert(this.$.sharedMenu.getIfExists());
|
||||
|
@ -3,12 +3,13 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import {HistoryEmbeddingsUserActions, QUERY_RESULT_MINIMUM_AGE} from 'chrome://resources/cr_components/history/constants.js';
|
||||
import type {QueryResult, QueryState} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {EventTracker} from 'chrome://resources/js/event_tracker.js';
|
||||
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
|
||||
import {BrowserServiceImpl} from './browser_service.js';
|
||||
import type {HistoryEntry, HistoryQuery, QueryResult, QueryState} from './externs.js';
|
||||
import {RESULTS_PER_PAGE} from './constants.js';
|
||||
import type {HistoryRouterElement} from './router.js';
|
||||
|
||||
// Converts a JS Date object to a human readable string in the format of
|
||||
@ -81,6 +82,7 @@ export class HistoryQueryManagerElement extends PolymerElement {
|
||||
// A query is initiated by page load.
|
||||
querying: true,
|
||||
searchTerm: '',
|
||||
after: '',
|
||||
};
|
||||
}
|
||||
|
||||
@ -121,10 +123,12 @@ export class HistoryQueryManagerElement extends PolymerElement {
|
||||
|
||||
const browserService = BrowserServiceImpl.getInstance();
|
||||
const promise = incremental ?
|
||||
browserService.queryHistoryContinuation() :
|
||||
browserService.queryHistory(this.queryState.searchTerm, afterTimestamp);
|
||||
browserService.handler.queryHistoryContinuation() :
|
||||
browserService.handler.queryHistory(
|
||||
this.queryState.searchTerm, RESULTS_PER_PAGE,
|
||||
afterTimestamp ? afterTimestamp : null);
|
||||
// Ignore rejected (cancelled) queries.
|
||||
promise.then(result => this.onQueryResult_(result), () => {});
|
||||
promise.then((result) => this.onQueryResult_(result.results), () => {});
|
||||
}
|
||||
|
||||
private onChangeQuery_(e: CustomEvent<{search?: string, after?: string}>) {
|
||||
@ -160,10 +164,10 @@ export class HistoryQueryManagerElement extends PolymerElement {
|
||||
/**
|
||||
* @param results List of results with information about the query.
|
||||
*/
|
||||
private onQueryResult_(results: {info: HistoryQuery, value: HistoryEntry[]}) {
|
||||
private onQueryResult_(results: QueryResult) {
|
||||
this.set('queryState.querying', false);
|
||||
this.set('queryResult.info', results.info);
|
||||
this.set('queryResult.results', results.value);
|
||||
this.set('queryResult.value', results.value);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('query-finished', {bubbles: true, composed: true}));
|
||||
}
|
||||
|
@ -2,12 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import type {QueryState} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {CrRouter} from 'chrome://resources/js/cr_router.js';
|
||||
import {EventTracker} from 'chrome://resources/js/event_tracker.js';
|
||||
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
|
||||
import type {QueryState} from './externs.js';
|
||||
|
||||
// All valid pages.
|
||||
// TODO(crbug.com/40069898): Change this to an enum and use that type for holding
|
||||
// these values for better type check when `loadTimeData` is no longer needed.
|
||||
|
@ -132,7 +132,7 @@ export class HistorySideBarElement extends PolymerElement {
|
||||
private onClearBrowsingDataClick_(e: Event) {
|
||||
const browserService = BrowserServiceImpl.getInstance();
|
||||
browserService.recordAction('InitClearBrowsingData');
|
||||
browserService.openClearBrowsingData();
|
||||
browserService.handler.openClearBrowsingDataDialog();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
|
@ -1860,6 +1860,7 @@ static_library("ui") {
|
||||
"//ui/web_dialogs",
|
||||
"//ui/webui/resources/cr_components/app_management:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/help_bubble:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history_clusters:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history_embeddings:mojo_bindings",
|
||||
"//ui/webui/resources/js/browser_command:mojo_bindings",
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "components/supervised_user/core/browser/supervised_user_preferences.h"
|
||||
#include "components/supervised_user/core/browser/supervised_user_service.h"
|
||||
#include "components/supervised_user/core/browser/supervised_user_url_filter.h"
|
||||
#include "components/supervised_user/core/browser/supervised_user_utils.h"
|
||||
#include "components/sync/protocol/sync_enums.pb.h"
|
||||
#include "components/sync/service/sync_service.h"
|
||||
#include "components/sync_device_info/device_info.h"
|
||||
@ -61,6 +62,7 @@
|
||||
#include "content/public/browser/web_ui.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/l10n/time_format.h"
|
||||
#include "ui/webui/resources/cr_components/history/history.mojom.h"
|
||||
|
||||
using bookmarks::BookmarkModel;
|
||||
using history::BrowsingHistoryService;
|
||||
@ -115,11 +117,8 @@ void GetDeviceNameAndType(const syncer::DeviceInfoTracker* tracker,
|
||||
}
|
||||
|
||||
// Formats `entry`'s URL and title and adds them to `result`.
|
||||
void SetHistoryEntryUrlAndTitle(
|
||||
const BrowsingHistoryService::HistoryEntry& entry,
|
||||
base::Value::Dict* result) {
|
||||
result->Set("url", entry.url.spec());
|
||||
|
||||
std::pair<std::string, std::string> SetHistoryEntryUrlAndTitle(
|
||||
const BrowsingHistoryService::HistoryEntry& entry) {
|
||||
bool using_url_as_the_title = false;
|
||||
std::u16string title_to_set(entry.title);
|
||||
if (entry.title.empty()) {
|
||||
@ -145,7 +144,7 @@ void SetHistoryEntryUrlAndTitle(
|
||||
title_to_set.resize(kShortTitleLength);
|
||||
}
|
||||
|
||||
result->Set("title", title_to_set);
|
||||
return std::make_tuple(entry.url.spec(), base::UTF16ToUTF8(title_to_set));
|
||||
}
|
||||
|
||||
// Helper function to check if entry is present in local database (local-side
|
||||
@ -185,15 +184,32 @@ constexpr UrlIdentity::FormatOptions url_identity_options{
|
||||
.default_options = {UrlIdentity::DefaultFormatOptions::
|
||||
kOmitSchemePathAndTrivialSubdomains}};
|
||||
|
||||
// Converts `entry` to a base::Value::Dict to be owned by the caller.
|
||||
base::Value::Dict HistoryEntryToValue(
|
||||
history::mojom::FilteringBehavior FilteringBehaviorToMojom(
|
||||
supervised_user::FilteringBehavior filtering_behavior) {
|
||||
switch (filtering_behavior) {
|
||||
case supervised_user::FilteringBehavior::kAllow:
|
||||
return history::mojom::FilteringBehavior::kAllow;
|
||||
case supervised_user::FilteringBehavior::kBlock:
|
||||
return history::mojom::FilteringBehavior::kBlock;
|
||||
case supervised_user::FilteringBehavior::kInvalid:
|
||||
return history::mojom::FilteringBehavior::kInvalid;
|
||||
default:
|
||||
return history::mojom::FilteringBehavior::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts `entry` to a history::mojom::QueryResult to be owned by the caller.
|
||||
history::mojom::HistoryEntryPtr HistoryEntryToMojom(
|
||||
const BrowsingHistoryService::HistoryEntry& entry,
|
||||
BookmarkModel* bookmark_model,
|
||||
Profile& profile,
|
||||
const syncer::DeviceInfoTracker* tracker,
|
||||
base::Clock* clock) {
|
||||
base::Value::Dict result;
|
||||
SetHistoryEntryUrlAndTitle(entry, &result);
|
||||
auto result_mojom = history::mojom::HistoryEntry::New();
|
||||
base::Value::Dict dictionary;
|
||||
auto url_and_title = SetHistoryEntryUrlAndTitle(entry);
|
||||
result_mojom->url = url_and_title.first;
|
||||
result_mojom->title = url_and_title.second;
|
||||
|
||||
// UrlIdentity holds a user-identifiable string for a URL. We will display
|
||||
// this string to the user.
|
||||
@ -212,31 +228,31 @@ base::Value::Dict HistoryEntryToValue(
|
||||
// chrome/browser/resources/history/history.js in @typedef for
|
||||
// HistoryEntry. Please update it whenever you add or remove
|
||||
// any keys in result.
|
||||
result.Set("domain", domain);
|
||||
result_mojom->domain = base::UTF16ToUTF8(domain);
|
||||
|
||||
result.Set("fallbackFaviconText",
|
||||
base::UTF16ToASCII(favicon::GetFallbackIconText(entry.url)));
|
||||
result_mojom->fallback_favicon_text =
|
||||
base::UTF16ToASCII(favicon::GetFallbackIconText(entry.url));
|
||||
|
||||
result.Set("time", entry.time.InMillisecondsFSinceUnixEpoch());
|
||||
result_mojom->time = entry.time.InMillisecondsFSinceUnixEpoch();
|
||||
|
||||
// Pass the timestamps in a list.
|
||||
base::Value::List timestamps;
|
||||
std::vector<double> timestamps;
|
||||
for (int64_t timestamp : entry.all_timestamps) {
|
||||
timestamps.Append(
|
||||
timestamps.push_back(
|
||||
base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(timestamp))
|
||||
.InMillisecondsFSinceUnixEpoch());
|
||||
}
|
||||
result.Set("allTimestamps", std::move(timestamps));
|
||||
result_mojom->all_timestamps = std::move(timestamps);
|
||||
|
||||
// Always pass the short date since it is needed both in the search and in
|
||||
// the monthly view.
|
||||
result.Set("dateShort", base::TimeFormatShortDate(entry.time));
|
||||
result_mojom->date_short =
|
||||
base::UTF16ToUTF8(base::TimeFormatShortDate(entry.time));
|
||||
|
||||
std::u16string snippet_string;
|
||||
std::u16string date_relative_day;
|
||||
std::u16string date_time_of_day;
|
||||
bool is_blocked_visit = false;
|
||||
int host_filtering_behavior = -1;
|
||||
|
||||
// Only pass in the strings we need (search results need a shortdate
|
||||
// and snippet, browse results need day and time information). Makes sure that
|
||||
@ -263,126 +279,89 @@ base::Value::Dict HistoryEntryToValue(
|
||||
if (!entry.client_id.empty()) {
|
||||
GetDeviceNameAndType(tracker, entry.client_id, &device_name, &device_type);
|
||||
}
|
||||
result.Set("deviceName", device_name);
|
||||
result.Set("deviceType", device_type);
|
||||
|
||||
result_mojom->device_name = device_name;
|
||||
result_mojom->device_type = device_type;
|
||||
|
||||
supervised_user::FilteringBehavior filtering_behavior;
|
||||
if (profile.IsChild()) {
|
||||
supervised_user::SupervisedUserService* supervised_user_service =
|
||||
SupervisedUserServiceFactory::GetForProfile(&profile);
|
||||
supervised_user::SupervisedUserURLFilter* url_filter =
|
||||
supervised_user_service->GetURLFilter();
|
||||
filtering_behavior =
|
||||
url_filter->GetFilteringBehavior(entry.url.GetWithEmptyPath()).behavior;
|
||||
is_blocked_visit = entry.blocked_visit;
|
||||
host_filtering_behavior = static_cast<int>(
|
||||
url_filter->GetFilteringBehavior(entry.url.GetWithEmptyPath())
|
||||
.behavior);
|
||||
result_mojom->host_filtering_behavior =
|
||||
FilteringBehaviorToMojom(filtering_behavior);
|
||||
} else {
|
||||
result_mojom->host_filtering_behavior =
|
||||
history::mojom::FilteringBehavior::kUnknown;
|
||||
}
|
||||
|
||||
result.Set("dateTimeOfDay", date_time_of_day);
|
||||
result.Set("dateRelativeDay", date_relative_day);
|
||||
result.Set("snippet", snippet_string);
|
||||
result.Set("starred", bookmark_model->IsBookmarked(entry.url));
|
||||
result.Set("hostFilteringBehavior", host_filtering_behavior);
|
||||
result.Set("blockedVisit", is_blocked_visit);
|
||||
result.Set("isUrlInRemoteUserData", IsEntryInRemoteUserData(entry));
|
||||
result.Set("remoteIconUrlForUma", entry.remote_icon_url_for_uma.spec());
|
||||
result_mojom->date_time_of_day = base::UTF16ToUTF8(date_time_of_day);
|
||||
result_mojom->date_relative_day = base::UTF16ToUTF8(date_relative_day);
|
||||
result_mojom->snippet = base::UTF16ToUTF8(snippet_string);
|
||||
result_mojom->starred = bookmark_model->IsBookmarked(entry.url);
|
||||
result_mojom->blocked_visit = is_blocked_visit;
|
||||
result_mojom->is_url_in_remote_user_data = IsEntryInRemoteUserData(entry);
|
||||
result_mojom->remote_icon_url_for_uma = entry.remote_icon_url_for_uma.spec();
|
||||
|
||||
// Additional debugging fields shown only if the debug feature is enabled.
|
||||
if (history_clusters::GetConfig().user_visible_debug) {
|
||||
base::Value::Dict debug;
|
||||
debug.Set("isUrlInLocalDatabase", IsUrlInLocalDatabase(entry));
|
||||
debug.Set("visitCount", entry.visit_count);
|
||||
debug.Set("typedCount", entry.typed_count);
|
||||
result.Set("debug", std::move(debug));
|
||||
auto debug_mojom = history::mojom::DebugInfo::New();
|
||||
debug_mojom->is_url_in_local_database = IsUrlInLocalDatabase(entry);
|
||||
debug_mojom->visit_count = entry.visit_count;
|
||||
debug_mojom->typed_count = entry.typed_count;
|
||||
result_mojom->debug_info = std::move(debug_mojom);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result_mojom;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BrowsingHistoryHandler::BrowsingHistoryHandler()
|
||||
: clock_(base::DefaultClock::GetInstance()),
|
||||
BrowsingHistoryHandler::BrowsingHistoryHandler(
|
||||
mojo::PendingReceiver<history::mojom::PageHandler> pending_page_handler,
|
||||
Profile* profile,
|
||||
content::WebContents* web_contents)
|
||||
: profile_(profile),
|
||||
web_contents_(web_contents),
|
||||
page_handler_(this, std::move(pending_page_handler)),
|
||||
clock_(base::DefaultClock::GetInstance()),
|
||||
browsing_history_service_(nullptr) {}
|
||||
|
||||
BrowsingHistoryHandler::~BrowsingHistoryHandler() = default;
|
||||
|
||||
void BrowsingHistoryHandler::OnJavascriptAllowed() {
|
||||
if (!browsing_history_service_ && !initial_results_) {
|
||||
// Page was refreshed, so need to call StartQueryHistory here
|
||||
StartQueryHistory();
|
||||
}
|
||||
|
||||
for (auto& callback : deferred_callbacks_) {
|
||||
std::move(callback).Run();
|
||||
void BrowsingHistoryHandler::SetPage(
|
||||
mojo::PendingRemote<history::mojom::Page> pending_page) {
|
||||
page_.Bind(std::move(pending_page));
|
||||
// TODO(mfacey@): Explore whether deferred_callbacks_ can be removed.
|
||||
for (auto& deferred_callback : deferred_callbacks_) {
|
||||
std::move(deferred_callback).Run();
|
||||
}
|
||||
deferred_callbacks_.clear();
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::OnJavascriptDisallowed() {
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
browsing_history_service_ = nullptr;
|
||||
initial_results_ = std::nullopt;
|
||||
deferred_callbacks_.clear();
|
||||
query_history_callback_id_.clear();
|
||||
while (!remove_visits_callbacks_.empty()) {
|
||||
remove_visits_callbacks_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::RegisterMessages() {
|
||||
// Create our favicon data source.
|
||||
Profile* profile = GetProfile();
|
||||
content::URLDataSource::Add(
|
||||
profile, std::make_unique<FaviconSource>(
|
||||
profile, chrome::FaviconUrlFormat::kFavicon2));
|
||||
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"queryHistory",
|
||||
base::BindRepeating(&BrowsingHistoryHandler::HandleQueryHistory,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"queryHistoryContinuation",
|
||||
base::BindRepeating(
|
||||
&BrowsingHistoryHandler::HandleQueryHistoryContinuation,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"removeVisits",
|
||||
base::BindRepeating(&BrowsingHistoryHandler::HandleRemoveVisits,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"clearBrowsingData",
|
||||
base::BindRepeating(&BrowsingHistoryHandler::HandleClearBrowsingData,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"removeBookmark",
|
||||
base::BindRepeating(&BrowsingHistoryHandler::HandleRemoveBookmark,
|
||||
base::Unretained(this)));
|
||||
web_ui()->RegisterMessageCallback(
|
||||
"setLastSelectedTab",
|
||||
base::BindRepeating(&BrowsingHistoryHandler::HandleSetLastSelectedTab,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::StartQueryHistory() {
|
||||
Profile* profile = GetProfile();
|
||||
HistoryService* local_history = HistoryServiceFactory::GetForProfile(
|
||||
profile, ServiceAccessType::EXPLICIT_ACCESS);
|
||||
profile_, ServiceAccessType::EXPLICIT_ACCESS);
|
||||
syncer::SyncService* sync_service =
|
||||
SyncServiceFactory::GetForProfile(profile);
|
||||
SyncServiceFactory::GetForProfile(profile_);
|
||||
browsing_history_service_ = std::make_unique<BrowsingHistoryService>(
|
||||
this, local_history, sync_service);
|
||||
|
||||
// 150 = RESULTS_PER_PAGE from chrome/browser/resources/history/constants.js
|
||||
SendHistoryQuery(150, std::u16string(), std::nullopt);
|
||||
SendHistoryQuery(150, std::string(), std::nullopt);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HandleQueryHistory(const base::Value::List& args) {
|
||||
AllowJavascript();
|
||||
const base::Value& callback_id = args[0];
|
||||
if (initial_results_.has_value()) {
|
||||
ResolveJavascriptCallback(callback_id, *initial_results_);
|
||||
initial_results_ = std::nullopt;
|
||||
return;
|
||||
void BrowsingHistoryHandler::QueryHistory(const std::string& query,
|
||||
int max_count,
|
||||
std::optional<double> begin_timestamp,
|
||||
QueryHistoryCallback callback) {
|
||||
if (!browsing_history_service_) {
|
||||
// Page was refreshed, so need to call StartQueryHistory here
|
||||
StartQueryHistory();
|
||||
}
|
||||
|
||||
// Reset the query history continuation callback. Since it is repopulated in
|
||||
@ -391,42 +370,26 @@ void BrowsingHistoryHandler::HandleQueryHistory(const base::Value::List& args) {
|
||||
query_history_continuation_.Reset();
|
||||
|
||||
// Cancel the previous query if it is still in flight.
|
||||
if (!query_history_callback_id_.empty()) {
|
||||
RejectJavascriptCallback(base::Value(query_history_callback_id_),
|
||||
base::Value());
|
||||
}
|
||||
query_history_callback_id_ = callback_id.GetString();
|
||||
|
||||
// Parse the arguments from JavaScript. There are two required arguments:
|
||||
// - the text to search for (may be empty)
|
||||
// - the maximum number of results to return (may be 0, meaning that there
|
||||
// is no maximum).
|
||||
const base::Value& search_text = args[1];
|
||||
|
||||
const base::Value& count = args[2];
|
||||
if (!count.is_int()) {
|
||||
NOTREACHED() << "Failed to convert argument 2.";
|
||||
if (query_history_callback_) {
|
||||
std::move(query_history_callback_).Run({});
|
||||
}
|
||||
|
||||
std::optional<double> begin_timestamp;
|
||||
if (args.size() == 4) {
|
||||
begin_timestamp = args[3].GetIfDouble();
|
||||
}
|
||||
SendHistoryQuery(count.GetInt(), base::UTF8ToUTF16(search_text.GetString()),
|
||||
begin_timestamp);
|
||||
query_history_callback_ = std::move(callback);
|
||||
|
||||
SendHistoryQuery(max_count, query, begin_timestamp);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::SendHistoryQuery(
|
||||
int max_count,
|
||||
const std::u16string& query,
|
||||
const std::string& query,
|
||||
std::optional<double> begin_timestamp) {
|
||||
history::QueryOptions options;
|
||||
options.max_count = max_count;
|
||||
options.duplicate_policy = history::QueryOptions::REMOVE_DUPLICATES_PER_DAY;
|
||||
std::u16string query_without_prefix = query;
|
||||
std::string query_without_prefix = query;
|
||||
|
||||
const std::u16string kHostPrefix = u"host:";
|
||||
if (base::StartsWith(query, kHostPrefix)) {
|
||||
const std::string kHostPrefix = "host:";
|
||||
if (query.rfind(kHostPrefix, 0) == 0) {
|
||||
options.host_only = true;
|
||||
query_without_prefix = query.substr(kHostPrefix.length());
|
||||
}
|
||||
@ -436,57 +399,43 @@ void BrowsingHistoryHandler::SendHistoryQuery(
|
||||
base::Time::FromMillisecondsSinceUnixEpoch(begin_timestamp.value());
|
||||
}
|
||||
|
||||
browsing_history_service_->QueryHistory(query_without_prefix, options);
|
||||
browsing_history_service_->QueryHistory(
|
||||
base::UTF8ToUTF16(query_without_prefix), options);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HandleQueryHistoryContinuation(
|
||||
const base::Value::List& args) {
|
||||
CHECK_EQ(args.size(), 1U);
|
||||
const base::Value& callback_id = args[0];
|
||||
void BrowsingHistoryHandler::QueryHistoryContinuation(
|
||||
QueryHistoryContinuationCallback callback) {
|
||||
// Cancel the previous query if it is still in flight.
|
||||
if (!query_history_callback_id_.empty()) {
|
||||
RejectJavascriptCallback(base::Value(query_history_callback_id_),
|
||||
base::Value());
|
||||
if (query_history_callback_) {
|
||||
std::move(query_history_callback_).Run({});
|
||||
}
|
||||
query_history_callback_id_ = callback_id.GetString();
|
||||
query_history_callback_ = std::move(callback);
|
||||
|
||||
DCHECK(query_history_continuation_);
|
||||
std::move(query_history_continuation_).Run();
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HandleRemoveVisits(const base::Value::List& args) {
|
||||
CHECK_EQ(args.size(), 2U);
|
||||
const base::Value& callback_id = args[0];
|
||||
remove_visits_callbacks_.push(callback_id.GetString());
|
||||
void BrowsingHistoryHandler::RemoveVisits(
|
||||
const std::vector<history::mojom::RemovalItemPtr> items,
|
||||
RemoveVisitsCallback callback) {
|
||||
remove_visits_callbacks_.push(std::move(callback));
|
||||
|
||||
std::vector<BrowsingHistoryService::HistoryEntry> items_to_remove;
|
||||
const base::Value& items = args[1];
|
||||
const base::Value::List& list = items.GetList();
|
||||
items_to_remove.reserve(list.size());
|
||||
for (const auto& i : list) {
|
||||
// Each argument is a dictionary with properties "url" and "timestamps".
|
||||
if (!i.is_dict()) {
|
||||
items_to_remove.reserve(items.size());
|
||||
for (const auto& item : items) {
|
||||
const std::string url = item->url;
|
||||
const std::vector<double> timestamps = item->timestamps;
|
||||
if (url.empty()) {
|
||||
NOTREACHED() << "Unable to extract arguments";
|
||||
}
|
||||
|
||||
const std::string* url_ptr = i.GetDict().FindString("url");
|
||||
const base::Value::List* timestamps_ptr =
|
||||
i.GetDict().FindList("timestamps");
|
||||
if (!url_ptr || !timestamps_ptr) {
|
||||
NOTREACHED() << "Unable to extract arguments";
|
||||
}
|
||||
|
||||
DCHECK_GT(timestamps_ptr->size(), 0U);
|
||||
DCHECK_GT(timestamps.size(), 0U);
|
||||
BrowsingHistoryService::HistoryEntry entry;
|
||||
entry.url = GURL(*url_ptr);
|
||||
|
||||
for (const base::Value& timestamp : *timestamps_ptr) {
|
||||
if (!timestamp.is_double() && !timestamp.is_int()) {
|
||||
NOTREACHED() << "Unable to extract visit timestamp.";
|
||||
}
|
||||
entry.url = GURL(url);
|
||||
|
||||
for (const auto& timestamp : timestamps) {
|
||||
base::Time visit_time =
|
||||
base::Time::FromMillisecondsSinceUnixEpoch(timestamp.GetDouble());
|
||||
base::Time::FromMillisecondsSinceUnixEpoch(timestamp);
|
||||
entry.all_timestamps.insert(visit_time.ToInternalValue());
|
||||
}
|
||||
|
||||
@ -496,29 +445,21 @@ void BrowsingHistoryHandler::HandleRemoveVisits(const base::Value::List& args) {
|
||||
browsing_history_service_->RemoveVisits(items_to_remove);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HandleClearBrowsingData(
|
||||
const base::Value::List& args) {
|
||||
void BrowsingHistoryHandler::OpenClearBrowsingDataDialog() {
|
||||
// TODO(beng): This is an improper direct dependency on Browser. Route this
|
||||
// through some sort of delegate.
|
||||
Browser* browser = chrome::FindBrowserWithTab(web_ui()->GetWebContents());
|
||||
Browser* browser = chrome::FindBrowserWithTab(web_contents_);
|
||||
chrome::ShowClearBrowsingDataDialog(browser);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HandleRemoveBookmark(
|
||||
const base::Value::List& args) {
|
||||
CHECK_EQ(1U, args.size());
|
||||
std::string url = args[0].GetString();
|
||||
Profile* profile = GetProfile();
|
||||
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile);
|
||||
void BrowsingHistoryHandler::RemoveBookmark(const std::string& url) {
|
||||
BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile_);
|
||||
bookmarks::RemoveAllBookmarks(model, GURL(url), FROM_HERE);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HandleSetLastSelectedTab(
|
||||
const base::Value::List& args) {
|
||||
const base::Value& last_tab = args[0];
|
||||
Profile* profile = GetProfile();
|
||||
profile->GetPrefs()->SetInteger(history_clusters::prefs::kLastSelectedTab,
|
||||
last_tab.GetInt());
|
||||
//
|
||||
void BrowsingHistoryHandler::SetLastSelectedTab(const int last_tab) {
|
||||
profile_->GetPrefs()->SetInteger(history_clusters::prefs::kLastSelectedTab,
|
||||
last_tab);
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::OnQueryComplete(
|
||||
@ -526,63 +467,50 @@ void BrowsingHistoryHandler::OnQueryComplete(
|
||||
const BrowsingHistoryService::QueryResultsInfo& query_results_info,
|
||||
base::OnceClosure continuation_closure) {
|
||||
query_history_continuation_ = std::move(continuation_closure);
|
||||
Profile* profile = Profile::FromWebUI(web_ui());
|
||||
CHECK(profile);
|
||||
CHECK(profile_);
|
||||
BookmarkModel* bookmark_model =
|
||||
BookmarkModelFactory::GetForBrowserContext(profile);
|
||||
BookmarkModelFactory::GetForBrowserContext(profile_);
|
||||
|
||||
const syncer::DeviceInfoTracker* tracker =
|
||||
DeviceInfoSyncServiceFactory::GetForProfile(profile)
|
||||
DeviceInfoSyncServiceFactory::GetForProfile(profile_)
|
||||
->GetDeviceInfoTracker();
|
||||
|
||||
// Convert the result vector into a base::Value::List
|
||||
DCHECK(tracker);
|
||||
base::Value::List results_value;
|
||||
std::vector<history::mojom::HistoryEntryPtr> results_mojom;
|
||||
for (const BrowsingHistoryService::HistoryEntry& entry : results) {
|
||||
results_value.Append(
|
||||
HistoryEntryToValue(entry, bookmark_model, *profile, tracker, clock_));
|
||||
results_mojom.push_back(
|
||||
HistoryEntryToMojom(entry, bookmark_model, *profile_, tracker, clock_));
|
||||
}
|
||||
|
||||
base::Value::Dict results_info;
|
||||
// The items which are to be written into results_info_value_ are also
|
||||
// described in chrome/browser/resources/history/history.js in @typedef for
|
||||
// HistoryQuery. Please update it whenever you add or remove any keys in
|
||||
// results_info_value_.
|
||||
results_info.Set("term", query_results_info.search_text);
|
||||
results_info.Set("finished", query_results_info.reached_beginning);
|
||||
auto results_info = history::mojom::HistoryQuery::New();
|
||||
// The items which are to be written into results_info_ are also
|
||||
// described in ui/webui/resources/cr_components/history/history.mojom.
|
||||
results_info->term = base::UTF16ToUTF8(query_results_info.search_text);
|
||||
results_info->finished = query_results_info.reached_beginning;
|
||||
|
||||
base::Value::Dict final_results;
|
||||
final_results.Set("info", std::move(results_info));
|
||||
final_results.Set("value", std::move(results_value));
|
||||
auto final_results = history::mojom::QueryResult::New();
|
||||
final_results->info = std::move(results_info);
|
||||
final_results->value = std::move(results_mojom);
|
||||
|
||||
if (query_history_callback_id_.empty()) {
|
||||
// This can happen if JS isn't ready yet when the first query comes back.
|
||||
initial_results_ = std::move(final_results);
|
||||
return;
|
||||
}
|
||||
|
||||
ResolveJavascriptCallback(base::Value(query_history_callback_id_),
|
||||
final_results);
|
||||
query_history_callback_id_.clear();
|
||||
std::move(query_history_callback_).Run(std::move(final_results));
|
||||
return;
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::OnRemoveVisitsComplete() {
|
||||
CHECK(!remove_visits_callbacks_.empty());
|
||||
ResolveJavascriptCallback(base::Value(remove_visits_callbacks_.front()),
|
||||
base::Value());
|
||||
std::move(remove_visits_callbacks_.front()).Run();
|
||||
remove_visits_callbacks_.pop();
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::OnRemoveVisitsFailed() {
|
||||
CHECK(!remove_visits_callbacks_.empty());
|
||||
RejectJavascriptCallback(base::Value(remove_visits_callbacks_.front()),
|
||||
base::Value());
|
||||
std::move(remove_visits_callbacks_.front()).Run();
|
||||
remove_visits_callbacks_.pop();
|
||||
}
|
||||
|
||||
void BrowsingHistoryHandler::HistoryDeleted() {
|
||||
if (IsJavascriptAllowed()) {
|
||||
FireWebUIListener("history-deleted", base::Value());
|
||||
if (page_) {
|
||||
page_->OnHistoryDeleted();
|
||||
} else {
|
||||
deferred_callbacks_.push_back(base::BindOnce(
|
||||
&BrowsingHistoryHandler::HistoryDeleted, weak_factory_.GetWeakPtr()));
|
||||
@ -592,8 +520,8 @@ void BrowsingHistoryHandler::HistoryDeleted() {
|
||||
void BrowsingHistoryHandler::HasOtherFormsOfBrowsingHistory(
|
||||
bool has_other_forms,
|
||||
bool has_synced_results) {
|
||||
if (IsJavascriptAllowed()) {
|
||||
FireWebUIListener("has-other-forms-changed", base::Value(has_other_forms));
|
||||
if (page_) {
|
||||
page_->OnHasOtherFormsChanged(has_other_forms);
|
||||
} else {
|
||||
deferred_callbacks_.push_back(base::BindOnce(
|
||||
&BrowsingHistoryHandler::HasOtherFormsOfBrowsingHistory,
|
||||
@ -602,5 +530,5 @@ void BrowsingHistoryHandler::HasOtherFormsOfBrowsingHistory(
|
||||
}
|
||||
|
||||
Profile* BrowsingHistoryHandler::GetProfile() {
|
||||
return Profile::FromWebUI(web_ui());
|
||||
return profile_;
|
||||
}
|
||||
|
@ -20,43 +20,50 @@
|
||||
#include "base/time/clock.h"
|
||||
#include "base/values.h"
|
||||
#include "chrome/browser/history/profile_based_browsing_history_driver.h"
|
||||
#include "content/public/browser/web_ui_message_handler.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "ui/webui/resources/cr_components/history/history.mojom.h"
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
} // namespace content
|
||||
|
||||
// The handler for Javascript messages related to the "history" view.
|
||||
class BrowsingHistoryHandler : public content::WebUIMessageHandler,
|
||||
class BrowsingHistoryHandler : public history::mojom::PageHandler,
|
||||
public ProfileBasedBrowsingHistoryDriver {
|
||||
public:
|
||||
BrowsingHistoryHandler();
|
||||
BrowsingHistoryHandler(
|
||||
mojo::PendingReceiver<history::mojom::PageHandler> pending_page_handler,
|
||||
Profile* profile,
|
||||
content::WebContents* web_contents);
|
||||
|
||||
BrowsingHistoryHandler(const BrowsingHistoryHandler&) = delete;
|
||||
BrowsingHistoryHandler& operator=(const BrowsingHistoryHandler&) = delete;
|
||||
|
||||
~BrowsingHistoryHandler() override;
|
||||
|
||||
// WebUIMessageHandler implementation.
|
||||
void OnJavascriptAllowed() override;
|
||||
void OnJavascriptDisallowed() override;
|
||||
void RegisterMessages() override;
|
||||
|
||||
void StartQueryHistory();
|
||||
|
||||
// Handler for the "queryHistory" message.
|
||||
void HandleQueryHistory(const base::Value::List& args);
|
||||
void SetPage(mojo::PendingRemote<history::mojom::Page> pending_page) override;
|
||||
|
||||
// Handler for the "queryHistoryContinuation" message.
|
||||
void HandleQueryHistoryContinuation(const base::Value::List& args);
|
||||
void QueryHistory(const std::string& query,
|
||||
int max_count,
|
||||
std::optional<double> begin_timestamp,
|
||||
QueryHistoryCallback callback) override;
|
||||
|
||||
// Handler for the "removeVisits" message.
|
||||
void HandleRemoveVisits(const base::Value::List& args);
|
||||
void QueryHistoryContinuation(
|
||||
QueryHistoryContinuationCallback callback) override;
|
||||
|
||||
// Handler for "clearBrowsingData" message.
|
||||
void HandleClearBrowsingData(const base::Value::List& args);
|
||||
void RemoveVisits(const std::vector<history::mojom::RemovalItemPtr> items,
|
||||
RemoveVisitsCallback callback) override;
|
||||
|
||||
// Handler for "removeBookmark" message.
|
||||
void HandleRemoveBookmark(const base::Value::List& args);
|
||||
void OpenClearBrowsingDataDialog() override;
|
||||
|
||||
// Handler for "setLastSelectedTab" message.
|
||||
void HandleSetLastSelectedTab(const base::Value::List& args);
|
||||
void RemoveBookmark(const std::string& url) override;
|
||||
|
||||
void SetLastSelectedTab(const int last_tab) override;
|
||||
|
||||
// BrowsingHistoryDriver implementation.
|
||||
void OnQueryComplete(
|
||||
@ -82,9 +89,13 @@ class BrowsingHistoryHandler : public content::WebUIMessageHandler,
|
||||
browsing_history_service_ = std::move(service);
|
||||
}
|
||||
|
||||
history::BrowsingHistoryService* get_browsing_history_service_for_testing() {
|
||||
return browsing_history_service_.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SendHistoryQuery(int count,
|
||||
const std::u16string& query,
|
||||
const std::string& query,
|
||||
std::optional<double> begin_timestamp);
|
||||
|
||||
private:
|
||||
@ -92,6 +103,12 @@ class BrowsingHistoryHandler : public content::WebUIMessageHandler,
|
||||
ObservingWebHistoryDeletions);
|
||||
FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest, MdTruncatesTitles);
|
||||
|
||||
raw_ptr<Profile> profile_;
|
||||
raw_ptr<content::WebContents> web_contents_;
|
||||
|
||||
mojo::Remote<history::mojom::Page> page_;
|
||||
mojo::Receiver<history::mojom::PageHandler> page_handler_;
|
||||
|
||||
// The clock used to vend times.
|
||||
raw_ptr<base::Clock> clock_;
|
||||
|
||||
@ -99,13 +116,11 @@ class BrowsingHistoryHandler : public content::WebUIMessageHandler,
|
||||
|
||||
std::vector<base::OnceClosure> deferred_callbacks_;
|
||||
|
||||
std::optional<base::Value::Dict> initial_results_;
|
||||
|
||||
std::string query_history_callback_id_;
|
||||
QueryHistoryCallback query_history_callback_;
|
||||
|
||||
base::OnceClosure query_history_continuation_;
|
||||
|
||||
std::queue<std::string> remove_visits_callbacks_;
|
||||
std::queue<RemoveVisitsCallback> remove_visits_callbacks_;
|
||||
|
||||
base::WeakPtrFactory<BrowsingHistoryHandler> weak_factory_{this};
|
||||
};
|
||||
|
@ -16,26 +16,28 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/gmock_callback_support.h"
|
||||
#include "base/test/mock_callback.h"
|
||||
#include "base/test/simple_test_clock.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
|
||||
#include "chrome/browser/history/web_history_service_factory.h"
|
||||
#include "chrome/browser/sync/sync_service_factory.h"
|
||||
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
|
||||
#include "chrome/test/base/testing_profile.h"
|
||||
#include "components/history/core/browser/browsing_history_service.h"
|
||||
#include "components/history/core/test/fake_web_history_service.h"
|
||||
#include "components/sync/base/data_type.h"
|
||||
#include "components/sync/test/test_sync_service.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/test/test_web_ui.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "ui/webui/resources/cr_components/history/history.mojom.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
using testing::_;
|
||||
|
||||
namespace history {
|
||||
|
||||
class MockBrowsingHistoryService : public BrowsingHistoryService {
|
||||
@ -46,8 +48,6 @@ class MockBrowsingHistoryService : public BrowsingHistoryService {
|
||||
(override));
|
||||
};
|
||||
|
||||
} // namespace history
|
||||
|
||||
namespace {
|
||||
|
||||
base::Time PretendNow() {
|
||||
@ -67,13 +67,17 @@ base::Time PretendNow() {
|
||||
class BrowsingHistoryHandlerWithWebUIForTesting
|
||||
: public BrowsingHistoryHandler {
|
||||
public:
|
||||
explicit BrowsingHistoryHandlerWithWebUIForTesting(content::WebUI* web_ui) {
|
||||
explicit BrowsingHistoryHandlerWithWebUIForTesting(
|
||||
mojo::PendingReceiver<mojom::PageHandler> pending_page_handler,
|
||||
Profile* profile,
|
||||
content::WebContents* web_contents)
|
||||
: BrowsingHistoryHandler(std::move(pending_page_handler),
|
||||
profile,
|
||||
web_contents) {
|
||||
set_clock(&test_clock_);
|
||||
set_web_ui(web_ui);
|
||||
test_clock_.SetNow(PretendNow());
|
||||
auto service = std::make_unique<
|
||||
testing::StrictMock<history::MockBrowsingHistoryService>>();
|
||||
mock_service_ = service.get();
|
||||
set_browsing_history_service_for_testing(std::move(service));
|
||||
}
|
||||
|
||||
@ -82,24 +86,14 @@ class BrowsingHistoryHandlerWithWebUIForTesting
|
||||
BrowsingHistoryHandlerWithWebUIForTesting& operator=(
|
||||
const BrowsingHistoryHandlerWithWebUIForTesting&) = delete;
|
||||
|
||||
void SendHistoryQuery(int count,
|
||||
const std::u16string& query,
|
||||
std::optional<double> begin_timestamp) override {
|
||||
if (postpone_query_results_) {
|
||||
return;
|
||||
}
|
||||
BrowsingHistoryHandler::SendHistoryQuery(count, query, begin_timestamp);
|
||||
}
|
||||
|
||||
void PostponeResults() { postpone_query_results_ = true; }
|
||||
|
||||
base::SimpleTestClock* test_clock() { return &test_clock_; }
|
||||
history::MockBrowsingHistoryService* mock_service() { return mock_service_; }
|
||||
history::MockBrowsingHistoryService* mock_service() {
|
||||
return static_cast<history::MockBrowsingHistoryService*>(
|
||||
get_browsing_history_service_for_testing());
|
||||
}
|
||||
|
||||
private:
|
||||
base::SimpleTestClock test_clock_;
|
||||
bool postpone_query_results_ = false;
|
||||
raw_ptr<history::MockBrowsingHistoryService, DanglingUntriaged> mock_service_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -109,296 +103,155 @@ class BrowsingHistoryHandlerTest : public ChromeRenderViewHostTestHarness {
|
||||
void SetUp() override {
|
||||
ChromeRenderViewHostTestHarness::SetUp();
|
||||
|
||||
sync_service_ = static_cast<syncer::TestSyncService*>(
|
||||
SyncServiceFactory::GetForProfile(profile()));
|
||||
web_history_service_ = static_cast<history::FakeWebHistoryService*>(
|
||||
WebHistoryServiceFactory::GetForProfile(profile()));
|
||||
ASSERT_TRUE(web_history_service_);
|
||||
|
||||
web_ui_ = std::make_unique<content::TestWebUI>();
|
||||
web_ui_->set_web_contents(web_contents());
|
||||
|
||||
handler_ = std::make_unique<BrowsingHistoryHandlerWithWebUIForTesting>(
|
||||
mojo::PendingReceiver<history::mojom::PageHandler>(), profile(),
|
||||
web_contents());
|
||||
}
|
||||
|
||||
void MockHistoryServiceCall(
|
||||
const std::u16string& search_text,
|
||||
const QueryOptions& options,
|
||||
std::vector<BrowsingHistoryService::HistoryEntry> mock_results = {}) {
|
||||
EXPECT_CALL(
|
||||
*handler_->mock_service(),
|
||||
QueryHistory(search_text,
|
||||
::testing::FieldsAre(
|
||||
/*begin_time*/ options.begin_time,
|
||||
/*end_time*/ options.end_time,
|
||||
/*max_count*/ 150,
|
||||
/*duplicate_policy*/
|
||||
history::QueryOptions::REMOVE_DUPLICATES_PER_DAY,
|
||||
/*matching_algorithm*/ options.matching_algorithm,
|
||||
/*host_only*/ options.host_only,
|
||||
/*visit_order*/ options.visit_order,
|
||||
/*app_id*/ options.app_id)))
|
||||
.Times(1)
|
||||
.WillOnce(testing::Invoke([&, mock_results](
|
||||
const std::u16string& search_text,
|
||||
const QueryOptions& options) {
|
||||
std::vector<BrowsingHistoryService::HistoryEntry> results;
|
||||
if (mock_results.empty()) {
|
||||
BrowsingHistoryService::HistoryEntry entry(
|
||||
BrowsingHistoryService::HistoryEntry::LOCAL_ENTRY,
|
||||
GURL(("http://test.com")), u"Test",
|
||||
base::Time::Now() - base::Minutes(5), std::string(), false,
|
||||
std::u16string(), false, GURL(), 0, 0, history::kNoAppIdFilter);
|
||||
results.push_back(entry);
|
||||
}
|
||||
|
||||
BrowsingHistoryService::QueryResultsInfo info;
|
||||
info.search_text = search_text;
|
||||
info.reached_beginning = true;
|
||||
info.sync_timed_out = false;
|
||||
info.has_synced_results = true;
|
||||
handler_->OnQueryComplete(
|
||||
mock_results.empty() ? results : mock_results, info,
|
||||
base::OnceClosure());
|
||||
}));
|
||||
}
|
||||
|
||||
mojom::QueryResultPtr RunQueryHistory(
|
||||
const std::string& query,
|
||||
std::optional<double> begin_timestamp = std::nullopt) {
|
||||
mojom::QueryResultPtr history_query_results;
|
||||
base::RunLoop run_loop;
|
||||
handler_->QueryHistory(
|
||||
query, 150, begin_timestamp,
|
||||
base::BindLambdaForTesting([&](history::mojom::QueryResultPtr result) {
|
||||
history_query_results = std::move(result);
|
||||
run_loop.Quit();
|
||||
}));
|
||||
run_loop.Run();
|
||||
return history_query_results;
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
handler_.reset();
|
||||
web_ui_.reset();
|
||||
ChromeRenderViewHostTestHarness::TearDown();
|
||||
}
|
||||
|
||||
TestingProfile::TestingFactories GetTestingFactories() const override {
|
||||
return {
|
||||
TestingProfile::TestingFactory{
|
||||
SyncServiceFactory::GetInstance(),
|
||||
base::BindRepeating(&BuildTestSyncService)},
|
||||
TestingProfile::TestingFactory{
|
||||
WebHistoryServiceFactory::GetInstance(),
|
||||
base::BindRepeating(&BuildFakeWebHistoryService)},
|
||||
TestingProfile::TestingFactory{
|
||||
BookmarkModelFactory::GetInstance(),
|
||||
BookmarkModelFactory::GetDefaultFactory()},
|
||||
};
|
||||
}
|
||||
|
||||
void VerifyHistoryDeletedFired(content::TestWebUI::CallData& data) {
|
||||
EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
|
||||
ASSERT_TRUE(data.arg1()->is_string());
|
||||
EXPECT_EQ("history-deleted", data.arg1()->GetString());
|
||||
}
|
||||
|
||||
void InitializeWebUI(BrowsingHistoryHandlerWithWebUIForTesting& handler) {
|
||||
// Send historyLoaded so that JS will be allowed.
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append("");
|
||||
init_args.Append(150);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
}
|
||||
|
||||
syncer::TestSyncService* sync_service() { return sync_service_; }
|
||||
history::WebHistoryService* web_history_service() {
|
||||
return web_history_service_;
|
||||
}
|
||||
content::TestWebUI* web_ui() { return web_ui_.get(); }
|
||||
BrowsingHistoryHandlerWithWebUIForTesting* handler() {
|
||||
return handler_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unique_ptr<KeyedService> BuildTestSyncService(
|
||||
content::BrowserContext* context) {
|
||||
return std::make_unique<syncer::TestSyncService>();
|
||||
}
|
||||
|
||||
static std::unique_ptr<KeyedService> BuildFakeWebHistoryService(
|
||||
content::BrowserContext* context) {
|
||||
std::unique_ptr<history::FakeWebHistoryService> service =
|
||||
std::make_unique<history::FakeWebHistoryService>();
|
||||
service->SetupFakeResponse(true /* success */, net::HTTP_OK);
|
||||
return service;
|
||||
}
|
||||
|
||||
raw_ptr<syncer::TestSyncService, DanglingUntriaged> sync_service_ = nullptr;
|
||||
raw_ptr<history::FakeWebHistoryService, DanglingUntriaged>
|
||||
web_history_service_ = nullptr;
|
||||
std::unique_ptr<content::TestWebUI> web_ui_;
|
||||
std::unique_ptr<BrowsingHistoryHandlerWithWebUIForTesting> handler_;
|
||||
};
|
||||
|
||||
// Tests that BrowsingHistoryHandler is informed about WebHistoryService
|
||||
// deletions.
|
||||
TEST_F(BrowsingHistoryHandlerTest, ObservingWebHistoryDeletions) {
|
||||
base::RepeatingCallback<void(bool)> callback = base::DoNothing();
|
||||
|
||||
// BrowsingHistoryHandler is informed about WebHistoryService history
|
||||
// deletions.
|
||||
{
|
||||
ASSERT_EQ(sync_service()->GetTransportState(),
|
||||
syncer::SyncService::TransportState::ACTIVE);
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
handler.RegisterMessages();
|
||||
handler.StartQueryHistory();
|
||||
InitializeWebUI(handler);
|
||||
|
||||
// QueryHistory triggers 2 calls to HasOtherFormsOfBrowsingHistory that fire
|
||||
// before the callback is resolved if the sync service is active when the
|
||||
// first query is sent. The handler should also resolve the initial
|
||||
// queryHistory callback.
|
||||
EXPECT_EQ(3U, web_ui()->call_data().size());
|
||||
|
||||
web_history_service()->ExpireHistoryBetween(
|
||||
std::set<GURL>(), base::Time(), base::Time::Max(), callback,
|
||||
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
|
||||
|
||||
EXPECT_EQ(4U, web_ui()->call_data().size());
|
||||
VerifyHistoryDeletedFired(*web_ui()->call_data().back());
|
||||
}
|
||||
|
||||
// BrowsingHistoryHandler will be informed about WebHistoryService deletions
|
||||
// even if history sync is activated later.
|
||||
{
|
||||
sync_service()->SetMaxTransportState(
|
||||
syncer::SyncService::TransportState::INITIALIZING);
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
handler.RegisterMessages();
|
||||
handler.StartQueryHistory();
|
||||
sync_service()->SetMaxTransportState(
|
||||
syncer::SyncService::TransportState::ACTIVE);
|
||||
sync_service()->FireStateChanged();
|
||||
|
||||
web_history_service()->ExpireHistoryBetween(
|
||||
std::set<GURL>(), base::Time(), base::Time::Max(), callback,
|
||||
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
|
||||
EXPECT_EQ(4U, web_ui()->call_data().size());
|
||||
|
||||
// Simulate initialization after history has been deleted. The
|
||||
// history-deleted event will happen before the historyResults() callback,
|
||||
// since AllowJavascript is called before returning the results.
|
||||
InitializeWebUI(handler);
|
||||
|
||||
// QueryHistory triggers 1 call to HasOtherFormsOfBrowsingHistory that fire
|
||||
// before the callback is resolved if the sync service is inactive when the
|
||||
// first query is sent. The handler should also have fired history-deleted
|
||||
// and resolved the initial queryHistory callback.
|
||||
EXPECT_EQ(7U, web_ui()->call_data().size());
|
||||
VerifyHistoryDeletedFired(
|
||||
*web_ui()->call_data()[web_ui()->call_data().size() - 2]);
|
||||
}
|
||||
|
||||
// BrowsingHistoryHandler does not fire historyDeleted while a web history
|
||||
// delete request is happening.
|
||||
{
|
||||
ASSERT_EQ(sync_service()->GetTransportState(),
|
||||
syncer::SyncService::TransportState::ACTIVE);
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
handler.RegisterMessages();
|
||||
handler.StartQueryHistory();
|
||||
InitializeWebUI(handler);
|
||||
// QueryHistory triggers 2 calls to HasOtherFormsOfBrowsingHistory that fire
|
||||
// before the callback is resolved if the sync service is active when the
|
||||
// first query is sent. The handler should also resolve the initial
|
||||
// queryHistory callback.
|
||||
EXPECT_EQ(10U, web_ui()->call_data().size());
|
||||
|
||||
// Simulate a delete request.
|
||||
base::Value::List args;
|
||||
args.Append("remove-visits-callback-id");
|
||||
base::Value::List to_remove;
|
||||
base::Value::Dict visit;
|
||||
visit.Set("url", "https://www.google.com");
|
||||
base::Value::List timestamps;
|
||||
timestamps.Append(12345678.0);
|
||||
visit.Set("timestamps", std::move(timestamps));
|
||||
to_remove.Append(std::move(visit));
|
||||
args.Append(std::move(to_remove));
|
||||
handler.HandleRemoveVisits(args);
|
||||
|
||||
EXPECT_EQ(11U, web_ui()->call_data().size());
|
||||
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
|
||||
EXPECT_EQ("cr.webUIResponse", data.function_name());
|
||||
ASSERT_TRUE(data.arg1()->is_string());
|
||||
EXPECT_EQ("remove-visits-callback-id", data.arg1()->GetString());
|
||||
ASSERT_TRUE(data.arg2()->is_bool());
|
||||
ASSERT_TRUE(data.arg2()->GetBool());
|
||||
}
|
||||
|
||||
// When history sync is not active, we don't listen to WebHistoryService
|
||||
// deletions. The WebHistoryService object still exists (because it's a
|
||||
// BrowserContextKeyedService), but is not visible to BrowsingHistoryHandler.
|
||||
{
|
||||
sync_service()->SetMaxTransportState(
|
||||
syncer::SyncService::TransportState::INITIALIZING);
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
handler.RegisterMessages();
|
||||
handler.StartQueryHistory();
|
||||
|
||||
web_history_service()->ExpireHistoryBetween(
|
||||
std::set<GURL>(), base::Time(), base::Time::Max(), callback,
|
||||
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
|
||||
|
||||
// No additional WebUI calls were made.
|
||||
EXPECT_EQ(11U, web_ui()->call_data().size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BrowsingHistoryHandlerTest, HostPrefixParameter) {
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
|
||||
std::u16string query = u"www.chromium.org";
|
||||
EXPECT_CALL(
|
||||
*handler.mock_service(),
|
||||
QueryHistory(query,
|
||||
::testing::Field(&history::QueryOptions::host_only, true)));
|
||||
QueryOptions options;
|
||||
options.host_only = true;
|
||||
MockHistoryServiceCall(query, options);
|
||||
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append("host:www.chromium.org");
|
||||
init_args.Append(150);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
RunQueryHistory("host:www.chromium.org");
|
||||
}
|
||||
|
||||
TEST_F(BrowsingHistoryHandlerTest, WithoutHostPrefixParameter) {
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
|
||||
std::u16string query = u"www.chromium.org";
|
||||
EXPECT_CALL(
|
||||
*handler.mock_service(),
|
||||
QueryHistory(query,
|
||||
::testing::Field(&history::QueryOptions::host_only, false)));
|
||||
QueryOptions options;
|
||||
options.host_only = false;
|
||||
MockHistoryServiceCall(query, options);
|
||||
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append("www.chromium.org");
|
||||
init_args.Append(150);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
RunQueryHistory("www.chromium.org");
|
||||
}
|
||||
|
||||
TEST_F(BrowsingHistoryHandlerTest, MisplacedHostPrefixParameter) {
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
{
|
||||
std::u16string query = u"whost:ww.chromium.org";
|
||||
EXPECT_CALL(
|
||||
*handler.mock_service(),
|
||||
QueryHistory(
|
||||
query, ::testing::Field(&history::QueryOptions::host_only, false)));
|
||||
QueryOptions options;
|
||||
options.host_only = false;
|
||||
MockHistoryServiceCall(query, options);
|
||||
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append("whost:ww.chromium.org");
|
||||
init_args.Append(150);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
RunQueryHistory("whost:ww.chromium.org");
|
||||
}
|
||||
|
||||
{
|
||||
std::u16string query = u"www.chromium.orghost:";
|
||||
EXPECT_CALL(
|
||||
*handler.mock_service(),
|
||||
QueryHistory(
|
||||
query, ::testing::Field(&history::QueryOptions::host_only, false)));
|
||||
QueryOptions options;
|
||||
options.host_only = false;
|
||||
MockHistoryServiceCall(query, options);
|
||||
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append("www.chromium.orghost:");
|
||||
init_args.Append(150);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
RunQueryHistory("www.chromium.orghost:");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BrowsingHistoryHandlerTest, BeginTimestamp) {
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
{
|
||||
std::u16string query = u"query";
|
||||
double timestamp = 1713546406359L;
|
||||
EXPECT_CALL(
|
||||
*handler.mock_service(),
|
||||
QueryHistory(
|
||||
query, ::testing::Field(
|
||||
&history::QueryOptions::begin_time,
|
||||
base::Time::FromMillisecondsSinceUnixEpoch(timestamp))));
|
||||
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append(query);
|
||||
init_args.Append(150);
|
||||
init_args.Append(timestamp);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
QueryOptions options;
|
||||
options.begin_time = base::Time::FromMillisecondsSinceUnixEpoch(timestamp);
|
||||
MockHistoryServiceCall(query, options);
|
||||
RunQueryHistory("query", timestamp);
|
||||
}
|
||||
|
||||
{
|
||||
std::u16string query = u"www.chromium.orghost:";
|
||||
EXPECT_CALL(
|
||||
*handler.mock_service(),
|
||||
QueryHistory(
|
||||
query, ::testing::Field(&history::QueryOptions::host_only, false)));
|
||||
|
||||
base::Value::List init_args;
|
||||
init_args.Append("query-history-callback-id");
|
||||
init_args.Append("www.chromium.orghost:");
|
||||
init_args.Append(150);
|
||||
handler.HandleQueryHistory(init_args);
|
||||
QueryOptions options;
|
||||
options.host_only = false;
|
||||
MockHistoryServiceCall(query, options);
|
||||
RunQueryHistory("www.chromium.orghost:");
|
||||
}
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(IS_ANDROID)
|
||||
TEST_F(BrowsingHistoryHandlerTest, MdTruncatesTitles) {
|
||||
std::vector<BrowsingHistoryService::HistoryEntry> results;
|
||||
history::BrowsingHistoryService::HistoryEntry long_url_entry;
|
||||
long_url_entry.url = GURL(
|
||||
"http://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
|
||||
@ -408,53 +261,15 @@ TEST_F(BrowsingHistoryHandlerTest, MdTruncatesTitles) {
|
||||
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
|
||||
"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
|
||||
"ngurlislong.com");
|
||||
results.push_back(long_url_entry);
|
||||
ASSERT_GT(long_url_entry.url.spec().size(), 300U);
|
||||
QueryOptions options;
|
||||
MockHistoryServiceCall(u"test", options, results);
|
||||
auto results_mojom = RunQueryHistory("test");
|
||||
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
|
||||
handler.OnQueryComplete({long_url_entry},
|
||||
history::BrowsingHistoryService::QueryResultsInfo(),
|
||||
base::OnceClosure());
|
||||
InitializeWebUI(handler);
|
||||
ASSERT_FALSE(web_ui()->call_data().empty());
|
||||
|
||||
// Request should be resolved successfully.
|
||||
ASSERT_TRUE(web_ui()->call_data().front()->arg2()->GetBool());
|
||||
const base::Value* arg3 = web_ui()->call_data().front()->arg3();
|
||||
ASSERT_TRUE(arg3->is_dict());
|
||||
const base::Value* list = arg3->GetDict().Find("value");
|
||||
ASSERT_TRUE(list->is_list());
|
||||
|
||||
const base::Value& first_entry = list->GetList()[0];
|
||||
ASSERT_TRUE(first_entry.is_dict());
|
||||
|
||||
const std::string* title = first_entry.GetDict().FindString("title");
|
||||
ASSERT_TRUE(title);
|
||||
|
||||
ASSERT_EQ(0u, title->find("http://loooo"));
|
||||
EXPECT_EQ(300u, title->size());
|
||||
}
|
||||
|
||||
TEST_F(BrowsingHistoryHandlerTest, Reload) {
|
||||
BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
|
||||
handler.RegisterMessages();
|
||||
handler.PostponeResults();
|
||||
handler.StartQueryHistory();
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
InitializeWebUI(handler);
|
||||
// Still empty, since no results are available yet.
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
|
||||
// Simulate page refresh and results being returned asynchronously.
|
||||
handler.OnJavascriptDisallowed();
|
||||
history::BrowsingHistoryService::HistoryEntry url_entry;
|
||||
url_entry.url = GURL("https://www.chromium.org");
|
||||
handler.OnQueryComplete({url_entry},
|
||||
history::BrowsingHistoryService::QueryResultsInfo(),
|
||||
base::OnceClosure());
|
||||
|
||||
// There should be no new Web UI calls, since JS is still disallowed.
|
||||
ASSERT_TRUE(web_ui()->call_data().empty());
|
||||
ASSERT_EQ(0u, results_mojom->value[0]->title.find("http://loooo"));
|
||||
EXPECT_EQ(300u, results_mojom->value[0]->title.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace history
|
||||
|
@ -289,11 +289,6 @@ HistoryUI::HistoryUI(content::WebUI* web_ui)
|
||||
base::Unretained(this)));
|
||||
|
||||
web_ui->AddMessageHandler(std::make_unique<webui::NavigationHandler>());
|
||||
auto browsing_history_handler = std::make_unique<BrowsingHistoryHandler>();
|
||||
BrowsingHistoryHandler* browsing_history_handler_ptr =
|
||||
browsing_history_handler.get();
|
||||
web_ui->AddMessageHandler(std::move(browsing_history_handler));
|
||||
browsing_history_handler_ptr->StartQueryHistory();
|
||||
web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
|
||||
|
||||
auto foreign_session_handler =
|
||||
@ -328,6 +323,13 @@ void HistoryUI::BindInterface(
|
||||
/*for_side_panel=*/false);
|
||||
}
|
||||
|
||||
void HistoryUI::BindInterface(
|
||||
mojo::PendingReceiver<history::mojom::PageHandler> pending_page_handler) {
|
||||
browsing_history_handler_ = std::make_unique<BrowsingHistoryHandler>(
|
||||
std::move(pending_page_handler), Profile::FromWebUI(web_ui()),
|
||||
web_ui()->GetWebContents());
|
||||
}
|
||||
|
||||
void HistoryUI::BindInterface(
|
||||
mojo::PendingReceiver<history_clusters::mojom::PageHandler>
|
||||
pending_page_handler) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "ui/base/resource/resource_scale_factor.h"
|
||||
#include "ui/webui/mojo_web_ui_controller.h"
|
||||
#include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
|
||||
#include "ui/webui/resources/cr_components/history/history.mojom-forward.h"
|
||||
#include "ui/webui/resources/cr_components/history_clusters/history_clusters.mojom-forward.h"
|
||||
#include "ui/webui/resources/cr_components/history_embeddings/history_embeddings.mojom.h"
|
||||
|
||||
@ -25,6 +26,8 @@ namespace base {
|
||||
class RefCountedMemory;
|
||||
}
|
||||
|
||||
class BrowsingHistoryHandler;
|
||||
|
||||
namespace history_clusters {
|
||||
class HistoryClustersHandler;
|
||||
}
|
||||
@ -69,6 +72,8 @@ class HistoryUI : public ui::MojoWebUIController,
|
||||
void BindInterface(
|
||||
mojo::PendingReceiver<history_embeddings::mojom::PageHandler>
|
||||
pending_page_handler);
|
||||
void BindInterface(
|
||||
mojo::PendingReceiver<history::mojom::PageHandler> pending_page_handler);
|
||||
void BindInterface(mojo::PendingReceiver<history_clusters::mojom::PageHandler>
|
||||
pending_page_handler);
|
||||
void BindInterface(
|
||||
@ -90,6 +95,10 @@ class HistoryUI : public ui::MojoWebUIController,
|
||||
return history_clusters_handler_.get();
|
||||
}
|
||||
|
||||
BrowsingHistoryHandler* GetBrowsingHistoryHandlerForTesting() {
|
||||
return browsing_history_handler_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateShoppingServiceHandler(
|
||||
mojo::PendingReceiver<shopping_service::mojom::ShoppingServiceHandler>
|
||||
@ -107,6 +116,7 @@ class HistoryUI : public ui::MojoWebUIController,
|
||||
std::unique_ptr<HistoryEmbeddingsHandler> history_embeddings_handler_;
|
||||
std::unique_ptr<history_clusters::HistoryClustersHandler>
|
||||
history_clusters_handler_;
|
||||
std::unique_ptr<BrowsingHistoryHandler> browsing_history_handler_;
|
||||
std::unique_ptr<page_image_service::ImageServiceHandler>
|
||||
image_service_handler_;
|
||||
PrefChangeRegistrar pref_change_registrar_;
|
||||
|
@ -315,6 +315,7 @@ browser_exposed_mojom_targets = [
|
||||
"//ui/webui/resources/cr_components/color_change_listener:mojom",
|
||||
"//ui/webui/resources/cr_components/customize_color_scheme_mode:mojom",
|
||||
"//ui/webui/resources/cr_components/help_bubble:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history_clusters:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/history_embeddings:mojo_bindings",
|
||||
"//ui/webui/resources/cr_components/most_visited:mojom",
|
||||
|
@ -9266,7 +9266,10 @@ test("unit_tests") {
|
||||
"../browser/policy/developer_tools_policy_handler_unittest.cc",
|
||||
]
|
||||
|
||||
deps += [ "//components/webauthn/core/browser:test_support" ]
|
||||
deps += [
|
||||
"//components/webauthn/core/browser:test_support",
|
||||
"//ui/webui/resources/cr_components/history:mojo_bindings",
|
||||
]
|
||||
}
|
||||
|
||||
if (use_gio) {
|
||||
|
@ -49,6 +49,7 @@ build_webui_tests("build") {
|
||||
"//third_party/lit/v3_0:build_ts",
|
||||
"//third_party/polymer/v3_0:library",
|
||||
"//ui/webui/resources/cr_components/commerce:build_ts",
|
||||
"//ui/webui/resources/cr_components/history:build_ts",
|
||||
"//ui/webui/resources/js:build_ts",
|
||||
]
|
||||
}
|
||||
|
@ -197,7 +197,8 @@ suite('HistoryAppTest', function() {
|
||||
lastUrlVisitTimestamp: 1000,
|
||||
},
|
||||
}));
|
||||
const removeVisitsArg = await browserService.whenCalled('removeVisits');
|
||||
const removeVisitsArg =
|
||||
await browserService.handler.whenCalled('removeVisits');
|
||||
assertEquals(1, removeVisitsArg.length);
|
||||
assertEquals('http://google.com', removeVisitsArg[0].url);
|
||||
assertEquals(1, removeVisitsArg[0].timestamps.length);
|
||||
|
@ -22,7 +22,7 @@ suite('drawer-test', function() {
|
||||
app = document.createElement('history-app');
|
||||
document.body.appendChild(app);
|
||||
return Promise.all([
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
ensureLazyLoaded(),
|
||||
]);
|
||||
});
|
||||
|
@ -80,11 +80,10 @@ suite('<history-item> integration test', function() {
|
||||
document.body.innerHTML = window.trustedTypes!.emptyHTML;
|
||||
const testService = new TestBrowserService();
|
||||
BrowserServiceImpl.setInstance(testService);
|
||||
|
||||
const app = document.createElement('history-app');
|
||||
document.body.appendChild(app);
|
||||
element = app.$.history;
|
||||
return testService.whenCalled('queryHistory');
|
||||
return testService.handler.whenCalled('queryHistory');
|
||||
});
|
||||
|
||||
function getHistoryData() {
|
||||
|
@ -32,10 +32,12 @@ suite('<history-list>', function() {
|
||||
document.body.innerHTML = window.trustedTypes!.emptyHTML;
|
||||
testService = new TestBrowserService();
|
||||
BrowserServiceImpl.setInstance(testService);
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo(),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo(),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
},
|
||||
}));
|
||||
|
||||
app = document.createElement('history-app');
|
||||
document.body.appendChild(app);
|
||||
|
@ -7,6 +7,7 @@ import {BrowserServiceImpl, CrRouter, ensureLazyLoaded} from 'chrome://history/h
|
||||
import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
|
||||
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||
import {isMac} from 'chrome://resources/js/platform.js';
|
||||
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
|
||||
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
import {assertDeepEquals, assertEquals, assertFalse, assertGT, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
|
||||
import {pressAndReleaseKeyOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
|
||||
@ -57,8 +58,10 @@ suite('HistoryListTest', function() {
|
||||
function finishSetup(
|
||||
queryResults: HistoryEntry[], finished: boolean = true,
|
||||
query?: string): Promise<any> {
|
||||
testService.setQueryResult(
|
||||
{info: {finished: finished, term: query || ''}, value: queryResults});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results:
|
||||
{info: {finished: finished, term: query || ''}, value: queryResults},
|
||||
}));
|
||||
document.body.appendChild(app);
|
||||
|
||||
element = app.$.history;
|
||||
@ -66,7 +69,7 @@ suite('HistoryListTest', function() {
|
||||
app.shadowRoot!.querySelector(
|
||||
'history-query-manager')!.queryState.incremental = true;
|
||||
return Promise.all([
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
ensureLazyLoaded(),
|
||||
]);
|
||||
}
|
||||
@ -81,19 +84,22 @@ suite('HistoryListTest', function() {
|
||||
assertTrue(element.isEmpty);
|
||||
|
||||
// Load some results.
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.setQueryResult(
|
||||
{info: createHistoryInfo(), value: ADDITIONAL_RESULTS});
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor(
|
||||
'queryHistoryContinuation',
|
||||
Promise.resolve(
|
||||
{results: {info: createHistoryInfo(), value: ADDITIONAL_RESULTS}}));
|
||||
element.dispatchEvent(new CustomEvent(
|
||||
'query-history', {detail: true, bubbles: true, composed: true}));
|
||||
await testService.whenCalled('queryHistoryContinuation');
|
||||
await testService.handler.whenCalled('queryHistoryContinuation');
|
||||
await flushTasks();
|
||||
|
||||
assertFalse(element.isEmpty);
|
||||
});
|
||||
|
||||
test('DeletingSingleItem', async function() {
|
||||
await finishSetup([createHistoryEntry('2015-01-01', 'http://example.com')]);
|
||||
const visit = createHistoryEntry('2015-01-01', 'http://example.com');
|
||||
await finishSetup([visit]);
|
||||
await flushTasks();
|
||||
assertEquals(getHistoryData().length, 1);
|
||||
flush();
|
||||
@ -108,9 +114,11 @@ suite('HistoryListTest', function() {
|
||||
await flushTasks();
|
||||
const dialog = element.$.dialog.get();
|
||||
assertTrue(dialog.open);
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor('removeVisits', Promise.resolve([visit]));
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({}));
|
||||
element.shadowRoot!.querySelector<HTMLElement>('.action-button')!.click();
|
||||
const visits = await testService.whenCalled('removeVisits');
|
||||
const visits = await testService.handler.whenCalled('removeVisits');
|
||||
assertEquals(1, visits.length);
|
||||
assertEquals('http://example.com', visits[0].url);
|
||||
assertEquals(Date.parse('2015-01-01 UTC'), visits[0].timestamps[0]);
|
||||
@ -118,7 +126,7 @@ suite('HistoryListTest', function() {
|
||||
// The list should fire a query-history event which results in a
|
||||
// queryHistory call, since deleting the only item results in an
|
||||
// empty history list.
|
||||
return testService.whenCalled('queryHistory');
|
||||
return testService.handler.whenCalled('queryHistory');
|
||||
});
|
||||
|
||||
test('CancellingSelectionOfMultipleItems', async function() {
|
||||
@ -247,12 +255,14 @@ suite('HistoryListTest', function() {
|
||||
|
||||
async function loadWithAdditionalResults() {
|
||||
await finishSetup(TEST_HISTORY_RESULTS);
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.setQueryResult(
|
||||
{info: createHistoryInfo(), value: ADDITIONAL_RESULTS});
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor(
|
||||
'queryHistoryContinuation',
|
||||
Promise.resolve(
|
||||
{results: {info: createHistoryInfo(), value: ADDITIONAL_RESULTS}}));
|
||||
element.dispatchEvent(new CustomEvent(
|
||||
'query-history', {detail: true, bubbles: true, composed: true}));
|
||||
await testService.whenCalled('queryHistoryContinuation');
|
||||
await testService.handler.whenCalled('queryHistoryContinuation');
|
||||
return flushTasks();
|
||||
}
|
||||
|
||||
@ -322,11 +332,12 @@ suite('HistoryListTest', function() {
|
||||
assertNotEquals('', element.$['no-results'].textContent!.trim());
|
||||
assertTrue(element.$['infinite-list'].hidden);
|
||||
|
||||
testService.setQueryResult(
|
||||
{info: createHistoryInfo(), value: TEST_HISTORY_RESULTS});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {info: createHistoryInfo(), value: TEST_HISTORY_RESULTS},
|
||||
}));
|
||||
element.dispatchEvent(new CustomEvent(
|
||||
'query-history', {bubbles: true, composed: true, detail: false}));
|
||||
await testService.whenCalled('queryHistory');
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
await flushTasks();
|
||||
assertTrue(element.$['no-results'].hidden);
|
||||
assertFalse(element.$['infinite-list'].hidden);
|
||||
@ -339,17 +350,19 @@ suite('HistoryListTest', function() {
|
||||
new CustomEvent('iron-resize', {bubbles: true, composed: true}));
|
||||
await waitAfterNextRender(element);
|
||||
flush();
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('www.google.com'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
});
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('www.google.com'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
},
|
||||
}));
|
||||
const items = element.shadowRoot!.querySelectorAll('history-item');
|
||||
items[0]!.$['menu-button'].click();
|
||||
element.$.sharedMenu.get();
|
||||
element.shadowRoot!.querySelector<HTMLElement>('#menuMoreButton')!.click();
|
||||
const query = await testService.whenCalled('queryHistory');
|
||||
assertEquals('host:www.google.com', query);
|
||||
const query = await testService.handler.whenCalled('queryHistory');
|
||||
assertEquals('host:www.google.com', query[0]);
|
||||
await flushTasks();
|
||||
assertEquals(
|
||||
'host:www.google.com',
|
||||
@ -381,14 +394,16 @@ suite('HistoryListTest', function() {
|
||||
app.shadowRoot!.querySelector(
|
||||
'history-query-manager')!.queryState.incremental = false;
|
||||
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('ample'),
|
||||
value: [createHistoryEntry('2016-06-9', 'https://www.example.com')],
|
||||
});
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('ample'),
|
||||
value: [createHistoryEntry('2016-06-9', 'https://www.example.com')],
|
||||
},
|
||||
}));
|
||||
element.dispatchEvent(new CustomEvent(
|
||||
'query-history', {bubbles: true, composed: true, detail: false}));
|
||||
await testService.whenCalled('queryHistory');
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
assertEquals(0, toolbar.count);
|
||||
});
|
||||
|
||||
@ -414,11 +429,14 @@ suite('HistoryListTest', function() {
|
||||
await flushTasks();
|
||||
toolbar.deleteSelectedItems();
|
||||
await flushTasks();
|
||||
testService.resetResolver('removeVisits');
|
||||
testService.handler.resetResolver('removeVisits');
|
||||
const results = [...TEST_HISTORY_RESULTS, ...ADDITIONAL_RESULTS];
|
||||
testService.handler.setResultFor(
|
||||
'removeVisits', Promise.resolve([results[2], results[5], results[7]]));
|
||||
// Confirmation dialog should appear.
|
||||
assertTrue(dialog.open);
|
||||
element.shadowRoot!.querySelector<HTMLElement>('.action-button')!.click();
|
||||
const visits = await testService.whenCalled('removeVisits');
|
||||
const visits = await testService.handler.whenCalled('removeVisits');
|
||||
assertEquals(3, visits.length);
|
||||
assertEquals(TEST_HISTORY_RESULTS[2]!.url, visits[0]!.url);
|
||||
assertEquals(
|
||||
@ -466,10 +484,13 @@ suite('HistoryListTest', function() {
|
||||
|
||||
items[1]!.$['menu-button'].click();
|
||||
|
||||
testService.handler.setResultFor(
|
||||
'removeVisits', Promise.resolve([TEST_HISTORY_RESULTS[1]]));
|
||||
|
||||
element.$.sharedMenu.get();
|
||||
element.shadowRoot!.querySelector<HTMLElement>(
|
||||
'#menuRemoveButton')!.click();
|
||||
const visits = await testService.whenCalled('removeVisits');
|
||||
const visits = await testService.handler.whenCalled('removeVisits');
|
||||
assertEquals(1, visits.length);
|
||||
assertEquals(TEST_HISTORY_RESULTS[1]!.url, visits[0]!.url);
|
||||
assertEquals(
|
||||
@ -492,7 +513,10 @@ suite('HistoryListTest', function() {
|
||||
test('DeleteDisabledWhilePending', async function() {
|
||||
let items: NodeListOf<HistoryItemElement>;
|
||||
await finishSetup(TEST_HISTORY_RESULTS);
|
||||
testService.delayDelete();
|
||||
|
||||
const delayedRemove = new PromiseResolver();
|
||||
testService.handler.setResultFor('removeVisits', delayedRemove.promise);
|
||||
|
||||
await flushTasks();
|
||||
element.shadowRoot!.querySelector('iron-list')!.dispatchEvent(
|
||||
new CustomEvent('iron-resize', {bubbles: true, composed: true}));
|
||||
@ -509,7 +533,7 @@ suite('HistoryListTest', function() {
|
||||
element.$.sharedMenu.get();
|
||||
element.shadowRoot!.querySelector<HTMLElement>(
|
||||
'#menuRemoveButton')!.click();
|
||||
const visits = await testService.whenCalled('removeVisits');
|
||||
const visits = await testService.handler.whenCalled('removeVisits');
|
||||
assertEquals(1, visits.length);
|
||||
assertEquals(TEST_HISTORY_RESULTS[1]!.url, visits[0]!.url);
|
||||
assertEquals(
|
||||
@ -526,12 +550,13 @@ suite('HistoryListTest', function() {
|
||||
.querySelector('cr-button')!.disabled);
|
||||
|
||||
// Key event should be ignored.
|
||||
assertEquals(1, testService.getCallCount('removeVisits'));
|
||||
assertEquals(1, testService.handler.getCallCount('removeVisits'));
|
||||
pressAndReleaseKeyOn(document.body, 46, [], 'Delete');
|
||||
|
||||
await flushTasks();
|
||||
assertEquals(1, testService.getCallCount('removeVisits'));
|
||||
testService.finishRemoveVisits();
|
||||
assertEquals(1, testService.handler.getCallCount('removeVisits'));
|
||||
|
||||
delayedRemove.resolve({});
|
||||
await flushTasks();
|
||||
// Reselect some items.
|
||||
items = element.shadowRoot!.querySelectorAll('history-item');
|
||||
@ -581,7 +606,9 @@ suite('HistoryListTest', function() {
|
||||
]);
|
||||
|
||||
assertEquals(2, toolbar.count);
|
||||
|
||||
testService.handler.setResultFor(
|
||||
'removeVisits',
|
||||
Promise.resolve([TEST_HISTORY_RESULTS[1], TEST_HISTORY_RESULTS[2]]));
|
||||
pressAndReleaseKeyOn(document.body, 46, [], 'Delete');
|
||||
await flushTasks();
|
||||
assertTrue(dialog.open);
|
||||
@ -592,7 +619,7 @@ suite('HistoryListTest', function() {
|
||||
await flushTasks();
|
||||
assertTrue(dialog.open);
|
||||
element.shadowRoot!.querySelector<HTMLElement>('.action-button')!.click();
|
||||
const toRemove = await testService.whenCalled('removeVisits');
|
||||
const toRemove = await testService.handler.whenCalled('removeVisits');
|
||||
assertEquals('https://www.example.com', toRemove[0].url);
|
||||
assertEquals('https://www.google.com', toRemove[1].url);
|
||||
assertEquals(Date.parse('2016-03-14 10:00 UTC'), toRemove[0].timestamps[0]);
|
||||
@ -602,13 +629,15 @@ suite('HistoryListTest', function() {
|
||||
test('DeleteDialogClosedOnBackNavigation', async function() {
|
||||
// Ensure that state changes are always mirrored to the URL.
|
||||
await finishSetup([]);
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
CrRouter.getInstance().setDwellTime(0);
|
||||
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('something else'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('something else'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
},
|
||||
}));
|
||||
|
||||
// Navigate from chrome://history/ to
|
||||
// chrome://history/?q=something else.
|
||||
@ -617,15 +646,18 @@ suite('HistoryListTest', function() {
|
||||
composed: true,
|
||||
detail: {search: 'something else'},
|
||||
}));
|
||||
await testService.whenCalled('queryHistory');
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('something else'),
|
||||
value: ADDITIONAL_RESULTS,
|
||||
});
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor(
|
||||
'queryHistoryContinuation', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('something else'),
|
||||
value: ADDITIONAL_RESULTS,
|
||||
},
|
||||
}));
|
||||
element.dispatchEvent(new CustomEvent(
|
||||
'query-history', {bubbles: true, composed: true, detail: true}));
|
||||
await testService.whenCalled('queryHistoryContinuation');
|
||||
await testService.handler.whenCalled('queryHistoryContinuation');
|
||||
await flushTasks();
|
||||
const items = element.shadowRoot!.querySelectorAll('history-item');
|
||||
|
||||
@ -637,6 +669,12 @@ suite('HistoryListTest', function() {
|
||||
// Confirmation dialog should appear.
|
||||
assertTrue(element.$.dialog.getIfExists()!.open);
|
||||
// Navigate back to chrome://history.
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('something else'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
},
|
||||
}));
|
||||
window.history.back();
|
||||
|
||||
await waitForEvent(window, 'popstate');
|
||||
@ -656,7 +694,7 @@ suite('HistoryListTest', function() {
|
||||
|
||||
test('DeleteHistoryResultsInQueryHistoryEvent', async function() {
|
||||
await finishSetup(TEST_HISTORY_RESULTS);
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
webUIListenerCallback('history-deleted');
|
||||
await flushTasks();
|
||||
element.shadowRoot!.querySelector('iron-list')!.dispatchEvent(
|
||||
@ -672,10 +710,10 @@ suite('HistoryListTest', function() {
|
||||
items[3]!.$.checkbox.updateComplete,
|
||||
]);
|
||||
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
webUIListenerCallback('history-deleted');
|
||||
await flushTasks();
|
||||
assertEquals(0, testService.getCallCount('queryHistory'));
|
||||
assertEquals(0, testService.handler.getCallCount('queryHistory'));
|
||||
});
|
||||
|
||||
test('SetsScrollTarget', async () => {
|
||||
@ -730,7 +768,7 @@ suite('HistoryListTest', function() {
|
||||
document.body.style.height = '300px';
|
||||
const results = [...TEST_HISTORY_RESULTS, ...ADDITIONAL_RESULTS];
|
||||
await finishSetup(results, /*finished=*/ false);
|
||||
testService.reset();
|
||||
testService.handler.reset();
|
||||
// Make scroll debounce shorter to shorten some wait times below.
|
||||
element.setScrollDebounceForTest(1);
|
||||
|
||||
@ -743,27 +781,33 @@ suite('HistoryListTest', function() {
|
||||
app.scrollTarget.scrollHeight - app.scrollTarget.offsetHeight - 600;
|
||||
// Wait for the scroll observer to trigger.
|
||||
await eventToPromise('scroll-timeout-for-test', element);
|
||||
assertEquals(0, testService.getCallCount('queryHistoryContinuation'));
|
||||
assertEquals(
|
||||
0, testService.handler.getCallCount('queryHistoryContinuation'));
|
||||
|
||||
// Set up more results.
|
||||
testService.setQueryResult({
|
||||
info: {finished: false, term: ''},
|
||||
value: [
|
||||
createHistoryEntry('2013-02-13 10:00', 'https://en.wikipedia.org'),
|
||||
createHistoryEntry('2013-02-13 9:50', 'https://www.youtube.com'),
|
||||
createHistoryEntry('2013-02-11', 'https://www.google.com'),
|
||||
createHistoryEntry('2013-02-10', 'https://www.example.com'),
|
||||
],
|
||||
});
|
||||
testService.handler.setResultFor(
|
||||
'queryHistoryContinuation', Promise.resolve({
|
||||
results: {
|
||||
info: {finished: false, term: ''},
|
||||
value: [
|
||||
createHistoryEntry(
|
||||
'2013-02-13 10:00', 'https://en.wikipedia.org'),
|
||||
createHistoryEntry('2013-02-13 9:50', 'https://www.youtube.com'),
|
||||
createHistoryEntry('2013-02-11', 'https://www.google.com'),
|
||||
createHistoryEntry('2013-02-10', 'https://www.example.com'),
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
// Scroll to within 500px of the scroll height. More results should be
|
||||
// requested.
|
||||
app.scrollTarget.scrollTop =
|
||||
app.scrollTarget.scrollHeight - app.scrollTarget.offsetHeight - 400;
|
||||
await testService.whenCalled('queryHistoryContinuation');
|
||||
await testService.handler.whenCalled('queryHistoryContinuation');
|
||||
await flushTasks();
|
||||
assertEquals(1, testService.getCallCount('queryHistoryContinuation'));
|
||||
testService.reset();
|
||||
assertEquals(
|
||||
1, testService.handler.getCallCount('queryHistoryContinuation'));
|
||||
testService.handler.reset();
|
||||
|
||||
// Should not respond to scroll when inactive.
|
||||
element.isActive = false;
|
||||
@ -775,7 +819,8 @@ suite('HistoryListTest', function() {
|
||||
app.scrollTarget.scrollHeight - app.scrollTarget.offsetHeight - 400;
|
||||
// Wait longer than scroll debounce.
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
assertEquals(0, testService.getCallCount('queryHistoryContinuation'));
|
||||
assertEquals(
|
||||
0, testService.handler.getCallCount('queryHistoryContinuation'));
|
||||
});
|
||||
|
||||
test('ResizingLoadsMore', async () => {
|
||||
@ -783,13 +828,26 @@ suite('HistoryListTest', function() {
|
||||
document.body.style.maxHeight = '300px';
|
||||
document.body.style.height = '300px';
|
||||
await finishSetup(TEST_HISTORY_RESULTS, /*finished=*/ false);
|
||||
testService.reset();
|
||||
testService.handler.reset();
|
||||
|
||||
// Set up more results.
|
||||
testService.handler.setResultFor(
|
||||
'queryHistoryContinuation', Promise.resolve({
|
||||
results: {
|
||||
info: {finished: false, term: ''},
|
||||
value: [
|
||||
createHistoryEntry(
|
||||
'2013-02-13 10:00', 'https://en.wikipedia.org'),
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
// Simulate resizing the window. More results should be loaded.
|
||||
document.body.style.maxHeight = '800px';
|
||||
document.body.style.height = '800px';
|
||||
await testService.whenCalled('queryHistoryContinuation');
|
||||
assertEquals(1, testService.getCallCount('queryHistoryContinuation'));
|
||||
await testService.handler.whenCalled('queryHistoryContinuation');
|
||||
assertEquals(
|
||||
1, testService.handler.getCallCount('queryHistoryContinuation'));
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
|
@ -44,12 +44,13 @@ suite('Metrics', function() {
|
||||
*/
|
||||
function finishSetup(
|
||||
queryResults: HistoryEntry[], query?: string): Promise<void> {
|
||||
testService.setQueryResult(
|
||||
{info: createHistoryInfo(query), value: queryResults});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {info: createHistoryInfo(query), value: queryResults},
|
||||
}));
|
||||
document.body.appendChild(app);
|
||||
return Promise
|
||||
.all([
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
ensureLazyLoaded(),
|
||||
])
|
||||
.then(function() {
|
||||
@ -101,22 +102,25 @@ suite('Metrics', function() {
|
||||
items[1].$.link.click();
|
||||
assertEquals(1, actionMap['EntryLinkClick']);
|
||||
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('goog'),
|
||||
value: [
|
||||
createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
|
||||
createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
|
||||
createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
|
||||
],
|
||||
});
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('goog'),
|
||||
value: [
|
||||
createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
|
||||
createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
|
||||
createHistoryEntry(weekAgo.getTime(), 'http://www.google.com'),
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
app.dispatchEvent(new CustomEvent(
|
||||
'change-query',
|
||||
{bubbles: true, composed: true, detail: {search: 'goog'}}));
|
||||
assertEquals(1, actionMap['Search']);
|
||||
app.set('queryState_.incremental', true);
|
||||
await Promise.all([
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
flushTasks(),
|
||||
]);
|
||||
|
||||
@ -143,6 +147,7 @@ suite('Metrics', function() {
|
||||
app.$.toolbar.deleteSelectedItems();
|
||||
await flushTasks();
|
||||
|
||||
testService.handler.setResultFor('removeVisits', Promise.resolve());
|
||||
app.$.history.shadowRoot!.querySelector<HTMLElement>(
|
||||
'.action-button')!.click();
|
||||
assertEquals(1, actionMap['ConfirmRemoveSelected']);
|
||||
@ -156,7 +161,7 @@ suite('Metrics', function() {
|
||||
app.$.history.shadowRoot!.querySelector<HTMLElement>(
|
||||
'#menuRemoveButton')!.click();
|
||||
await Promise.all([
|
||||
testService.whenCalled('removeVisits'),
|
||||
testService.handler.whenCalled('removeVisits'),
|
||||
flushTasks(),
|
||||
]);
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ suite('#overflow-menu', function() {
|
||||
document.body.appendChild(app);
|
||||
return Promise
|
||||
.all([
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
ensureLazyLoaded(),
|
||||
])
|
||||
.then(function() {
|
||||
|
@ -264,7 +264,7 @@ suite(`routing-test-with-history-clusters-pref-set`, () => {
|
||||
navigateTo('/grouped', app);
|
||||
assertEquals(`chrome://history/grouped`, window.location.href);
|
||||
const lastSelectedTab =
|
||||
await testBrowserService.whenCalled('setLastSelectedTab');
|
||||
await testBrowserService.handler.whenCalled('setLastSelectedTab');
|
||||
assertEquals(lastSelectedTab, 1);
|
||||
});
|
||||
|
||||
|
@ -26,14 +26,13 @@ suite('routing-with-query-param', function() {
|
||||
window.history.replaceState({}, '', '/?q=query');
|
||||
testService = new TestBrowserService();
|
||||
BrowserServiceImpl.setInstance(testService);
|
||||
// Ignore the initial empty query so that we can correctly check the
|
||||
// search term for the second call to queryHistory().
|
||||
testService.ignoreNextQuery();
|
||||
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('query'),
|
||||
value: [],
|
||||
});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('query'),
|
||||
value: [],
|
||||
},
|
||||
}));
|
||||
|
||||
embeddingsHandler = TestMock.fromClass(HistoryEmbeddingsPageHandlerRemote);
|
||||
HistoryEmbeddingsBrowserProxyImpl.setInstance(
|
||||
@ -47,9 +46,9 @@ suite('routing-with-query-param', function() {
|
||||
});
|
||||
|
||||
test('search initiated on load', function() {
|
||||
return testService.whenCalled('queryHistory')
|
||||
return testService.handler.whenCalled('queryHistory')
|
||||
.then(query => {
|
||||
assertEquals(expectedQuery, query);
|
||||
assertEquals(expectedQuery, query[0]);
|
||||
return flushTasks();
|
||||
})
|
||||
.then(function() {
|
||||
@ -61,8 +60,8 @@ suite('routing-with-query-param', function() {
|
||||
|
||||
test('search with after date', async () => {
|
||||
// Wait for initial query to get called.
|
||||
await testService.whenCalled('queryHistory');
|
||||
testService.reset();
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
testService.handler.reset();
|
||||
|
||||
loadTimeData.overrideValues({enableHistoryEmbeddings: true});
|
||||
|
||||
@ -70,22 +69,38 @@ suite('routing-with-query-param', function() {
|
||||
expectedDate.setHours(0, 0, 0, 0);
|
||||
const expectedTimestamp = expectedDate.getTime();
|
||||
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo(''),
|
||||
value: [],
|
||||
},
|
||||
}));
|
||||
|
||||
navigateTo('/?q=query&after=2011-04-05', app);
|
||||
const [query, timestamp] = await testService.whenCalled('queryHistory');
|
||||
const [query, numResults, timestamp] =
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
assertEquals(expectedQuery, query);
|
||||
assertEquals(numResults, 150);
|
||||
assertEquals(expectedTimestamp, timestamp);
|
||||
});
|
||||
|
||||
test('invalidates wrongly formatted dates', async () => {
|
||||
// Wait for initial query to get called.
|
||||
await testService.whenCalled('queryHistory');
|
||||
testService.reset();
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
testService.handler.reset();
|
||||
|
||||
loadTimeData.overrideValues({enableHistoryEmbeddings: true});
|
||||
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo(''),
|
||||
value: [],
|
||||
},
|
||||
}));
|
||||
|
||||
// Invalid date format should only query the search term.
|
||||
navigateTo('/?q=hello', app);
|
||||
const searchTerm = await testService.whenCalled('queryHistory');
|
||||
assertEquals('hello', searchTerm);
|
||||
const queryArgs = await testService.handler.whenCalled('queryHistory');
|
||||
assertEquals('hello', queryArgs[0]);
|
||||
});
|
||||
});
|
||||
|
@ -24,17 +24,20 @@ suite('history-list supervised-user', function() {
|
||||
testService = new TestBrowserService();
|
||||
BrowserServiceImpl.setInstance(testService);
|
||||
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo(),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo(),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
},
|
||||
}));
|
||||
|
||||
app = document.createElement('history-app');
|
||||
document.body.appendChild(app);
|
||||
|
||||
historyList = app.$.history;
|
||||
toolbar = app.$.toolbar;
|
||||
return Promise.all([
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
ensureLazyLoaded(),
|
||||
]);
|
||||
});
|
||||
@ -67,7 +70,7 @@ suite('history-list supervised-user', function() {
|
||||
.then(() => {
|
||||
toolbar.deleteSelectedItems();
|
||||
// Make sure that removeVisits is not being called.
|
||||
assertEquals(0, testService.getCallCount('removeVisits'));
|
||||
assertEquals(0, testService.handler.getCallCount('removeVisits'));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -4,9 +4,11 @@
|
||||
|
||||
import 'chrome://history/history.js';
|
||||
|
||||
import type {HistoryAppElement, HistoryEntry} from 'chrome://history/history.js';
|
||||
import type {HistoryAppElement} from 'chrome://history/history.js';
|
||||
import {BrowserServiceImpl, ensureLazyLoaded, HistoryEmbeddingsBrowserProxyImpl, HistoryEmbeddingsPageHandlerRemote} from 'chrome://history/history.js';
|
||||
import type {HistoryEntry, QueryResult} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
|
||||
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
|
||||
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
|
||||
import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
|
||||
import {TestMock} from 'chrome://webui-test/test_mock.js';
|
||||
@ -43,17 +45,19 @@ suite('history-toolbar', function() {
|
||||
return Promise
|
||||
.all([
|
||||
ensureLazyLoaded(),
|
||||
testService.whenCalled('queryHistory'),
|
||||
testService.handler.whenCalled('queryHistory'),
|
||||
])
|
||||
.then(flushTasks);
|
||||
});
|
||||
|
||||
test('selecting checkbox causes toolbar to change', async function() {
|
||||
testService.setQueryResult(
|
||||
{info: createHistoryInfo(), value: TEST_HISTORY_RESULTS});
|
||||
testService.handler.setResultFor(
|
||||
'queryHistoryContinuation', Promise.resolve({
|
||||
results: {info: createHistoryInfo(), value: TEST_HISTORY_RESULTS},
|
||||
}));
|
||||
app.$.history.dispatchEvent(new CustomEvent(
|
||||
'query-history', {bubbles: true, composed: true, detail: true}));
|
||||
await testService.whenCalled('queryHistoryContinuation');
|
||||
await testService.handler.whenCalled('queryHistoryContinuation');
|
||||
await flushTasks();
|
||||
const item = app.$.history.shadowRoot!.querySelector('history-item')!;
|
||||
item.$.checkbox.click();
|
||||
@ -76,30 +80,36 @@ suite('history-toolbar', function() {
|
||||
});
|
||||
|
||||
test('search term gathered correctly from toolbar', async function() {
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
const toolbar = app.$.toolbar;
|
||||
testService.setQueryResult(
|
||||
{info: createHistoryInfo('Test'), value: TEST_HISTORY_RESULTS});
|
||||
testService.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {info: createHistoryInfo('Test'), value: TEST_HISTORY_RESULTS},
|
||||
}));
|
||||
toolbar.$.mainToolbar.dispatchEvent(new CustomEvent(
|
||||
'search-changed', {bubbles: true, composed: true, detail: 'Test'}));
|
||||
const query = await testService.whenCalled('queryHistory');
|
||||
assertEquals('Test', query);
|
||||
const queryArgs = await testService.handler.whenCalled('queryHistory');
|
||||
assertEquals('Test', queryArgs[0]);
|
||||
});
|
||||
|
||||
test('spinner is active on search', async function() {
|
||||
testService.resetResolver('queryHistory');
|
||||
testService.delayQueryResult();
|
||||
testService.setQueryResult({
|
||||
info: createHistoryInfo('Test2'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
});
|
||||
testService.handler.resetResolver('queryHistory');
|
||||
|
||||
const delayedQuery = new PromiseResolver<{results: QueryResult}>();
|
||||
|
||||
testService.handler.setResultFor('queryHistory', delayedQuery.promise);
|
||||
|
||||
const toolbar = app.$.toolbar;
|
||||
toolbar.$.mainToolbar.dispatchEvent(new CustomEvent(
|
||||
'search-changed', {bubbles: true, composed: true, detail: 'Test2'}));
|
||||
await testService.whenCalled('queryHistory');
|
||||
await flushTasks();
|
||||
await testService.handler.whenCalled('queryHistory');
|
||||
|
||||
assertTrue(toolbar.spinnerActive);
|
||||
testService.finishQueryHistory();
|
||||
delayedQuery.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo('Test2'),
|
||||
value: TEST_HISTORY_RESULTS,
|
||||
},
|
||||
});
|
||||
await flushTasks();
|
||||
assertFalse(toolbar.spinnerActive);
|
||||
});
|
||||
|
@ -2,22 +2,21 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import type {BrowserService, ForeignSession, QueryResult, RemoveVisitsRequest} from 'chrome://history/history.js';
|
||||
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
|
||||
import type {BrowserService, ForeignSession} from 'chrome://history/history.js';
|
||||
import {PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import {assertTrue} from 'chrome://webui-test/chai_assert.js';
|
||||
import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
|
||||
import {TestMock} from 'chrome://webui-test/test_mock.js';
|
||||
|
||||
import {createHistoryInfo} from './test_util.js';
|
||||
|
||||
export class TestBrowserService extends TestBrowserProxy implements
|
||||
BrowserService {
|
||||
handler: TestMock<PageHandlerRemote>&PageHandlerRemote;
|
||||
callbackRouter: PageCallbackRouter;
|
||||
histogramMap: {[key: string]: {[key: string]: number}} = {};
|
||||
actionMap: {[key: string]: number} = {};
|
||||
private delayedRemove_: PromiseResolver<void>|null = null;
|
||||
private delayedQueryResult_: PromiseResolver<QueryResult>|null = null;
|
||||
private ignoreNextQuery_: boolean = false;
|
||||
private foreignSessions_: ForeignSession[] = [];
|
||||
private queryResult_: QueryResult;
|
||||
|
||||
constructor() {
|
||||
super([
|
||||
@ -27,34 +26,22 @@ export class TestBrowserService extends TestBrowserProxy implements
|
||||
'navigateToUrl',
|
||||
'openForeignSessionTab',
|
||||
'otherDevicesInitialized',
|
||||
'queryHistory',
|
||||
'queryHistoryContinuation',
|
||||
'recordHistogram',
|
||||
'recordLongTime',
|
||||
'removeVisits',
|
||||
'setLastSelectedTab',
|
||||
'startTurnOnSyncFlow',
|
||||
]);
|
||||
|
||||
this.queryResult_ = {info: createHistoryInfo(), value: []};
|
||||
this.handler = TestMock.fromClass(PageHandlerRemote);
|
||||
this.callbackRouter = new PageCallbackRouter();
|
||||
|
||||
this.handler.setResultFor('queryHistory', Promise.resolve({
|
||||
results: {
|
||||
info: createHistoryInfo(''),
|
||||
value: [],
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
// Will delay resolution of the queryHistory() promise until
|
||||
// finishQueryHistory is called.
|
||||
delayQueryResult() {
|
||||
this.delayedQueryResult_ = new PromiseResolver();
|
||||
}
|
||||
|
||||
// Will delay resolution of the removeVisits() promise until
|
||||
// finishRemoveVisits is called.
|
||||
delayDelete() {
|
||||
this.delayedRemove_ = new PromiseResolver();
|
||||
}
|
||||
|
||||
// Prevents a call to methodCalled for the next call to queryHistory.
|
||||
ignoreNextQuery() {
|
||||
this.ignoreNextQuery_ = true;
|
||||
}
|
||||
|
||||
deleteForeignSession(sessionTag: string) {
|
||||
this.methodCalled('deleteForeignSession', sessionTag);
|
||||
@ -69,31 +56,6 @@ export class TestBrowserService extends TestBrowserProxy implements
|
||||
this.foreignSessions_ = sessions;
|
||||
}
|
||||
|
||||
removeVisits(removalList: RemoveVisitsRequest) {
|
||||
this.methodCalled('removeVisits', removalList);
|
||||
if (this.delayedRemove_) {
|
||||
return this.delayedRemove_.promise;
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
setLastSelectedTab(lastSelectedTab: number) {
|
||||
this.methodCalled('setLastSelectedTab', lastSelectedTab);
|
||||
}
|
||||
|
||||
// Resolves the removeVisits promise. delayRemove() must be called first.
|
||||
finishRemoveVisits() {
|
||||
this.delayedRemove_!.resolve();
|
||||
this.delayedRemove_ = null;
|
||||
}
|
||||
|
||||
// Resolves the queryHistory promise. delayQueryHistory() must be called
|
||||
// first.
|
||||
finishQueryHistory() {
|
||||
this.delayedQueryResult_!.resolve(this.queryResult_);
|
||||
this.delayedQueryResult_ = null;
|
||||
}
|
||||
|
||||
historyLoaded() {
|
||||
this.methodCalled('historyLoaded');
|
||||
}
|
||||
@ -102,8 +64,6 @@ export class TestBrowserService extends TestBrowserProxy implements
|
||||
this.methodCalled('navigateToUrl', url);
|
||||
}
|
||||
|
||||
openClearBrowsingData() {}
|
||||
|
||||
openForeignSessionAllTabs() {}
|
||||
|
||||
openForeignSessionTab(sessionTag: string, tabId: number, e: MouseEvent) {
|
||||
@ -117,32 +77,6 @@ export class TestBrowserService extends TestBrowserProxy implements
|
||||
otherDevicesInitialized() {
|
||||
this.methodCalled('otherDevicesInitialized');
|
||||
}
|
||||
|
||||
setQueryResult(queryResult: QueryResult) {
|
||||
this.queryResult_ = queryResult;
|
||||
}
|
||||
|
||||
queryHistory(searchTerm: string, afterDate?: number) {
|
||||
if (!this.ignoreNextQuery_) {
|
||||
if (afterDate) {
|
||||
this.methodCalled('queryHistory', searchTerm, afterDate);
|
||||
} else {
|
||||
this.methodCalled('queryHistory', searchTerm);
|
||||
}
|
||||
} else {
|
||||
this.ignoreNextQuery_ = false;
|
||||
}
|
||||
if (this.delayedQueryResult_) {
|
||||
return this.delayedQueryResult_.promise;
|
||||
}
|
||||
return Promise.resolve(this.queryResult_);
|
||||
}
|
||||
|
||||
queryHistoryContinuation() {
|
||||
this.methodCalled('queryHistoryContinuation');
|
||||
return Promise.resolve(this.queryResult_);
|
||||
}
|
||||
|
||||
recordAction(action: string) {
|
||||
if (!(action in this.actionMap)) {
|
||||
this.actionMap[action] = 0;
|
||||
|
@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import type {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryAppElement, HistoryEntry, HistoryQuery} from 'chrome://history/history.js';
|
||||
import type {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryAppElement} from 'chrome://history/history.js';
|
||||
import type {HistoryEntry, HistoryQuery} from 'chrome://resources/cr_components/history/history.mojom-webui.js';
|
||||
import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
|
||||
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
import {middleOfNode} from 'chrome://webui-test/mouse_mock_interactions.js';
|
||||
@ -34,6 +35,7 @@ export function createHistoryEntry(
|
||||
dateRelativeDay: d.toISOString().split('T')[0]!,
|
||||
dateShort: '',
|
||||
dateTimeOfDay: d.getUTCHours() + ':' + d.getUTCMinutes(),
|
||||
debugInfo: null,
|
||||
deviceName: '',
|
||||
deviceType: '',
|
||||
domain: domain,
|
||||
|
@ -2,16 +2,26 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//mojo/public/tools/bindings/mojom.gni")
|
||||
import("//ui/webui/resources/tools/build_webui.gni")
|
||||
|
||||
assert(!is_android && !is_ios)
|
||||
|
||||
mojom("mojo_bindings") {
|
||||
sources = [ "history.mojom" ]
|
||||
webui_module_path = ""
|
||||
}
|
||||
|
||||
build_webui("build") {
|
||||
grd_prefix = "cr_components_history_embeddings"
|
||||
non_web_component_files = [ "constants.ts" ]
|
||||
|
||||
mojo_files_deps = [ ":mojo_bindings_ts__generator" ]
|
||||
mojo_files = [ "$target_gen_dir/history.mojom-webui.ts" ]
|
||||
|
||||
ts_out_dir = "$root_gen_dir/ui/webui/resources/tsc/cr_components/history"
|
||||
ts_composite = true
|
||||
ts_deps = [ "//ui/webui/resources/mojo:build_ts" ]
|
||||
generate_grdp = true
|
||||
grd_resource_path_prefix = rebase_path(".", "//ui/webui/resources")
|
||||
}
|
||||
|
177
ui/webui/resources/cr_components/history/history.mojom
Normal file
177
ui/webui/resources/cr_components/history/history.mojom
Normal file
@ -0,0 +1,177 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
module history.mojom;
|
||||
|
||||
/**
|
||||
* The following enum must be kept in sync with its respective variants in
|
||||
* components/supervised_user/core/browser/supervised_user_utils.h
|
||||
*/
|
||||
enum FilteringBehavior {
|
||||
kUnknown = -1,
|
||||
kAllow = 0,
|
||||
// Deprecated, kWarn = 1.
|
||||
kBlock = 2,
|
||||
kInvalid = 3,
|
||||
};
|
||||
|
||||
// Represents the possible states of the query process.
|
||||
struct QueryState {
|
||||
// Whether the most recent query was incremental.
|
||||
bool incremental;
|
||||
|
||||
// Whether a query is still happening.
|
||||
bool querying;
|
||||
|
||||
// Term to search for.
|
||||
string search_term;
|
||||
|
||||
// Timestamp for determining which times to consider in subsequent query.
|
||||
string? after;
|
||||
};
|
||||
|
||||
|
||||
// Basic info about a query.
|
||||
struct HistoryQuery {
|
||||
// Term searched for.
|
||||
string term;
|
||||
|
||||
// Whether a query is finished.
|
||||
bool finished;
|
||||
};
|
||||
|
||||
// Additional debugging fields shown only if the debug feature is enabled.
|
||||
struct DebugInfo {
|
||||
// Whether URL is stored locally.
|
||||
bool is_url_in_local_database;
|
||||
|
||||
// Total number of times this URL has been visited.
|
||||
uint32 visit_count;
|
||||
|
||||
// Number of times this URL has been manually entered in the URL bar.
|
||||
uint32 typed_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a single entry. Holds all information needed to display in the
|
||||
* UI. Most of this data is stored here: browsing_history_service.h.
|
||||
*/
|
||||
struct HistoryEntry {
|
||||
// URL of visit.
|
||||
string url;
|
||||
|
||||
// Title of the entry. May be empty.
|
||||
string title;
|
||||
|
||||
// Domain of visit.
|
||||
string domain;
|
||||
|
||||
// Fallback favicon url.
|
||||
string fallback_favicon_text;
|
||||
|
||||
// The time of the entry. Usually this will be the time of the most recent
|
||||
// visit to `url` on a particular day as defined in the local timezone.
|
||||
double time;
|
||||
|
||||
// Timestamps of all local or remote visits the same URL on the same day.
|
||||
array<double> all_timestamps;
|
||||
|
||||
// Date to display on various services.
|
||||
string date_short;
|
||||
|
||||
// Name of device entry was visited on.
|
||||
string device_name;
|
||||
|
||||
// Type of device entry was visited on.
|
||||
string device_type;
|
||||
|
||||
// Used in the screenreader entry summary.
|
||||
string date_time_of_day;
|
||||
|
||||
// Currently to determine the boundaries between cards and other visual
|
||||
// information.
|
||||
string date_relative_day;
|
||||
|
||||
// Boolean to determine if a visit is currently selected.
|
||||
bool selected;
|
||||
|
||||
// Timestamp to be read by screenreader.
|
||||
string readableTimestamp;
|
||||
|
||||
// The entry's search snippet, if this entry is a search result.
|
||||
string snippet;
|
||||
|
||||
// If an entry is bookmarked.
|
||||
bool starred;
|
||||
|
||||
// Whether an entry is allowed via a supervised user.
|
||||
FilteringBehavior host_filtering_behavior;
|
||||
|
||||
// Whether an entry is blocked.
|
||||
bool blocked_visit;
|
||||
|
||||
// Whether the URL was obtained from a remote device.
|
||||
bool is_url_in_remote_user_data;
|
||||
|
||||
// Optional parameter used to plumb footprints associated icon url.
|
||||
string remote_icon_url_for_uma;
|
||||
|
||||
// Optional debug info about a visit.
|
||||
DebugInfo? debug_info;
|
||||
};
|
||||
|
||||
|
||||
// Hold info about query as well as array of returned entries.
|
||||
struct QueryResult {
|
||||
// Holds search term and whether a query is finished.
|
||||
HistoryQuery? info;
|
||||
|
||||
// Array of returned entries.
|
||||
array<HistoryEntry> value;
|
||||
};
|
||||
|
||||
// Represents an item to be removed.
|
||||
struct RemovalItem {
|
||||
// URL of items to be removed.
|
||||
string url;
|
||||
|
||||
// Timestamps of items to be removed.
|
||||
array<double> timestamps;
|
||||
};
|
||||
|
||||
// Browser-side handler for requests from WebUI page.
|
||||
interface PageHandler {
|
||||
// The BrowserProxy singleton calls this when it's first initialized.
|
||||
SetPage(pending_remote<Page> page);
|
||||
|
||||
// Queries for entries matching the query term. Returns a promise of a
|
||||
// result object.
|
||||
QueryHistory(string query, int32 max_results, double? begin_time) => (QueryResult results);
|
||||
|
||||
// Completes continuation of ongoing query. Returns a promise of a result
|
||||
// object.
|
||||
QueryHistoryContinuation() => (QueryResult results);
|
||||
|
||||
// Removes marked visits.
|
||||
RemoveVisits(array<RemovalItem> items) => ();
|
||||
|
||||
// Opens clear browsing data dialog.
|
||||
OpenClearBrowsingDataDialog();
|
||||
|
||||
// Removes bookmark for a given url.
|
||||
RemoveBookmark(string url);
|
||||
|
||||
// Sets last selected tab so user returns to most recently used tab upon
|
||||
// revisiting the history page.
|
||||
SetLastSelectedTab(int32 last_tab);
|
||||
};
|
||||
|
||||
// WebUI-side handler for requests from browser.
|
||||
interface Page {
|
||||
// Alerts the page that the history was deleted.
|
||||
OnHistoryDeleted();
|
||||
|
||||
// Alerts the page the other forms of history may or may not be available.
|
||||
OnHasOtherFormsChanged(bool has_other_forms);
|
||||
};
|
Reference in New Issue
Block a user