Commit 3c502d26 authored by Esmael El-Moslimany's avatar Esmael El-Moslimany Committed by Commit Bot

WebUI NTP: load one-google-bar

The goal of this CL is to make the OneGoogleBar as functional as
possible and allow collaboration with the OneGoogle team to make the
OneGoogleBar completely functional as well as provide the ability to
test end-to-end.

The OneGoogleBar is hosted by
chrome-untrusted://new-tab-page/one-google-bar. The WebUI NTP embeds
this using an iframe.

In order for OneGoogleBar overlays to work, this CL uses mouse event
forwarding. While this may not be the approach used for launch, it will
allow the NTP and OneGoogle teams to test the OneGoogleBar and iterate
towards a better approach.

The Gmail and Images links are also updated so they open in the top
frame. This is temporary until the OneGoogleBar updates those links to
use <a target="_top">.

Bug: 1039913
Change-Id: Ic265d5bfd5c8728c6ff3b6556cede51c92571863
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2106739Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Commit-Queue: Esmael Elmoslimany <aee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751407}
parent b0dba9b7
......@@ -43,6 +43,19 @@
flex-direction: column;
}
#oneGoogleBarSpacer {
height: 56px;
width: 100%;
}
#oneGoogleBar {
height: 100%;
pointer-events: none;
position: fixed;
width: 100%;
z-index: 1;
}
#promo {
bottom: 16px;
height: 35px;
......@@ -154,6 +167,10 @@
</ntp-untrusted-iframe>
<div id="backgroundGradient" hidden="[[!showBackgroundImage_]]"></div>
<div id="content">
<div id="oneGoogleBarSpacer"></div>
<ntp-untrusted-iframe id="oneGoogleBar" path="one-google-bar"
hidden$="[[!oneGoogleBarLoaded_]]">
</ntp-untrusted-iframe>
<ntp-fakebox id="fakebox" on-open-voice-search="onVoiceSearchClick_">
</ntp-fakebox>
<ntp-most-visited id="mostVisited" dark$="[[theme_.isDark]]">
......
......@@ -29,6 +29,12 @@ class AppElement extends PolymerElement {
static get properties() {
return {
/** @private */
oneGoogleBarLoaded_: {
type: Boolean,
value: false,
},
/** @private */
promoLoaded_: {
type: Boolean,
......@@ -77,16 +83,17 @@ class AppElement extends PolymerElement {
this.theme_ = theme;
});
this.eventTracker_.add(window, 'message', ({data}) => {
if ('frameType' in data && data.frameType === 'promo' &&
data.messageType === 'loaded') {
this.promoLoaded_ = true;
const onResize = () => {
const hidePromo = this.$.mostVisited.getBoundingClientRect().bottom >=
this.$.promo.offsetTop;
this.$.promo.style.opacity = hidePromo ? 0 : 1;
};
this.eventTracker_.add(window, 'resize', onResize);
onResize();
// Something in OneGoogleBar is sending a message that is received here.
// Need to ignore it.
if (typeof data !== 'object') {
return;
}
if ('frameType' in data) {
if (data.frameType === 'promo') {
this.handlePromoMessage_(data);
} else if (data.frameType === 'one-google-bar') {
this.handleOneGoogleBarMessage_(data);
}
}
});
}
......@@ -145,6 +152,47 @@ class AppElement extends PolymerElement {
}
return `image?${this.theme_.backgroundImageUrl.url}`;
}
/**
* Handles messages from the OneGoogleBar iframe. The messages that are
* handled include show bar on load and activate/deactivate.
* The activate/deactivate controls if the OneGoogleBar accepts mouse events,
* though other events need to be forwarded to support touch.
* @param {!Object} data
* @private
*/
handleOneGoogleBarMessage_(data) {
if (data.messageType === 'loaded') {
this.oneGoogleBarLoaded_ = true;
this.eventTracker_.add(window, 'mousemove', ({x, y}) => {
this.$.oneGoogleBar.postMessage({type: 'mousemove', x, y});
});
} else if (data.messageType === 'activate') {
this.$.oneGoogleBar.style.pointerEvents = 'unset';
} else if (data.messageType === 'deactivate') {
this.$.oneGoogleBar.style.pointerEvents = 'none';
}
}
/**
* 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
* @private
*/
handlePromoMessage_(data) {
if (data.messageType === 'loaded') {
this.promoLoaded_ = true;
const onResize = () => {
const hidePromo = this.$.mostVisited.getBoundingClientRect().bottom >=
this.$.promo.offsetTop;
this.$.promo.style.opacity = hidePromo ? 0 : 1;
};
this.eventTracker_.add(window, 'resize', onResize);
onResize();
}
}
}
customElements.define(AppElement.is, AppElement);
......@@ -92,6 +92,12 @@
file="browser_proxy.js" type="chrome_html" compress="gzip" />
<structure name="IDR_NEW_TAB_PAGE_UTILS_JS"
file="utils.js" type="chrome_html" compress="gzip" />
<structure name="IDR_NEW_TAB_PAGE_UNTRUSTED_ONE_GOOGLE_BAR_HTML"
file="untrusted/one_google_bar.html" type="chrome_html"
compress="gzip" />
<structure name="IDR_NEW_TAB_PAGE_UNTRUSTED_ONE_GOOGLE_BAR_JS"
file="untrusted/one_google_bar.js" type="chrome_html"
compress="gzip" />
<structure name="IDR_NEW_TAB_PAGE_UNTRUSTED_PROMO_HTML"
file="untrusted/promo.html" type="chrome_html" compress="gzip" />
<structure name="IDR_NEW_TAB_PAGE_UNTRUSTED_PROMO_JS"
......
<!doctype html>
<html>
<head>
<style>
body {
margin: 0;
overflow: hidden;
}
$i18nRaw{inHeadStyle}
</style>
<script>$i18nRaw{inHeadScript}</script>
</head>
<body>
$i18nRaw{barHtml}
<script>$i18nRaw{afterBarScript}</script>
$i18nRaw{endOfBodyHtml}
<script>$i18nRaw{endOfBodyScript}</script>
<script src="one_google_bar.js"></script>
</body>
</html>
// 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.
/**
* The following |messageType|'s are sent to the parent frame:
* - loaded: initial load
* - deactivate: the pointer is not over OneGoogleBar content
* - activate: the pointer is over OneGoogleBar content
*
* TODO(crbug.com/1039913): add support for light/dark theme. add support for
* forwarding touch events when OneGoogleBar is active.
*
* @param {string} messageType
*/
function postMessage(messageType) {
window.parent.postMessage(
{frameType: 'one-google-bar', messageType}, 'chrome://new-tab-page');
}
// Tracks if the OneGoogleBar is active and should accept pointer events.
let isActive;
/**
* @param {number} x
* @param {number} y
*/
function updateActiveState(x, y) {
const shouldBeActive = document.elementFromPoint(x, y).tagName !== 'HTML';
if (shouldBeActive === isActive) {
return;
}
isActive = shouldBeActive;
postMessage(shouldBeActive ? 'activate' : 'deactivate');
}
// Handle messages from parent frame which include forwarded mousemove events.
// The OneGoogleBar is loaded in an iframe on top of the embedder parent frame.
// The mousemove events are used to determine if the OneGoogleBar should be
// active or not.
// TODO(crbug.com/1039913): add support for touch which does not send mousemove
// events.
window.addEventListener('message', ({data}) => {
if (data.type === 'mousemove') {
updateActiveState(data.x, data.y);
}
});
window.addEventListener('mousemove', ({x, y}) => {
updateActiveState(x, y);
});
document.addEventListener('DOMContentLoaded', () => {
// TODO(crbug.com/1039913): remove after OneGoogleBar links are updated.
// Updates <a>'s so they load on the top frame instead of the iframe.
document.body.querySelectorAll('a').forEach(el => {
if (el.target !== '_blank') {
el.target = '_top';
}
});
postMessage('loaded');
});
......@@ -6,4 +6,4 @@
width: 100%;
}
</style>
<iframe src="[[src_]]"></iframe>
<iframe id="iframe" src="[[src_]]"></iframe>
......@@ -30,6 +30,15 @@ class UntrustedIframeElement extends PolymerElement {
};
}
/**
* Sends message to iframe.
* @param {*} message
*/
postMessage(message) {
this.$.iframe.contentWindow.postMessage(
message, 'chrome-untrusted://new-tab-page');
}
/** @private */
computeSrc_() {
return BrowserProxy.getInstance().createUntrustedIframeSrc(this.path);
......
......@@ -8,9 +8,12 @@
#include <utility>
#include "base/memory/ref_counted_memory.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
#include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
#include "chrome/browser/search/promos/promo_data.h"
#include "chrome/browser/search/promos/promo_service_factory.h"
#include "chrome/common/url_constants.h"
......@@ -33,18 +36,30 @@ std::string FormatTemplate(int resource_id,
} // namespace
UntrustedSource::UntrustedSource(Profile* profile)
: promo_service_(PromoServiceFactory::GetForProfile(profile)) {
: one_google_bar_service_(
OneGoogleBarServiceFactory::GetForProfile(profile)),
promo_service_(PromoServiceFactory::GetForProfile(profile)) {
// |promo_service_| is null in incognito, or when the feature is
// disabled.
if (promo_service_) {
promo_service_observer_.Add(promo_service_);
}
// |one_google_bar_service_| is null in incognito, or when the feature is
// disabled.
if (one_google_bar_service_) {
one_google_bar_service_observer_.Add(one_google_bar_service_);
}
}
UntrustedSource::~UntrustedSource() = default;
std::string UntrustedSource::GetContentSecurityPolicyScriptSrc() {
return "script-src 'self' 'unsafe-inline';";
return "script-src 'self' 'unsafe-inline' https:;";
}
std::string UntrustedSource::GetContentSecurityPolicyChildSrc() {
return "child-src https:;";
}
std::string UntrustedSource::GetSource() {
......@@ -56,6 +71,19 @@ void UntrustedSource::StartDataRequest(
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) {
const std::string path = url.has_path() ? url.path().substr(1) : "";
if (path == "one-google-bar" && one_google_bar_service_) {
one_google_bar_callbacks_.push_back(std::move(callback));
if (one_google_bar_callbacks_.size() == 1) {
one_google_bar_service_->Refresh();
}
return;
}
if (path == "one_google_bar.js") {
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
std::move(callback).Run(bundle.LoadDataResourceBytes(
IDR_NEW_TAB_PAGE_UNTRUSTED_ONE_GOOGLE_BAR_JS));
return;
}
if (path == "promo" && promo_service_) {
promo_callbacks_.push_back(std::move(callback));
if (promo_callbacks_.size() == 1) {
......@@ -108,7 +136,34 @@ bool UntrustedSource::ShouldServiceRequest(
return false;
}
const std::string path = url.path().substr(1);
return path == "promo" || path == "promo.js" || path == "image";
return path == "one-google-bar" || path == "one_google_bar.js" ||
path == "promo" || path == "promo.js" || path == "image";
}
void UntrustedSource::OnOneGoogleBarDataUpdated() {
base::Optional<OneGoogleBarData> data =
one_google_bar_service_->one_google_bar_data();
std::string html;
if (data.has_value()) {
ui::TemplateReplacements replacements;
replacements["barHtml"] = data->bar_html;
replacements["inHeadScript"] = data->in_head_script;
replacements["inHeadStyle"] = data->in_head_style;
replacements["afterBarScript"] = data->after_bar_script;
replacements["endOfBodyHtml"] = data->end_of_body_html;
replacements["endOfBodyScript"] = data->end_of_body_script;
html = FormatTemplate(IDR_NEW_TAB_PAGE_UNTRUSTED_ONE_GOOGLE_BAR_HTML,
replacements);
}
for (auto& callback : one_google_bar_callbacks_) {
std::move(callback).Run(base::RefCountedString::TakeString(&html));
}
one_google_bar_callbacks_.clear();
}
void UntrustedSource::OnOneGoogleBarServiceShuttingDown() {
one_google_bar_service_observer_.RemoveAll();
one_google_bar_service_ = nullptr;
}
void UntrustedSource::OnPromoDataUpdated() {
......
......@@ -9,6 +9,8 @@
#include <vector>
#include "base/scoped_observer.h"
#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
#include "chrome/browser/search/one_google_bar/one_google_bar_service_observer.h"
#include "chrome/browser/search/promos/promo_service.h"
#include "chrome/browser/search/promos/promo_service_observer.h"
#include "content/public/browser/url_data_source.h"
......@@ -20,6 +22,7 @@ class Profile;
// sources can only be embedded in the chrome://new-tab-page by using an
// <iframe>.
class UntrustedSource : public content::URLDataSource,
public OneGoogleBarServiceObserver,
public PromoServiceObserver {
public:
explicit UntrustedSource(Profile* profile);
......@@ -29,6 +32,7 @@ class UntrustedSource : public content::URLDataSource,
// content::URLDataSource:
std::string GetContentSecurityPolicyScriptSrc() override;
std::string GetContentSecurityPolicyChildSrc() override;
std::string GetSource() override;
void StartDataRequest(
const GURL& url,
......@@ -43,10 +47,19 @@ class UntrustedSource : public content::URLDataSource,
int render_process_id) override;
private:
// OneGoogleBarServiceObserver:
void OnOneGoogleBarDataUpdated() override;
void OnOneGoogleBarServiceShuttingDown() override;
// PromoServiceObserver:
void OnPromoDataUpdated() override;
void OnPromoServiceShuttingDown() override;
std::vector<content::URLDataSource::GotDataCallback>
one_google_bar_callbacks_;
OneGoogleBarService* one_google_bar_service_;
ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver>
one_google_bar_service_observer_{this};
std::vector<content::URLDataSource::GotDataCallback> promo_callbacks_;
PromoService* promo_service_;
ScopedObserver<PromoService, PromoServiceObserver> promo_service_observer_{
......
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