Commit 092d4eac authored by Moe Ahmadi's avatar Moe Ahmadi Committed by Commit Bot

Adds promo browser commands JS resources

- Adds promo browser command JS resources.
- Makes changes to NTP JS to handle browser commands from the promos.
- Exposes the desktop implementation of the handler interface via NTP.
- Adds browser tests.

For more information see go/promos-deeplinking-settings

Bug: 1106421
Change-Id: Ib2643f47889cbb5d4a05b7721b1f5026e120a5bb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2333292
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarNasko Oskov <nasko@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#794740}
parent c3c5876e
......@@ -110,6 +110,7 @@
#include "chrome/browser/media/kaleidoscope/kaleidoscope_ui.h"
#include "chrome/browser/media/kaleidoscope/mojom/kaleidoscope.mojom.h"
#include "chrome/browser/payments/payment_request_factory.h"
#include "chrome/browser/promo_browser_command/promo_browser_command.mojom.h"
#include "chrome/browser/speech/speech_recognition_service.h"
#include "chrome/browser/speech/speech_recognition_service_factory.h"
#include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
......@@ -524,6 +525,9 @@ void PopulateChromeWebUIFrameBinders(
RegisterWebUIControllerInterfaceBinder<
new_tab_page::mojom::PageHandlerFactory, NewTabPageUI>(map);
RegisterWebUIControllerInterfaceBinder<
promo_browser_command::mojom::CommandHandler, NewTabPageUI>(map);
RegisterWebUIControllerInterfaceBinder<media_feeds::mojom::MediaFeedsStore,
MediaFeedsUI>(map);
......
......@@ -260,6 +260,7 @@ if (!is_android) {
}
deps = [
"//chrome/browser/promo_browser_command:mojo_bindings_js",
"//chrome/browser/ui/webui/new_tab_page:mojo_bindings_js",
"//skia/public/mojom:mojom_js",
]
......
......@@ -23,6 +23,7 @@ js_type_check("closure_compile") {
":module_registry",
":modules",
":one_google_bar_api",
":promo_browser_command_proxy",
":realbox",
":realbox_button",
":realbox_dropdown",
......@@ -51,12 +52,20 @@ js_library("app") {
":modules",
":most_visited",
":one_google_bar_api",
":promo_browser_command_proxy",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
"//ui/webui/resources/js:event_tracker.m",
"//ui/webui/resources/js:load_time_data.m",
]
}
js_library("promo_browser_command_proxy") {
deps = [
"//chrome/browser/promo_browser_command:mojo_bindings_js_library_for_compile",
"//ui/webui/resources/js:cr.m",
]
}
js_library("most_visited") {
deps = [
":browser_proxy",
......@@ -271,6 +280,7 @@ if (optimize_webui) {
source = "new_tab_page_resources.grd"
deps = [
"//chrome/browser/promo_browser_command:mojo_bindings_js",
"//chrome/browser/resources/new_tab_page:web_components",
"//chrome/browser/ui/webui/new_tab_page:mojo_bindings_js",
"//skia/public/mojom:mojom_js",
......@@ -296,6 +306,7 @@ if (optimize_webui) {
deps = [ ":unoptimized_resources" ]
excludes = [
"../../promo_browser_command/promo_browser_command.mojom-lite.js",
"../../ui/webui/new_tab_page/new_tab_page.mojom-lite.js",
"../../../common/search/omnibox.mojom-lite.js",
"../../../../skia/public/mojom/skcolor.mojom-lite.js",
......@@ -322,6 +333,7 @@ if (optimize_webui) {
"chrome://resources/mojo/url/mojom/url.mojom-lite.js",
"new_tab_page.mojom-lite.js",
"omnibox.mojom-lite.js",
"promo_browser_command.mojom-lite.js",
]
}
}
......@@ -24,8 +24,17 @@ import {BrowserProxy} from './browser_proxy.js';
import {BackgroundSelection, BackgroundSelectionType} from './customize_dialog.js';
import {registry} from './modules/modules.js';
import {oneGoogleBarApi} from './one_google_bar_api.js';
import {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js';
import {$$, hexColorToSkColor, skColorToRgba} from './utils.js';
/**
* @typedef {{
* commandId: promoBrowserCommand.mojom.Command<number>,
* clickInfo: !promoBrowserCommand.mojom.ClickInfo
* }}
*/
let CommandData;
class AppElement extends PolymerElement {
static get is() {
return 'ntp-app';
......@@ -227,7 +236,9 @@ class AppElement extends PolymerElement {
performance.measure('theme-set');
this.theme_ = theme;
});
this.eventTracker_.add(window, 'message', ({data}) => {
this.eventTracker_.add(window, 'message', (event) => {
/** @type {!Object} */
const data = event.data;
// Something in OneGoogleBar is sending a message that is received here.
// Need to ignore it.
if (typeof data !== 'object') {
......@@ -235,9 +246,9 @@ class AppElement extends PolymerElement {
}
if ('frameType' in data) {
if (data.frameType === 'promo') {
this.handlePromoMessage_(data);
this.handlePromoMessage_(event);
} else if (data.frameType === 'one-google-bar') {
this.handleOneGoogleBarMessage_(data);
this.handleOneGoogleBarMessage_(event);
}
}
});
......@@ -639,6 +650,32 @@ class AppElement extends PolymerElement {
}
}
/**
* Sends the command and the accompanying mouse click info received from the
* promo of the given source and origin to the browser. Relays the execution
* status response back to the source promo frame. |commandSource| and
* |commandOrigin| are used only to send the execution status response back to
* the source promo frame and should not be used for anything else.
* @param {!CommandData} commandData Command and mouse click info.
* @param {Window} commandSource Source promo frame.
* @param {string} commandOrigin Origin of the source promo frame.
* @private
*/
executePromoBrowserCommand_(commandData, commandSource, commandOrigin) {
// Make sure we don't send unsupported commands to the browser.
/** @type {!promoBrowserCommand.mojom.Command} */
const commandId = Object.values(promoBrowserCommand.mojom.Command)
.includes(commandData.commandId) ?
commandData.commandId :
promoBrowserCommand.mojom.Command.kUnknownCommand;
PromoBrowserCommandProxy.getInstance()
.handler.executeCommand(commandId, commandData.clickInfo)
.then(({commandExecuted}) => {
commandSource.postMessage(commandExecuted, commandOrigin);
});
}
/**
* Handles messages from the OneGoogleBar iframe. The messages that are
* handled include show bar on load and overlay updates.
......@@ -649,10 +686,12 @@ class AppElement extends PolymerElement {
* When modal overlays are enabled, activate/deactivate controls if the
* OneGoogleBar is layered on top of #content with a backdrop. This would
* happen when OneGoogleBar has an overlay open.
* @param {!Object} data
* @param {!MessageEvent} event
* @private
*/
handleOneGoogleBarMessage_(data) {
handleOneGoogleBarMessage_(event) {
/** @type {!Object} */
const data = event.data;
if (data.messageType === 'loaded') {
if (!this.oneGoogleBarModalOverlaysEnabled_) {
const oneGoogleBar = $$(this, '#oneGoogleBar');
......@@ -683,6 +722,9 @@ class AppElement extends PolymerElement {
} else if (data.messageType === 'deactivate') {
this.$.oneGoogleBarOverlayBackdrop.toggleAttribute('show', false);
$$(this, '#oneGoogleBar').style.zIndex = '0';
} else if (data.messageType === 'execute-browser-command') {
this.executePromoBrowserCommand_(
/** @type CommandData */ (data), event.source, event.origin);
}
}
......@@ -690,10 +732,12 @@ class AppElement extends PolymerElement {
* Handle messages from promo iframe. This shows the promo on load and sets
* up the show/hide logic (in case there is an overlap with most-visited
* tiles).
* @param {!Object} data
* @param {!MessageEvent} event
* @private
*/
handlePromoMessage_(data) {
handlePromoMessage_(event) {
/** @type {!Object} */
const data = event.data;
if (data.messageType === 'loaded') {
this.promoLoaded_ = true;
const onResize = () => {
......@@ -706,6 +750,9 @@ class AppElement extends PolymerElement {
this.pageHandler_.onPromoRendered(BrowserProxy.getInstance().now());
} else if (data.messageType === 'link-clicked') {
this.pageHandler_.onPromoLinkClicked();
} else if (data.messageType === 'execute-browser-command') {
this.executePromoBrowserCommand_(
/** @type CommandData */ (data), event.source, event.origin);
}
}
......
......@@ -17,5 +17,6 @@ export {BrowserProxy} from './browser_proxy.js';
export {BackgroundSelectionType} from './customize_dialog.js';
export {dummyDescriptor} from './modules/dummy/module.js';
export {ModuleRegistry} from './modules/module_registry.js';
export {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js';
export {NO_SUGGESTION_GROUP_ID} from './realbox_dropdown.js';
export {$$, createScrollBorders, decodeString16, hexColorToSkColor, mojoString16, skColorToRgba} from './utils.js';
......@@ -6,6 +6,9 @@
<include name="IDR_NEW_TAB_PAGE_OMNIBOX_MOJO_LITE_JS"
file="${root_gen_dir}/chrome/common/search/omnibox.mojom-lite.js"
use_base_dir="false" type="BINDATA" />
<include name="IDR_NEW_TAB_PAGE_PROMO_BROWSER_COMMAND_MOJO_LITE_JS"
file="${root_gen_dir}/chrome/browser/promo_browser_command/promo_browser_command.mojom-lite.js"
use_base_dir="false" type="BINDATA" />
<include name="IDR_NEW_TAB_PAGE_ACCOUNT_CIRCLE_SVG"
file="icons/account_circle.svg" type="BINDATA" />
<include name="IDR_NEW_TAB_PAGE_BRUSH_ICON_SVG"
......@@ -64,4 +67,6 @@
file="untrusted/background_image.js" type="BINDATA" />
<include name="IDR_NEW_TAB_PAGE_ONE_GOOGLE_BAR_API_JS"
file="one_google_bar_api.js" type="BINDATA" compress="false" />
<include name="IDR_NEW_TAB_PAGE_PROMO_BROWSER_COMMAND_PROXY_JS"
file="promo_browser_command_proxy.js" type="BINDATA" compress="false" />
</grit-part>
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import './promo_browser_command.mojom-lite.js';
import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
/**
* @fileoverview This file provides a class that exposes the Mojo handler
* interface used for sending the NTP promos browser commands to the browser and
* receiving the browser response.
*/
export class PromoBrowserCommandProxy {
constructor() {
/** @type {!promoBrowserCommand.mojom.CommandHandlerRemote} */
this.handler = promoBrowserCommand.mojom.CommandHandler.getRemote();
}
}
addSingletonGetter(PromoBrowserCommandProxy);
......@@ -16,6 +16,7 @@
#include "chrome/browser/ui/search/omnibox_mojo_utils.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
#include "chrome/browser/ui/webui/new_tab_page/promo_browser_command/promo_browser_command_handler.h"
#include "chrome/browser/ui/webui/new_tab_page/untrusted_source.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/browser/ui/webui/webui_util.h"
......@@ -199,6 +200,8 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
IDR_NEW_TAB_PAGE_MOJO_LITE_JS);
source->AddResourcePath("omnibox.mojom-lite.js",
IDR_NEW_TAB_PAGE_OMNIBOX_MOJO_LITE_JS);
source->AddResourcePath("promo_browser_command.mojom-lite.js",
IDR_NEW_TAB_PAGE_PROMO_BROWSER_COMMAND_MOJO_LITE_JS);
#if BUILDFLAG(OPTIMIZE_WEBUI)
source->AddResourcePath("new_tab_page.js", IDR_NEW_TAB_PAGE_NEW_TAB_PAGE_JS);
#endif // BUILDFLAG(OPTIMIZE_WEBUI)
......@@ -280,6 +283,13 @@ void NewTabPageUI::BindInterface(
page_factory_receiver_.Bind(std::move(pending_receiver));
}
void NewTabPageUI::BindInterface(
mojo::PendingReceiver<promo_browser_command::mojom::CommandHandler>
pending_page_handler) {
promo_browser_command_handler_ = std::make_unique<PromoBrowserCommandHandler>(
std::move(pending_page_handler), profile_);
}
void NewTabPageUI::CreatePageHandler(
mojo::PendingRemote<new_tab_page::mojom::Page> pending_page,
mojo::PendingReceiver<new_tab_page::mojom::PageHandler>
......
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_WEBUI_NEW_TAB_PAGE_NEW_TAB_PAGE_UI_H_
#include "base/macros.h"
#include "chrome/browser/promo_browser_command/promo_browser_command.mojom-forward.h"
#include "chrome/browser/search/instant_service_observer.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
#include "content/public/browser/web_contents_observer.h"
......@@ -14,6 +15,7 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "ui/webui/mojo_web_ui_controller.h"
class PromoBrowserCommandHandler;
namespace content {
class NavigationHandle;
class WebContents;
......@@ -40,6 +42,13 @@ class NewTabPageUI : public ui::MojoWebUIController,
mojo::PendingReceiver<new_tab_page::mojom::PageHandlerFactory>
pending_receiver);
// Instantiates the implementor of the
// promo_browser_command::mojom::CommandHandler mojo interface passing the
// pending receiver that will be internally bound.
void BindInterface(
mojo::PendingReceiver<promo_browser_command::mojom::CommandHandler>
pending_receiver);
private:
// new_tab_page::mojom::PageHandlerFactory:
void CreatePageHandler(
......@@ -63,6 +72,7 @@ class NewTabPageUI : public ui::MojoWebUIController,
std::unique_ptr<NewTabPageHandler> page_handler_;
mojo::Receiver<new_tab_page::mojom::PageHandlerFactory>
page_factory_receiver_;
std::unique_ptr<PromoBrowserCommandHandler> promo_browser_command_handler_;
Profile* profile_;
InstantService* instant_service_;
content::WebContents* web_contents_;
......
......@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {$$, BackgroundManager, BackgroundSelectionType, BrowserProxy} from 'chrome://new-tab-page/new_tab_page.js';
import {$$, BackgroundManager, BackgroundSelectionType, BrowserProxy, PromoBrowserCommandProxy} from 'chrome://new-tab-page/new_tab_page.js';
import {isMac} from 'chrome://resources/js/cr.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {assertNotStyle, assertStyle, createTestProxy, createTheme} from 'chrome://test/new_tab_page/test_support.js';
import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
import {flushTasks} from 'chrome://test/test_util.m.js';
import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
suite('NewTabPageAppTest', () => {
/** @type {!AppElement} */
......@@ -389,4 +389,38 @@ suite('NewTabPageAppTest', () => {
await testProxy.callbackRouterRemote.$.flushForTesting();
assertTrue(app.$.mostVisited.hasAttribute('use-title-pill'));
});
test('executes promo browser command', async () => {
const testProxy = PromoBrowserCommandProxy.getInstance();
testProxy.handler = TestBrowserProxy.fromClass(
promoBrowserCommand.mojom.CommandHandlerRemote);
testProxy.handler.setResultFor(
'executeCommand', Promise.resolve({commandExecuted: true}));
const commandId = 123; // Unsupported command.
const clickInfo = {middleButton: true};
window.dispatchEvent(new MessageEvent('message', {
data: {
frameType: 'one-google-bar',
messageType: 'execute-browser-command',
commandId,
clickInfo,
},
source: window,
origin: window.origin,
}));
// Make sure the command and click information are sent to the browser.
const [expectedCommandId, expectedClickInfo] =
await testProxy.handler.whenCalled('executeCommand');
// Unsupported commands get resolved to the default command before being
// sent to the browser.
assertEquals(
promoBrowserCommand.mojom.Command.kUnknownCommand, expectedCommandId);
assertEquals(clickInfo, expectedClickInfo);
// Make sure the promo frame gets notified whether the command was executed.
const {data: commandExecuted} = await eventToPromise('message', window);
assertTrue(commandExecuted);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment