Commit bfbca528 authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

[Autofill Assistant] Implement trigger script ownership and start.

This puts into place the intended ownership and starting procedure for
trigger scripts:
- The owner is client_android
- Communication with Java is done with a dedicated bridge

Most of the relevant java <-> native methods are not yet plumbed, this
is done in a downstream CL. Integration tests will be able to provide
coverage for the new code, also further downstream.

Bug: b/171776026
Change-Id: I175fdd1896449575b0a5d944cb2207c588807335
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2524524
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825928}
parent a71eaca1
......@@ -160,6 +160,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayImage.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java",
......@@ -220,6 +221,7 @@ generate_jni("jni_headers") {
"java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateTime.java",
......
......@@ -17,6 +17,7 @@ import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.autofill_assistant.trigger_scripts.AssistantTriggerScriptBridge;
import org.chromium.chrome.browser.signin.IdentityServicesProvider;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.components.signin.AccessTokenData;
......@@ -35,7 +36,7 @@ import java.util.Map;
* This mainly a bridge to autofill_assistant::ClientAndroid.
*/
@JNINamespace("autofill_assistant")
class AutofillAssistantClient {
public class AutofillAssistantClient {
/** OAuth2 scope that RPCs require. */
private static final String AUTH_TOKEN_TYPE =
"oauth2:https://www.googleapis.com/auth/userinfo.profile";
......@@ -113,8 +114,8 @@ class AutofillAssistantClient {
checkNativeClientIsAliveOrThrow();
chooseAccountAsyncIfNecessary(userName);
return AutofillAssistantClientJni.get().start(mNativeClientAndroid,
AutofillAssistantClient.this, initialUrl, experimentIds, callerAccount,
return AutofillAssistantClientJni.get().start(mNativeClientAndroid, this, initialUrl,
experimentIds, callerAccount,
parameters.keySet().toArray(new String[parameters.size()]),
parameters.values().toArray(new String[parameters.size()]), isChromeCustomTab,
onboardingCoordinator,
......@@ -123,6 +124,18 @@ class AutofillAssistantClient {
AutofillAssistantServiceInjector.getServiceToInject());
}
public void startTriggerScript(AssistantTriggerScriptBridge delegate, String initialUrl,
Map<String, String> parameters, String experimentIds) {
if (mNativeClientAndroid == 0) {
return;
}
checkNativeClientIsAliveOrThrow();
AutofillAssistantClientJni.get().startTriggerScript(mNativeClientAndroid, this, delegate,
initialUrl, experimentIds,
parameters.keySet().toArray(new String[parameters.size()]),
parameters.values().toArray(new String[parameters.size()]));
}
/**
* Gets rid of the UI, if there is one. Leaves Autofill Assistant running.
*/
......@@ -393,6 +406,9 @@ class AutofillAssistantClient {
String[] parameterValues, boolean isChromeCustomTab,
@Nullable AssistantOnboardingCoordinator onboardingCoordinator,
boolean onboardingShown, long nativeService);
void startTriggerScript(long nativeClientAndroid, AutofillAssistantClient caller,
AssistantTriggerScriptBridge delegate, String initialUrl, String experimentIds,
String[] parameterNames, String[] parameterValues);
void onAccessToken(long nativeClientAndroid, AutofillAssistantClient caller,
boolean success, String accessToken);
String getPrimaryAccountName(long nativeClientAndroid, AutofillAssistantClient caller);
......
// 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.
package org.chromium.chrome.browser.autofill_assistant.trigger_scripts;
import android.content.Context;
import androidx.annotation.NonNull;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantClient;
import org.chromium.chrome.browser.autofill_assistant.metrics.LiteScriptFinishedState;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.content_public.browser.WebContents;
import java.util.Map;
/**
* Communicates with the native {@code TriggerScriptBridgeAndroid} to show and hide trigger
* scripts.
*/
@JNINamespace("autofill_assistant")
public class AssistantTriggerScriptBridge {
private AssistantTriggerScript mTriggerScript;
private long mNativeBridge;
private Delegate mDelegate;
/** Interface for delegates of the {@code start} method. */
public interface Delegate {
void onTriggerScriptFinished(@LiteScriptFinishedState int finishedState);
}
public AssistantTriggerScriptBridge() {}
/**
* Starts the trigger script for {@code initialUrl} and reports the finished state to {@code
* delegate}.
*/
public void start(BottomSheetController bottomSheetController, Context context,
@NonNull WebContents webContents, @NonNull String initialUrl,
Map<String, String> scriptParameters, String experimentIds, Delegate delegate) {
mDelegate = delegate;
mTriggerScript = new AssistantTriggerScript(context, new AssistantTriggerScript.Delegate() {
@Override
public void onTriggerScriptAction(int action) {
safeNativeOnTriggerScriptAction(action);
}
@Override
public void onBottomSheetClosedWithSwipe() {
safeNativeOnBottomSheetClosedWithSwipe();
}
@Override
public void onBackButtonPressed() {
safeNativeOnBackButtonPressed();
}
@Override
public void onFeedbackButtonClicked() {
safeNativeOnFeedbackButtonClicked();
}
}, bottomSheetController);
// Request the client to start the trigger script. Native will then bind itself to this java
// instance via setNativePtr.
AutofillAssistantClient.fromWebContents(webContents)
.startTriggerScript(this, initialUrl, scriptParameters, experimentIds);
}
@CalledByNative
private void showTriggerScript() {
// TODO update
mTriggerScript.show();
}
@CalledByNative
private void hideTriggerScript() {
mTriggerScript.hide();
}
@CalledByNative
private void onTriggerScriptFinished(@LiteScriptFinishedState int state) {
mDelegate.onTriggerScriptFinished(state);
}
@CalledByNative
private void setNativePtr(long nativePtr) {
mNativeBridge = nativePtr;
}
@CalledByNative
private void clearNativePtr() {
mNativeBridge = 0;
mTriggerScript.destroy();
}
private void safeNativeOnTriggerScriptAction(int action) {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onTriggerScriptAction(
mNativeBridge, AssistantTriggerScriptBridge.this, action);
}
}
private void safeNativeOnBottomSheetClosedWithSwipe() {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onBottomSheetClosedWithSwipe(
mNativeBridge, AssistantTriggerScriptBridge.this);
}
}
private void safeNativeOnBackButtonPressed() {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onBackButtonPressed(
mNativeBridge, AssistantTriggerScriptBridge.this);
}
}
private void safeNativeOnFeedbackButtonClicked() {
if (mNativeBridge != 0) {
AssistantTriggerScriptBridgeJni.get().onFeedbackButtonClicked(
mNativeBridge, AssistantTriggerScriptBridge.this);
}
}
@NativeMethods
interface Natives {
void onTriggerScriptAction(long nativeTriggerScriptBridgeAndroid,
AssistantTriggerScriptBridge caller, int action);
void onBottomSheetClosedWithSwipe(
long nativeTriggerScriptBridgeAndroid, AssistantTriggerScriptBridge caller);
void onBackButtonPressed(
long nativeTriggerScriptBridgeAndroid, AssistantTriggerScriptBridge caller);
void onFeedbackButtonClicked(
long nativeTriggerScriptBridgeAndroid, AssistantTriggerScriptBridge caller);
}
}
......@@ -2436,6 +2436,8 @@ static_library("browser") {
"android/autofill_assistant/onboarding_coordinator_bridge.h",
"android/autofill_assistant/onboarding_fetcher_factory.cc",
"android/autofill_assistant/onboarding_fetcher_factory.h",
"android/autofill_assistant/trigger_script_bridge_android.cc",
"android/autofill_assistant/trigger_script_bridge_android.h",
"android/autofill_assistant/ui_controller_android.cc",
"android/autofill_assistant/ui_controller_android.h",
"android/autofill_assistant/ui_controller_android_utils.cc",
......
......@@ -171,6 +171,21 @@ bool ClientAndroid::Start(JNIEnv* env,
return controller_->Start(initial_url, std::move(trigger_context));
}
void ClientAndroid::StartTriggerScript(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jdelegate,
const base::android::JavaParamRef<jstring>& jinitial_url,
const base::android::JavaParamRef<jstring>& jexperiment_ids,
const base::android::JavaParamRef<jobjectArray>& jparameter_names,
const base::android::JavaParamRef<jobjectArray>& jparameter_values) {
trigger_script_bridge_.StartTriggerScript(
this, jdelegate,
GURL(base::android::ConvertJavaStringToUTF8(env, jinitial_url)),
CreateTriggerContext(env, jexperiment_ids, jparameter_names,
jparameter_values));
}
void ClientAndroid::OnJavaDestroyUI(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
......
......@@ -12,6 +12,7 @@
#include "base/android/scoped_java_ref.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h"
#include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
#include "components/autofill_assistant/browser/client.h"
#include "components/autofill_assistant/browser/controller.h"
......@@ -63,6 +64,15 @@ class ClientAndroid : public Client,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jother_web_contents);
void StartTriggerScript(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jdelegate,
const base::android::JavaParamRef<jstring>& jinitial_url,
const base::android::JavaParamRef<jstring>& jexperiment_ids,
const base::android::JavaParamRef<jobjectArray>& jparameter_names,
const base::android::JavaParamRef<jobjectArray>& jparameter_values);
base::android::ScopedJavaLocalRef<jstring> GetPrimaryAccountName(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
......@@ -160,6 +170,9 @@ class ClientAndroid : public Client,
std::unique_ptr<UiControllerAndroid> ui_controller_android_;
// Bridge that allows Java to start trigger scripts.
TriggerScriptBridgeAndroid trigger_script_bridge_;
base::OnceCallback<void(bool, const std::string&)>
fetch_access_token_callback_;
......
// 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.
#include "chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantTriggerScriptBridge_jni.h"
#include "chrome/common/channel_info.h"
#include "components/autofill_assistant/browser/service/api_key_fetcher.h"
#include "components/autofill_assistant/browser/service/server_url_fetcher.h"
#include "components/autofill_assistant/browser/service/service_request_sender.h"
#include "components/autofill_assistant/browser/service/simple_url_loader_factory.h"
#include "components/autofill_assistant/browser/trigger_scripts/dynamic_trigger_conditions.h"
#include "components/autofill_assistant/browser/trigger_scripts/static_trigger_conditions.h"
#include "components/autofill_assistant/browser/web/web_controller.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
using base::android::AttachCurrentThread;
namespace autofill_assistant {
TriggerScriptBridgeAndroid::TriggerScriptBridgeAndroid() = default;
TriggerScriptBridgeAndroid::~TriggerScriptBridgeAndroid() = default;
void TriggerScriptBridgeAndroid::StartTriggerScript(
Client* client,
const base::android::JavaParamRef<jobject>& jdelegate,
const GURL& initial_url,
std::unique_ptr<TriggerContext> trigger_context) {
DCHECK(!java_object_);
java_object_ = base::android::ScopedJavaGlobalRef<jobject>(jdelegate);
Java_AssistantTriggerScriptBridge_setNativePtr(
AttachCurrentThread(), java_object_, reinterpret_cast<intptr_t>(this));
ServerUrlFetcher url_fetcher{ServerUrlFetcher::GetDefaultServerUrl()};
trigger_script_coordinator_ = std::make_unique<TriggerScriptCoordinator>(
client,
WebController::CreateForWebContents(client->GetWebContents(),
&client_settings_),
std::make_unique<ServiceRequestSender>(
client->GetWebContents()->GetBrowserContext(),
/* access_token_fetcher = */ nullptr,
std::make_unique<NativeURLLoaderFactory>(),
ApiKeyFetcher().GetAPIKey(chrome::GetChannel()),
/* auth_enabled = */ false,
/* disable_auth_if_no_access_token = */ true),
url_fetcher.GetTriggerScriptsEndpoint(),
std::make_unique<StaticTriggerConditions>(),
std::make_unique<DynamicTriggerConditions>(), ukm::UkmRecorder::Get());
trigger_script_coordinator_->AddObserver(this);
trigger_script_coordinator_->Start(initial_url, std::move(trigger_context));
}
void TriggerScriptBridgeAndroid::StopTriggerScript() {
if (java_object_) {
Java_AssistantTriggerScriptBridge_clearNativePtr(AttachCurrentThread(),
java_object_);
java_object_ = nullptr;
}
trigger_script_coordinator_ = nullptr;
}
void TriggerScriptBridgeAndroid::OnTriggerScriptAction(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint action) {
if (!trigger_script_coordinator_) {
return;
}
}
void TriggerScriptBridgeAndroid::OnBottomSheetClosedWithSwipe(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
if (!trigger_script_coordinator_) {
return;
}
}
void TriggerScriptBridgeAndroid::OnBackButtonPressed(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
if (!trigger_script_coordinator_) {
return;
}
}
void TriggerScriptBridgeAndroid::OnFeedbackButtonClicked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
if (!trigger_script_coordinator_) {
return;
}
}
void TriggerScriptBridgeAndroid::OnTriggerScriptShown(
const TriggerScriptUIProto& proto) {
if (!java_object_) {
return;
}
Java_AssistantTriggerScriptBridge_showTriggerScript(AttachCurrentThread(),
java_object_);
}
void TriggerScriptBridgeAndroid::OnTriggerScriptHidden() {
if (!java_object_) {
return;
}
Java_AssistantTriggerScriptBridge_hideTriggerScript(AttachCurrentThread(),
java_object_);
}
void TriggerScriptBridgeAndroid::OnTriggerScriptFinished(
Metrics::LiteScriptFinishedState state) {
if (!java_object_) {
return;
}
Java_AssistantTriggerScriptBridge_onTriggerScriptFinished(
AttachCurrentThread(), java_object_, static_cast<int>(state));
}
} // namespace autofill_assistant
// 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.
#ifndef CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_TRIGGER_SCRIPT_BRIDGE_ANDROID_H_
#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_TRIGGER_SCRIPT_BRIDGE_ANDROID_H_
#include <map>
#include <memory>
#include <string>
#include "base/android/jni_android.h"
#include "components/autofill_assistant/browser/client.h"
#include "components/autofill_assistant/browser/metrics.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "components/autofill_assistant/browser/trigger_context.h"
#include "components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h"
#include "url/gurl.h"
namespace autofill_assistant {
// Facilitates communication between trigger script UI and native coordinator.
class TriggerScriptBridgeAndroid : public TriggerScriptCoordinator::Observer {
public:
TriggerScriptBridgeAndroid();
~TriggerScriptBridgeAndroid() override;
TriggerScriptBridgeAndroid(const TriggerScriptBridgeAndroid&) = delete;
TriggerScriptBridgeAndroid& operator=(const TriggerScriptBridgeAndroid&) =
delete;
// Attempts to start a trigger script on |initial_url|. Will communicate with
// |jdelegate| to show/hide UI as necessary.
void StartTriggerScript(Client* client,
const base::android::JavaParamRef<jobject>& jdelegate,
const GURL& initial_url,
std::unique_ptr<TriggerContext> trigger_context);
// Stops and destroys the current trigger script, if any. Also disconnects the
// java-side delegate.
void StopTriggerScript();
// Called by the UI to execute a specific trigger script action.
void OnTriggerScriptAction(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint action);
// Called by the UI when the bottom sheet has been swipe-dismissed.
void OnBottomSheetClosedWithSwipe(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Called by the UI when the back button was pressed.
void OnBackButtonPressed(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Called by the UI when the feedback form was requested.
void OnFeedbackButtonClicked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
private:
// From TriggerScriptCoordinator::Observer:
void OnTriggerScriptShown(const TriggerScriptUIProto& proto) override;
void OnTriggerScriptHidden() override;
void OnTriggerScriptFinished(Metrics::LiteScriptFinishedState state) override;
// Reference to the Java counterpart to this class.
base::android::ScopedJavaGlobalRef<jobject> java_object_;
ClientSettings client_settings_;
std::unique_ptr<TriggerScriptCoordinator> trigger_script_coordinator_;
};
} // namespace autofill_assistant
#endif // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_TRIGGER_SCRIPT_BRIDGE_ANDROID_H_
......@@ -15,6 +15,7 @@ const char kDefaultAutofillAssistantServerUrl[] =
"https://automate-pa.googleapis.com";
const char kScriptEndpoint[] = "/v1/supportsSite2";
const char kActionEndpoint[] = "/v1/actions2";
const char kTriggersEndpoint[] = "/v1/triggers";
} // namespace
namespace autofill_assistant {
......@@ -46,4 +47,10 @@ GURL ServerUrlFetcher::GetNextActionsEndpoint() {
return server_url_.ReplaceComponents(action_replacements);
}
GURL ServerUrlFetcher::GetTriggerScriptsEndpoint() {
url::StringPieceReplacements<std::string> trigger_replacements;
trigger_replacements.SetPathStr(kTriggersEndpoint);
return server_url_.ReplaceComponents(trigger_replacements);
}
} // namespace autofill_assistant
......@@ -22,6 +22,8 @@ class ServerUrlFetcher {
virtual GURL GetSupportsScriptEndpoint();
// Returns the endpoint to send the GetNextActions RPC to.
virtual GURL GetNextActionsEndpoint();
// Returns the endpoint to send the GetTriggerScripts RPC to.
virtual GURL GetTriggerScriptsEndpoint();
private:
GURL server_url_;
......
......@@ -39,5 +39,11 @@ TEST(ServerUrlFetcherTest, GetActionsEndpoint) {
Eq(GURL("https://www.example.com/v1/actions2")));
}
TEST(ServerUrlFetcherTest, GetTriggerScriptsEndpoint) {
EXPECT_THAT(ServerUrlFetcher(GURL("https://www.example.com"))
.GetTriggerScriptsEndpoint(),
Eq(GURL("https://www.example.com/v1/triggers")));
}
} // namespace
} // namespace autofill_assistant
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