Commit 8684a69c authored by Donn Denman's avatar Donn Denman Committed by Commit Bot

[TTS] Add a new JS API: ChangeOverlayPosition.

This adds a second JavaScript API entry point to the Contextual Search
Overlay Panel that can be called from the panel content.

The new API allows changing the Overlay position from one open state
to another, or to close it, but not to open it.

BUG=834864

Change-Id: I4fe222edfcf030cfb086ddbb3ecec5071b5448f4
Reviewed-on: https://chromium-review.googlesource.com/1045178Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Commit-Queue: Donn Denman <donnd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578189}
parent 2badd9d4
......@@ -220,6 +220,7 @@ android_library("chrome_java") {
"//components/autofill/android:autofill_java",
"//components/background_task_scheduler:background_task_scheduler_java",
"//components/bookmarks/common/android:bookmarks_java",
"//components/contextual_search:mojo_bindings_java",
"//components/crash/android:java",
"//components/dom_distiller/content/browser/android:dom_distiller_content_java",
"//components/dom_distiller/core/android:dom_distiller_core_java",
......
......@@ -501,6 +501,16 @@ public class ContextualSearchPanel extends OverlayPanel {
mPanelMetrics.onPanelNavigatedToPrefetchedSearch(didResolve);
}
/**
* Maximizes the Contextual Search Panel.
* @param reason The {@code StateChangeReason} behind the maximization.
*/
@Override
public void maximizePanel(@StateChangeReason int reason) {
mShouldPromoteToTabAfterMaximizing = false;
maximizePanel(reason);
}
/**
* Maximizes the Contextual Search Panel, then promotes it to a regular Tab.
* @param reason The {@code StateChangeReason} behind the maximization and promotion to tab.
......@@ -540,6 +550,11 @@ public class ContextualSearchPanel extends OverlayPanel {
mHasContentBeenTouched = false;
}
@Override
public void expandPanel(@StateChangeReason int reason) {
super.expandPanel(reason);
}
@Override
public PanelState getPanelState() {
// NOTE(pedrosimonetti): exposing superclass method to the interface.
......
......@@ -12,6 +12,7 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import org.chromium.base.Log;
import org.chromium.base.ObserverList;
import org.chromium.base.SysUtils;
import org.chromium.base.VisibleForTesting;
......@@ -56,6 +57,7 @@ import org.chromium.content_public.browser.SelectionClient;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.BrowserControlsState;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.contextual_search.mojom.OverlayPosition;
import org.chromium.net.NetworkChangeNotifier;
import java.net.MalformedURLException;
......@@ -72,6 +74,9 @@ public class ContextualSearchManager
// TODO(donnd): provide an inner class that implements some of these interfaces (like the
// ContextualSearchTranslateInterface) rather than having the manager itself implement the
// interface because that exposes all the public methods of that interface at the manager level.
private static final String TAG = "ContextualSearch";
private static final String INTENT_URL_PREFIX = "intent:";
// The animation duration of a URL being promoted to a tab when triggered by an
......@@ -831,6 +836,40 @@ public class ContextualSearchManager
mPolicy.updateCountersForQuickAnswer(mWasActivatedByTap, doesAnswer);
}
/**
* Called by JavaScript in the Overlay to change the position of the overlay.
* The panel cannot be changed to any opened position if it's not already opened.
* @param desiredPosition The desired position of the Overlay Panel expressed as an
* OverlayPosition int (defined in contextual_search_js_api_service.mojom).
*/
@CalledByNative
private void onChangeOverlayPosition(int desiredPosition) {
assert desiredPosition >= OverlayPosition.CLOSE
&& desiredPosition <= OverlayPosition.MAXIMIZE;
// Ignore requests when the panel is not already open to prevent spam or abuse of the API.
if (!mSearchPanel.isShowing() || desiredPosition < OverlayPosition.CLOSE
|| desiredPosition > OverlayPosition.MAXIMIZE) {
Log.w(TAG, "Unexpected request to set Overlay position to " + desiredPosition);
return;
}
// Set the position.
switch (desiredPosition) {
case OverlayPosition.CLOSE:
mSearchPanel.closePanel(StateChangeReason.UNKNOWN, true);
break;
case OverlayPosition.PEEK:
mSearchPanel.peekPanel(StateChangeReason.UNKNOWN);
break;
case OverlayPosition.EXPAND:
mSearchPanel.expandPanel(StateChangeReason.UNKNOWN);
break;
case OverlayPosition.MAXIMIZE:
mSearchPanel.maximizePanel(StateChangeReason.UNKNOWN);
break;
}
}
/**
* Notifies that the Accessibility Mode state has changed.
*
......
......@@ -230,7 +230,7 @@ void ContextualSearchManager::ShouldEnableJsApi(
std::move(callback).Run(should_enable);
}
void ContextualSearchManager::SetCaption(std::string caption,
void ContextualSearchManager::SetCaption(const std::string& caption,
bool does_answer) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jstring> j_caption =
......@@ -239,6 +239,13 @@ void ContextualSearchManager::SetCaption(std::string caption,
does_answer);
}
void ContextualSearchManager::ChangeOverlayPosition(
contextual_search::mojom::OverlayPosition desired_position) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_ContextualSearchManager_onChangeOverlayPosition(
env, java_manager_, static_cast<int>(desired_position));
}
jlong JNI_ContextualSearchManager_Init(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
ContextualSearchManager* manager = new ContextualSearchManager(env, obj);
......
......@@ -12,6 +12,7 @@
#include "chrome/browser/android/contextualsearch/contextual_search_context.h"
#include "chrome/browser/android/contextualsearch/contextual_search_delegate.h"
#include "components/contextual_search/browser/contextual_search_js_api_handler.h"
#include "components/contextual_search/common/contextual_search_js_api_service.mojom.h"
// Manages the native extraction and request logic for Contextual Search,
// and interacts with the Java ContextualSearchManager for UX.
......@@ -82,7 +83,9 @@ class ContextualSearchManager
const base::android::JavaParamRef<jobject>& j_web_contents);
// ContextualSearchJsApiHandler overrides:
void SetCaption(std::string caption, bool does_answer) override;
void SetCaption(const std::string& caption, bool does_answer) override;
void ChangeOverlayPosition(
contextual_search::mojom::OverlayPosition desired_position) override;
// Determines whether the JS API should be enabled for the given URL.
// Calls the given |callback| with the answer: whether the API should be
......
The Contextual Search component provides renderer support for an overlay panel
with an API that peeks up from the bottom of the host page.
This component communicates via mojo from the renderer to the browser and vice
versa.
Eventually we may reuse some of this code for other overlay panels, including
DOM Distiller (which is currently in a separate component).
# Contextual Search Component
The Contextual Search component implements some platform agnostic services that
mediate between the main CS implementation in the Browser and some other parts
of the system. These include the JS API service to allow communication from
the Overlay to CS, and user-interaction aggregation.
## JS API Service
The JS API Service allows JavaScript in the Overlay Panel to call into
Contextual Search. This is done by providing an entry point at
chrome.contextualSearch when the Contextual Search Overlay Panel is active.
Enabling this API is somewhat complicated in order to make sure that the API
only exists for a Renderer created by CS for the Overlay:
1. Whenever a Renderer is created, an OverlayJsRenderFrameObserver is created.
This class is at the heart of the connection between the Renderer, CS, JS
and the JavaScript API.
2. An OverlayJsRenderFrameObserver is created for every renderer to check if
it's a CS Overlay Panel. When the renderer starts up it asks Contextual
Search if the current URL belongs to its overlay panel, and if it does then
the JS API is enabled for that renderer to allow it to accept JS messages
from the page.
3. When the OverlayJsRenderFrameObserver gets the response in #2 it creates a
ContextualSearchWrapper object that allows injecting JavaScript into the
WebFrame.
4. When some JS in a page wants to call into Chrome it simply accesses methods
in the chrome.contextualSearch API. If the page is being presented in the
Overlay Panel for CS then that object will exist with native method
implementations. The renderer forwards the API request to the browser for
processing.
The above interaction is used by the Translate onebox to allow showing a
translation in the caption of the Contextual Search Bar, and to provide control
of the position of the Overlay.
## User Interaction Aggregation
The CtrAggregator and WeeklyActivityStorage classes are used aggregate user
actions on a weekly basis for use in machine learning for improved triggering.
......@@ -22,8 +22,8 @@ class ContextualSearchJsApiHandler {
// Enabling API, determines if the JS API should be enabled for the given URL.
virtual void ShouldEnableJsApi(
const GURL& gurl,
contextual_search::mojom::ContextualSearchJsApiService::
ShouldEnableJsApiCallback callback) = 0;
mojom::ContextualSearchJsApiService::ShouldEnableJsApiCallback
callback) = 0;
//=======
// JS API
......@@ -32,7 +32,12 @@ class ContextualSearchJsApiHandler {
// Set the caption in the Contextual Search Bar, and indicate whether
// the caption provides an answer (such as an actual definition), rather than
// just general notification of what kind of answer may be available.
virtual void SetCaption(std::string caption, bool does_answer) = 0;
virtual void SetCaption(const std::string& caption, bool does_answer) = 0;
// Changes the Overlay position to the desired position.
// The panel cannot be set to any opened position if it's not already opened.
virtual void ChangeOverlayPosition(
mojom::OverlayPosition desired_position) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(ContextualSearchJsApiHandler);
......
......@@ -20,8 +20,7 @@ ContextualSearchJsApiServiceImpl::~ContextualSearchJsApiServiceImpl() {}
void ContextualSearchJsApiServiceImpl::ShouldEnableJsApi(
const GURL& gurl,
contextual_search::mojom::ContextualSearchJsApiService::
ShouldEnableJsApiCallback callback) {
mojom::ContextualSearchJsApiService::ShouldEnableJsApiCallback callback) {
contextual_search_js_api_handler_->ShouldEnableJsApi(gurl,
std::move(callback));
}
......@@ -32,6 +31,11 @@ void ContextualSearchJsApiServiceImpl::HandleSetCaption(
contextual_search_js_api_handler_->SetCaption(caption, does_answer);
}
void ContextualSearchJsApiServiceImpl::HandleChangeOverlayPosition(
mojom::OverlayPosition desired_position) {
contextual_search_js_api_handler_->ChangeOverlayPosition(desired_position);
}
// static
void CreateContextualSearchJsApiService(
ContextualSearchJsApiHandler* contextual_search_js_api_handler,
......
......@@ -12,6 +12,7 @@
namespace contextual_search {
// This is the receiving end of Contextual Search JavaScript API calls.
// TODO(donnd): Move this to java. See https://crbug.com/866972.
class ContextualSearchJsApiServiceImpl
: public mojom::ContextualSearchJsApiService {
public:
......@@ -24,13 +25,19 @@ class ContextualSearchJsApiServiceImpl
// The given |callback| will be notified with the answer.
void ShouldEnableJsApi(
const GURL& gurl,
contextual_search::mojom::ContextualSearchJsApiService::
ShouldEnableJsApiCallback callback) override;
mojom::ContextualSearchJsApiService::ShouldEnableJsApiCallback callback)
override;
// Handles a JavaScript call to set the caption in the Bar to
// the given |message|.
void HandleSetCaption(const std::string& message, bool does_answer) override;
// Handles a JavaScript call to change the Overlay position.
// The panel cannot be changed to any opened position if it's not already
// opened.
void HandleChangeOverlayPosition(
mojom::OverlayPosition desired_position) override;
private:
// The UI handler for calls through the JavaScript API.
ContextualSearchJsApiHandler* contextual_search_js_api_handler_;
......
......@@ -6,16 +6,39 @@ module contextual_search.mojom;
import "url/mojom/url.mojom";
// Possible positions for the Overlay Panel.
enum OverlayPosition {
// Not visible.
kClose,
// Visible only as a Bar peeking up from the bottom.
kPeek,
// Content is partially visible and partially offscreen.
kExpand,
// Content is fully visible.
kMaximize,
};
// This service is implemented by the browser process and is used by the
// renderer when a Contextual Search JavaScript API function is called.
// When a renderer wants to know if the CS JS API should be enabled it calls the
// first method. All the other methods are available to JavaScript at
// chrome.contextualSearch only when the first method returns true, indicating
// that the panel belongs to Contextual Search. More details are in the
// README.md file in this component.
interface ContextualSearchJsApiService {
// Determines if this JavaScript API should be enabled for the given URL.
// The asynchronous callback will be notified with the answer.
// TODO(donnd): Consider changing the messaging direction to be from browser
// to renderer. See https://crbug.com/866976.
ShouldEnableJsApi(url.mojom.Url url) => (bool should_enable);
// Handle a call from the JS API to set the caption, and indicate whether
// the caption provides an answer (such as an actual definition), rather than
// just general notification of what kind of answer may be available.
HandleSetCaption(string message, bool does_answer);
// Called by JavaScript to change the Overlay position. The panel cannot be
// changed to any opened position if it's not already opened.
HandleChangeOverlayPosition(OverlayPosition desired_position);
};
......@@ -19,6 +19,7 @@ namespace {
static const char kContextualSearchObjectName[] = "contextualSearch";
static const char kSetCaptionMethodName[] = "setCaption";
static const char kChangeOverlayPositionMethodName[] = "changeOverlayPosition";
} // namespace
......@@ -66,7 +67,9 @@ gin::ObjectTemplateBuilder ContextualSearchWrapper::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::Wrappable<ContextualSearchWrapper>::GetObjectTemplateBuilder(
isolate)
.SetMethod(kSetCaptionMethodName, &ContextualSearchWrapper::SetCaption);
.SetMethod(kSetCaptionMethodName, &ContextualSearchWrapper::SetCaption)
.SetMethod(kChangeOverlayPositionMethodName,
&ContextualSearchWrapper::ChangeOverlayPosition);
}
bool ContextualSearchWrapper::EnsureServiceConnected() {
......@@ -88,4 +91,12 @@ void ContextualSearchWrapper::SetCaption(const std::string& caption,
}
}
void ContextualSearchWrapper::ChangeOverlayPosition(
unsigned int desired_position) {
if (EnsureServiceConnected()) {
contextual_search_js_api_service_->HandleChangeOverlayPosition(
static_cast<mojom::OverlayPosition>(desired_position));
}
}
} // namespace contextual_search
......@@ -46,6 +46,11 @@ class ContextualSearchWrapper : public gin::Wrappable<ContextualSearchWrapper>,
// just general notification of what kind of answer may be available.
void SetCaption(const std::string& caption, bool does_answer);
// Called by JavaScript to change the Overlay position.
// The panel cannot be changed to any opened position if it's not already
// opened.
void ChangeOverlayPosition(unsigned int desired_position);
// Helper function to ensure that this class has connected to the API service.
// Returns false if cannot connect.
bool EnsureServiceConnected();
......
......@@ -45,7 +45,7 @@ class OverlayJsRenderFrameObserver : public content::RenderFrameObserver {
mojom::ContextualSearchJsApiServicePtr contextual_search_js_api_service_;
// Remembers whether we did start enabling the JS API by making a request
// to the Contextaual Search service to ask if we should enable for this
// to the Contextual Search service to ask if we should enable for this
// URL or not.
bool did_start_enabling_js_api_ = false;
......
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