Commit 68c021ec authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

[ash] app_list answer cards => Content Service

Changes the app_list UI to use Content Service client library to display
answer card web contents, rather than using some one-off IPCs to drive
remote WebContents behavior with a special WebContentsDelegate/Observer
in chrome/browser code. This allows quite a bit of code to be deleted
from src/chrome while retaining all the existing functionality of answer
cards in the app_list search UI.

Prior to this change, AnswerCardSearchProvider would query the potential
card URL (i.e. google.com/coac?q=whatever) and only send a corresponding
search result back to Ash if the response was a valid card response.
Instead, now all queries are treated as potentially valid search results,
and AnswerCardSearchProvider is little more than a translator which turns
a search query string into a useful answer card query URL.

Ash-side code then navigates its own NavigableContents to any potential
card URL it receives, inspecting response headers to determine whether
or not the contents are actually a card. If they are, it displays them
in the UI.

The key results of this CL are:

* Most relevant app_list answer card support logic lives in Ash now. A
future change could probably move the rest of it, but that requires a
more fundamental reconsideration of the browser's role (or non-role) in
supporting app_list search behavior.

* All of the Ash/Chrome code to support answer cards in the app_list
UI is unified for in- and out-of-process Ash cases.

* We have a working Content Service client being used in production
Chrome OS code, which establishes precedent for improving several other
features that embed WebContents within Chrome OS system UI.

Bug: 854367
Change-Id: Id913460d7a4bd4558d385b9b3f9b52845aa0f0e7
Reviewed-on: https://chromium-review.googlesource.com/c/1269622
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarEric Roman <eroman@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605166}
parent 083aa122
......@@ -110,6 +110,7 @@ component("app_list") {
"//components/keyed_service/core",
"//components/sync",
"//mojo/public/cpp/bindings",
"//services/content/public/cpp",
"//services/ws/public/cpp",
"//services/ws/public/mojom",
"//services/ws/remote_view_host",
......@@ -137,11 +138,14 @@ component("app_list") {
public_deps = [
"//ash/app_list/model:app_list_model",
"//ash/app_list/model:search_model",
"//ash/public/cpp:cpp",
"//ash/public/cpp",
"//services/content/public/mojom",
]
}
static_library("test_support") {
testonly = true
sources = [
"test/app_list_test_model.cc",
"test/app_list_test_model.h",
......@@ -156,6 +160,7 @@ static_library("test_support") {
deps = [
":app_list",
"//base",
"//services/content/public/cpp/test:test_support",
"//ui/base:base",
"//ui/events",
"//ui/gfx",
......@@ -215,6 +220,10 @@ test("app_list_unittests") {
"//base",
"//base/test:test_support",
"//mojo/core/embedder",
"//mojo/public/cpp/bindings",
"//services/content/public/cpp",
"//services/content/public/cpp/test:test_support",
"//services/content/public/mojom",
"//skia",
"//testing/gtest",
"//ui/accessibility",
......
......@@ -3,6 +3,7 @@ include_rules = [
"+components/keyed_service/core",
"+components/sync",
"+mojo/public/cpp",
"+net/http/http_response_headers.h",
"+services/ws/public",
"+skia",
"+third_party/google_toolbox_for_mac/src",
......
......@@ -40,15 +40,18 @@
namespace ash {
AppListControllerImpl::AppListControllerImpl(ws::WindowService* window_service)
: window_service_(window_service),
presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)),
AppListControllerImpl::AppListControllerImpl()
: presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)),
is_home_launcher_enabled_(app_list_features::IsHomeLauncherEnabled()),
voice_interaction_binding_(this) {
model_.AddObserver(this);
// Create only for non-mash. Mash uses window tree embed API to get a
// token to map answer card contents.
//
// TODO(https://crbug.com/894987): This is now only used (as a singleton) by
// assistant UI code to display its answer card contents. It can be removed
// once that code is ported to use Content Service.
if (!::features::IsUsingWindowService()) {
answer_card_contents_registry_ =
std::make_unique<app_list::AnswerCardContentsRegistry>();
......@@ -831,8 +834,10 @@ bool AppListControllerImpl::CanProcessEventsOnApplistViews() {
HomeLauncherGestureHandler::Mode::kSlideUpToShow;
}
ws::WindowService* AppListControllerImpl::GetWindowService() {
return window_service_;
void AppListControllerImpl::GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) {
if (client_)
client_->GetNavigableContentsFactory(std::move(request));
}
void AppListControllerImpl::OnVisibilityChanged(bool visible) {
......
......@@ -37,10 +37,6 @@ namespace ui {
class MouseWheelEvent;
} // namespace ui
namespace ws {
class WindowService;
} // namespace ws
namespace ash {
class HomeLauncherGestureHandler;
......@@ -61,7 +57,7 @@ class ASH_EXPORT AppListControllerImpl
public:
using AppListItemMetadataPtr = mojom::AppListItemMetadataPtr;
using SearchResultMetadataPtr = mojom::SearchResultMetadataPtr;
explicit AppListControllerImpl(ws::WindowService* window_service);
AppListControllerImpl();
~AppListControllerImpl() override;
// Binds the mojom::AppListController interface request to this object.
......@@ -185,7 +181,8 @@ class ASH_EXPORT AppListControllerImpl
bool ProcessHomeLauncherGesture(ui::GestureEvent* event,
const gfx::Point& screen_location) override;
bool CanProcessEventsOnApplistViews() override;
ws::WindowService* GetWindowService() override;
void GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) override;
void OnVisibilityChanged(bool visible);
void OnTargetVisibilityChanged(bool visible);
......@@ -248,8 +245,6 @@ class ASH_EXPORT AppListControllerImpl
int64_t GetDisplayIdToShowAppListOn();
ws::WindowService* window_service_;
base::string16 last_raw_query_;
mojom::AppListClientPtr client_;
......@@ -264,7 +259,9 @@ class ASH_EXPORT AppListControllerImpl
// Bindings for the AppListController interface.
mojo::BindingSet<mojom::AppListController> bindings_;
// Token to view map for classic/mus ash (i.e. non-mash).
// TODO(https://crbug.com/894987): Remove this once assistant UI is converted
// to use Content Service, as there will then be no more consumers of
// AnswerCardContentsRegistry.
std::unique_ptr<app_list::AnswerCardContentsRegistry>
answer_card_contents_registry_;
......
......@@ -12,6 +12,7 @@
#include "ash/public/interfaces/menu.mojom.h"
#include "base/callback_forward.h"
#include "base/strings/string16.h"
#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ui_base_types.h"
#include "ui/events/event_constants.h"
......@@ -21,10 +22,6 @@ namespace ui {
class GestureEvent;
} // namespace ui
namespace ws {
class WindowService;
} // namespace ws
namespace app_list {
class AppListModel;
......@@ -127,7 +124,11 @@ class ASH_PUBLIC_EXPORT AppListViewDelegate {
// its descendants.
virtual bool CanProcessEventsOnApplistViews() = 0;
virtual ws::WindowService* GetWindowService() = 0;
// Acquires a factory interface from the client which can be used to acquire
// initialize new NavigableContents objects for embedding web contents into
// the app list UI.
virtual void GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) = 0;
};
} // namespace app_list
......
......@@ -62,12 +62,10 @@ void SearchModel::PublishResults(
// Add items back to |results_| in the order of |new_results|.
for (auto&& new_result : new_results) {
auto ui_result_it = results_map.find(new_result->id());
if (ui_result_it != results_map.end() &&
new_result->answer_card_contents_token() ==
ui_result_it->second->answer_card_contents_token()) {
if (ui_result_it != results_map.end()) {
// Update and use the old result if it exists.
std::unique_ptr<SearchResult> ui_result = std::move(ui_result_it->second);
ui_result->SetMetadata(new_result->CloneMetadata());
ui_result->SetMetadata(new_result->TakeMetadata());
results_->Add(std::move(ui_result));
// Remove the item from the map so that it ends up only with unused
......
......@@ -80,18 +80,8 @@ class APP_LIST_MODEL_EXPORT SearchResult {
}
void SetFormattedPrice(const base::string16& formatted_price);
const base::Optional<base::UnguessableToken>& answer_card_contents_token()
const {
return metadata_->answer_card_contents_token;
}
void set_answer_card_contents_token(
const base::Optional<base::UnguessableToken>& token) {
metadata_->answer_card_contents_token = token;
}
gfx::Size answer_card_size() const {
return metadata_->answer_card_size.value_or(gfx::Size());
}
const base::Optional<GURL>& query_url() const { return metadata_->query_url; }
void set_query_url(const GURL& url) { metadata_->query_url = url; }
const std::string& id() const { return metadata_->id; }
......@@ -138,8 +128,8 @@ class APP_LIST_MODEL_EXPORT SearchResult {
virtual void InvokeAction(int action_index, int event_flags);
void SetMetadata(ash::mojom::SearchResultMetadataPtr metadata);
ash::mojom::SearchResultMetadataPtr CloneMetadata() const {
return metadata_.Clone();
ash::mojom::SearchResultMetadataPtr TakeMetadata() {
return std::move(metadata_);
}
protected:
......
......@@ -98,8 +98,9 @@ bool AppListTestViewDelegate::CanProcessEventsOnApplistViews() {
return true;
}
ws::WindowService* AppListTestViewDelegate::GetWindowService() {
return nullptr;
void AppListTestViewDelegate::GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) {
fake_navigable_contents_factory_.BindRequest(std::move(request));
}
void AppListTestViewDelegate::GetSearchResultContextMenuModel(
......
......@@ -18,6 +18,7 @@
#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "services/content/public/cpp/test/fake_navigable_contents_factory.h"
#include "ui/base/models/simple_menu_model.h"
namespace app_list {
......@@ -45,6 +46,10 @@ class AppListTestViewDelegate : public AppListViewDelegate,
// SetProfileByPath() is called.
void set_next_profile_app_count(int apps) { next_profile_app_count_ = apps; }
content::FakeNavigableContentsFactory& fake_navigable_contents_factory() {
return fake_navigable_contents_factory_;
}
// Sets whether the search engine is Google or not.
void SetSearchEngineIsGoogle(bool is_google);
......@@ -80,7 +85,8 @@ class AppListTestViewDelegate : public AppListViewDelegate,
bool ProcessHomeLauncherGesture(ui::GestureEvent* event,
const gfx::Point& screen_location) override;
bool CanProcessEventsOnApplistViews() override;
ws::WindowService* GetWindowService() override;
void GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) override;
// Do a bulk replacement of the items in the model.
void ReplaceTestModel(int item_count);
......@@ -103,6 +109,7 @@ class AppListTestViewDelegate : public AppListViewDelegate,
std::unique_ptr<SearchModel> search_model_;
std::vector<SkColor> wallpaper_prominent_colors_;
ui::SimpleMenuModel search_result_context_menu_model_;
content::FakeNavigableContentsFactory fake_navigable_contents_factory_;
DISALLOW_COPY_AND_ASSIGN(AppListTestViewDelegate);
};
......
......@@ -45,14 +45,16 @@ class TestAppListClient : public mojom::AppListClient {
int event_flags) override {}
void OnAppListTargetVisibilityChanged(bool visible) override {}
void OnAppListVisibilityChanged(bool visible) override {}
void StartVoiceInteractionSession() override;
void ToggleVoiceInteractionSession() override;
void OnFolderCreated(mojom::AppListItemMetadataPtr item) override {}
void OnFolderDeleted(mojom::AppListItemMetadataPtr item) override {}
void OnItemUpdated(mojom::AppListItemMetadataPtr item) override {}
void OnPageBreakItemAdded(const std::string& id,
const syncer::StringOrdinal& position) override {}
void OnPageBreakItemDeleted(const std::string& id) override {}
void StartVoiceInteractionSession() override;
void ToggleVoiceInteractionSession() override;
void GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) override {}
size_t voice_session_count() const { return voice_session_count_; }
......
......@@ -37,7 +37,6 @@
#include "ash/app_list/views/suggestion_chip_view.h"
#include "ash/app_list/views/suggestions_container_view.h"
#include "ash/app_list/views/test/apps_grid_view_test_api.h"
#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/cpp/app_list/app_list_constants.h"
#include "ash/public/cpp/app_list/app_list_features.h"
......@@ -47,6 +46,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/icu_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "services/content/public/cpp/test/fake_navigable_contents.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/chromeos/search_box/search_box_constants.h"
......@@ -278,14 +278,11 @@ class AppListViewFocusTest : public views::ViewsTestBase,
}
views::ViewsTestBase::SetUp();
answer_card_contents_registry_ =
std::make_unique<AnswerCardContentsRegistry>();
fake_answer_card_view_ = std::make_unique<views::View>();
fake_answer_card_view_->set_owned_by_client();
fake_answer_card_token_ = answer_card_contents_registry_->Register(
fake_answer_card_view_.get(), /*contents_native_view=*/nullptr);
// Initialize app list view.
fake_card_contents_.set_default_response_headers(
SearchResultAnswerCardView::CreateAnswerCardResponseHeadersForTest(
"weather", "Unimportant Title"));
delegate_ = std::make_unique<AppListTestViewDelegate>();
view_ = new AppListView(delegate_.get());
AppListView::InitParams params;
......@@ -325,6 +322,10 @@ class AppListViewFocusTest : public views::ViewsTestBase,
// Disable animation timer.
view_->GetWidget()->GetLayer()->GetAnimator()->set_disable_timer_for_test(
true);
// The Update above will elicit a navigation. Wait for it.
delegate_->fake_navigable_contents_factory()
.WaitForAndBindNextContentsRequest(&fake_card_contents_);
}
void TearDown() override {
......@@ -379,8 +380,10 @@ class AppListViewFocusTest : public views::ViewsTestBase,
std::make_unique<TestSearchResult>();
result->set_display_type(data.first);
result->set_display_score(display_score);
if (data.first == ash::SearchResultDisplayType::kCard)
result->set_answer_card_contents_token(fake_answer_card_token_);
if (data.first == ash::SearchResultDisplayType::kCard) {
const GURL kFakeCardUrl = GURL("https://www.google.com/coac?q=fake");
result->set_query_url(kFakeCardUrl);
}
results->Add(std::move(result));
}
}
......@@ -592,13 +595,12 @@ class AppListViewFocusTest : public views::ViewsTestBase,
// Restores the locale to default when destructor is called.
base::test::ScopedRestoreICUDefaultLocale restore_locale_;
std::unique_ptr<AnswerCardContentsRegistry> answer_card_contents_registry_;
std::unique_ptr<views::View> fake_answer_card_view_;
base::UnguessableToken fake_answer_card_token_;
// Used by AppListFolderView::UpdatePreferredBounds.
keyboard::KeyboardController keyboard_controller_;
// A fake NavigableContents implementation to back card navigation requests.
content::FakeNavigableContents fake_card_contents_;
DISALLOW_COPY_AND_ASSIGN(AppListViewFocusTest);
};
......
......@@ -6,6 +6,9 @@
#define ASH_APP_LIST_VIEWS_SEARCH_RESULT_ANSWER_CARD_VIEW_H_
#include "ash/app_list/views/search_result_container_view.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string16.h"
#include "net/http/http_response_headers.h"
namespace app_list {
......@@ -30,6 +33,10 @@ class APP_LIST_EXPORT SearchResultAnswerCardView
views::View* GetSearchAnswerContainerViewForTest() const;
static scoped_refptr<net::HttpResponseHeaders>
CreateAnswerCardResponseHeadersForTest(const std::string& query,
const std::string& title);
private:
class SearchAnswerContainerView;
......@@ -37,6 +44,10 @@ class APP_LIST_EXPORT SearchResultAnswerCardView
// It's visible iff we have a search answer result.
SearchAnswerContainerView* const search_answer_container_view_;
// Tracks the last known card title so we can update the accessibility
// framework if the title changes while the card has focus.
base::string16 last_known_card_title_;
DISALLOW_COPY_AND_ASSIGN(SearchResultAnswerCardView);
};
......
......@@ -10,11 +10,12 @@
#include "ash/app_list/test/app_list_test_view_delegate.h"
#include "ash/app_list/test/test_search_result.h"
#include "ash/app_list/views/search_result_view.h"
#include "ash/public/cpp/app_list/answer_card_contents_registry.h"
#include "ash/public/cpp/app_list/app_list_constants.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/unguessable_token.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/content/public/cpp/test/fake_navigable_contents.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/views/background.h"
#include "ui/views/test/views_test_base.h"
......@@ -37,31 +38,33 @@ class SearchResultAnswerCardViewTest : public views::ViewsTestBase {
search_card_view_ = std::make_unique<views::View>();
fake_card_contents_.set_default_response_headers(
SearchResultAnswerCardView::CreateAnswerCardResponseHeadersForTest(
"weather", kResultTitle));
result_container_view_ = new SearchResultAnswerCardView(&view_delegate_);
search_card_view_->AddChildView(result_container_view_);
result_container_view_->SetResults(
view_delegate_.GetSearchModel()->results());
result_view_ = std::make_unique<views::View>();
result_view_->set_owned_by_client();
token_ = contents_registry_.Register(result_view_.get(),
/*contents_native_view=*/nullptr);
SetUpSearchResult();
}
protected:
void SetUpSearchResult() {
const GURL kFakeQueryUrl = GURL("https://www.google.com/coac?q=fake");
SearchModel::SearchResults* results = GetResults();
std::unique_ptr<TestSearchResult> result =
std::make_unique<TestSearchResult>();
result->set_display_type(ash::SearchResultDisplayType::kCard);
result->set_title(base::UTF8ToUTF16(kResultTitle));
result->set_answer_card_contents_token(token_);
result->set_display_score(kDisplayScore);
result->set_query_url(kFakeQueryUrl);
results->Add(std::move(result));
// Adding results will schedule Update().
view_delegate_.fake_navigable_contents_factory()
.WaitForAndBindNextContentsRequest(&fake_card_contents_);
RunPendingMessages();
}
......@@ -100,8 +103,6 @@ class SearchResultAnswerCardViewTest : public views::ViewsTestBase {
result_container_view_->child_at(0)->GetAccessibleNodeData(node_data);
}
views::View* result_view() const { return result_view_.get(); }
private:
AppListTestViewDelegate view_delegate_;
......@@ -111,25 +112,17 @@ class SearchResultAnswerCardViewTest : public views::ViewsTestBase {
// Result container that we are testing. It's a child of search_card_view_.
// Owned by the view hierarchy.
SearchResultAnswerCardView* result_container_view_;
// View sent within the search result. May be shown within
// result_container_view_. Has set_owned_by_client() called.
std::unique_ptr<views::View> result_view_;
AnswerCardContentsRegistry contents_registry_;
base::UnguessableToken token_;
// Fake NavigableContents implementation to back answer card navigations.
content::FakeNavigableContents fake_card_contents_;
DISALLOW_COPY_AND_ASSIGN(SearchResultAnswerCardViewTest);
};
TEST_F(SearchResultAnswerCardViewTest, Basic) {
EXPECT_EQ(kDisplayScore, GetContainerScore());
EXPECT_EQ(1, GetResultCountFromView());
// Result view should be added to the hierarchy.
EXPECT_EQ(search_card_view(), result_view()->parent()->parent()->parent());
ASSERT_TRUE(search_card_view()->visible());
EXPECT_EQ(1, GetYSize());
}
......@@ -149,7 +142,6 @@ TEST_F(SearchResultAnswerCardViewTest, SpokenFeedback) {
TEST_F(SearchResultAnswerCardViewTest, DeleteResult) {
DeleteResult();
EXPECT_EQ(0UL, GetResults()->item_count());
EXPECT_EQ(nullptr, result_view()->parent());
EXPECT_EQ(0, GetYSize());
ASSERT_FALSE(search_card_view()->visible());
EXPECT_EQ(0, GetContainerScore());
......
......@@ -160,6 +160,7 @@ component("cpp") {
# this after Deserialize(Serialize()) API works with handles.
mojom("test_interfaces") {
visibility = [ ":unit_tests" ]
disable_variants = true
sources = [
"shelf_struct_traits_test_service.mojom",
......
......@@ -10,6 +10,7 @@ import("//mojo/public/tools/bindings/mojom.gni")
# enums and constants).
mojom("interfaces_internal") {
visibility = [ "//ash/public/cpp:*" ]
disable_variants = true
sources = [
"accelerator_controller.mojom",
......@@ -72,6 +73,7 @@ mojom("interfaces_internal") {
"//components/arc/common:notifications",
"//components/sync/mojo:interfaces",
"//mojo/public/mojom/base",
"//services/content/public/mojom",
"//services/preferences/public/mojom",
"//skia/public/interfaces",
"//ui/accessibility:ax_enums_mojo",
......@@ -95,6 +97,7 @@ mojom("interfaces_internal") {
mojom("test_interfaces") {
testonly = true
disable_variants = true
sources = [
"shelf_test_api.mojom",
"shell_test_api.mojom",
......
......@@ -8,9 +8,11 @@ import "ash/public/interfaces/menu.mojom";
import "components/sync/mojo/syncer.mojom";
import "mojo/public/mojom/base/string16.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
import "services/content/public/mojom/navigable_contents_factory.mojom";
import "ui/gfx/geometry/mojo/geometry.mojom";
import "ui/gfx/image/mojo/image.mojom";
import "ui/gfx/range/mojo/range.mojom";
import "url/mojom/url.mojom";
// A structure holding the common information which is sent between ash and,
// chrome representing an app list item.
......@@ -59,17 +61,12 @@ struct SearchResultMetadata {
double display_score; // A score to determine the result display order.
bool is_omnibox_search; // Whether this result is searched from Omnibox.
bool is_installing; // Whether this result is installing.
mojo_base.mojom.UnguessableToken? answer_card_contents_token;
// A token used to render answer card, which is
// empty before initialized. It's illegal to pass
// an empty UnguessableToken across processes. So
// we use a null value when it's empty here.
gfx.mojom.Size? answer_card_size; // Preferred size of answer card. It is
// unset for non-answer-card results. For
// answer card results, it is set when
// the answer card WebContents finishes
// loading and its renderer notifies it
// about a new contents size.
url.mojom.Url? query_url; // A query URL associated with this result. The
// meaning and treatment of the URL
// (e.g. displaying inline web contents) is
// dependent on the result type.
gfx.mojom.ImageSkia? icon; // The icon of this result.
gfx.mojom.ImageSkia? chip_icon; // The icon of this result in a smaller
// dimension to be rendered in suggestion
......@@ -372,4 +369,9 @@ interface AppListClient {
StartVoiceInteractionSession();
// Starts or stops voice interaction session based on current state.
ToggleVoiceInteractionSession();
// Acquires a NavigableContentsFactory (indirectly) from the Content Service
// to allow the app list to display embedded web contents. Currently used only
// for answer card search results.
GetNavigableContentsFactory(content.mojom.NavigableContentsFactory& request);
};
......@@ -1097,8 +1097,7 @@ void Shell::Init(
// |app_list_controller_| is put after |tablet_mode_controller_| as the former
// uses the latter in constructor.
app_list_controller_ = std::make_unique<AppListControllerImpl>(
window_service_owner_->window_service());
app_list_controller_ = std::make_unique<AppListControllerImpl>();
shelf_controller_ = std::make_unique<ShelfController>();
magnifier_key_scroll_handler_ = MagnifierKeyScroller::CreateHandler();
......
......@@ -329,9 +329,9 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate {
return true;
}
ws::WindowService* GetWindowService() override {
void GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) override {
NOTIMPLEMENTED();
return nullptr;
}
std::unique_ptr<app_list::AppListModel> model_;
......
......@@ -3085,14 +3085,10 @@ jumbo_split_static_library("ui") {
"app_list/extension_uninstaller.h",
"app_list/md_icon_normalizer.cc",
"app_list/md_icon_normalizer.h",
"app_list/search/answer_card/answer_card_contents.cc",
"app_list/search/answer_card/answer_card_contents.h",
"app_list/search/answer_card/answer_card_result.cc",
"app_list/search/answer_card/answer_card_result.h",
"app_list/search/answer_card/answer_card_search_provider.cc",
"app_list/search/answer_card/answer_card_search_provider.h",
"app_list/search/answer_card/answer_card_web_contents.cc",
"app_list/search/answer_card/answer_card_web_contents.h",
"app_list/search/app_result.cc",
"app_list/search/app_result.h",
"app_list/search/app_search_provider.cc",
......
include_rules = [
"-chrome/browser/ui/views",
"+mash/public/mojom",
"+services/content/public",
"+services/device/public/mojom",
]
......@@ -36,6 +36,7 @@
#include "chrome/browser/ui/browser_navigator_params.h"
#include "content/public/common/service_manager_connection.h"
#include "extensions/common/extension.h"
#include "services/content/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/models/menu_model.h"
#include "ui/display/display.h"
......@@ -201,22 +202,6 @@ void AppListClientImpl::OnAppListVisibilityChanged(bool visible) {
app_list_visible_ = visible;
}
void AppListClientImpl::StartVoiceInteractionSession() {
auto* service =
arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(
ChromeLauncherController::instance()->profile());
if (service)
service->StartSessionFromUserInteraction(gfx::Rect());
}
void AppListClientImpl::ToggleVoiceInteractionSession() {
auto* service =
arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(
ChromeLauncherController::instance()->profile());
if (service)
service->ToggleSessionFromUserInteraction();
}
void AppListClientImpl::OnFolderCreated(
ash::mojom::AppListItemMetadataPtr item) {
if (!model_updater_)
......@@ -253,6 +238,30 @@ void AppListClientImpl::OnPageBreakItemDeleted(const std::string& id) {
model_updater_->OnPageBreakItemDeleted(id);
}
void AppListClientImpl::StartVoiceInteractionSession() {
auto* service =
arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(
ChromeLauncherController::instance()->profile());
if (service)
service->StartSessionFromUserInteraction(gfx::Rect());
}
void AppListClientImpl::ToggleVoiceInteractionSession() {
auto* service =
arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(
ChromeLauncherController::instance()->profile());
if (service)
service->ToggleSessionFromUserInteraction();
}
void AppListClientImpl::GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) {
if (profile_) {
content::BrowserContext::GetConnectorFor(profile_)->BindInterface(
content::mojom::kServiceName, std::move(request));
}
}
void AppListClientImpl::ActiveUserChanged(
const user_manager::User* active_user) {
if (!active_user->is_profile_created())
......
......@@ -65,18 +65,18 @@ class AppListClientImpl
void ContextMenuItemSelected(const std::string& id,
int command_id,
int event_flags) override;
void OnAppListTargetVisibilityChanged(bool visible) override;
void OnAppListVisibilityChanged(bool visible) override;
void StartVoiceInteractionSession() override;
void ToggleVoiceInteractionSession() override;
void OnFolderCreated(ash::mojom::AppListItemMetadataPtr item) override;
void OnFolderDeleted(ash::mojom::AppListItemMetadataPtr item) override;
void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) override;
void OnPageBreakItemAdded(const std::string& id,
const syncer::StringOrdinal& position) override;
void OnPageBreakItemDeleted(const std::string& id) override;
void StartVoiceInteractionSession() override;
void ToggleVoiceInteractionSession() override;
void GetNavigableContentsFactory(
content::mojom::NavigableContentsFactoryRequest request) override;
// user_manager::UserManager::UserSessionStateObserver:
void ActiveUserChanged(const user_manager::User* active_user) override;
......
// Copyright 2017 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/ui/app_list/search/answer_card/answer_card_contents.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_result.h"
namespace app_list {
AnswerCardContents::AnswerCardContents() {}
AnswerCardContents::~AnswerCardContents() {
for (auto& result : results_)
result.OnContentsDestroying();
}
void AnswerCardContents::SetDelegate(Delegate* delegate) {
DCHECK(delegate);
DCHECK(!delegate_);
delegate_ = delegate;
}
void AnswerCardContents::RegisterResult(AnswerCardResult* result) {
results_.AddObserver(result);
}
void AnswerCardContents::UnregisterResult(AnswerCardResult* result) {
results_.RemoveObserver(result);
}
} // namespace app_list
// Copyright 2017 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_UI_APP_LIST_SEARCH_ANSWER_CARD_ANSWER_CARD_CONTENTS_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_ANSWER_CARD_CONTENTS_H_
#include <string>
#include "base/observer_list.h"
#include "base/unguessable_token.h"
namespace gfx {
class Size;
} // namespace gfx
class GURL;
namespace app_list {
class AnswerCardResult;
// Abstract source of contents for AnswerCardSearchProvider.
class AnswerCardContents {
public:
// Delegate handling contents-related events.
class Delegate {
public:
Delegate() {}
// Events that the delegate processes. They have same meaning as same-named
// events in WebContentsDelegate and WebContentsObserver, however,
// unnecessary parameters are omitted.
virtual void UpdatePreferredSize(const AnswerCardContents* source) = 0;
virtual void DidFinishNavigation(const AnswerCardContents* source,
const GURL& url,
bool has_error,
bool has_answer_card,
const std::string& result_title,
const std::string& issued_query) = 0;
// Invoked when |source| is ready to be shown.
virtual void OnContentsReady(const AnswerCardContents* source) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
AnswerCardContents();
virtual ~AnswerCardContents();
// Loads contents from |url|.
virtual void LoadURL(const GURL& url) = 0;
// Returns the token associated with the contents.
virtual const base::UnguessableToken& GetToken() const = 0;
// Returns the preferred contents size.
virtual gfx::Size GetPreferredSize() const = 0;
// Sets the delegate to process contents-related events.
void SetDelegate(Delegate* delegate);
// Registers a result that will be notified of input events for the view.
void RegisterResult(AnswerCardResult* result);
// Unregisters a result.
void UnregisterResult(AnswerCardResult* result);
protected:
Delegate* delegate() const { return delegate_; }
private:
// Results receiving input events.
base::ObserverList<AnswerCardResult>::Unchecked results_;
// Unowned delegate that handles content-related events.
Delegate* delegate_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(AnswerCardContents);
};
} // namespace app_list
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_ANSWER_CARD_CONTENTS_H_
......@@ -4,45 +4,27 @@
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_result.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_contents.h"
#include "chrome/browser/ui/app_list/search/search_util.h"
namespace app_list {
AnswerCardResult::AnswerCardResult(Profile* profile,
AppListControllerDelegate* list_controller,
const std::string& result_url,
const std::string& stripped_result_url,
const base::string16& result_title,
AnswerCardContents* contents)
: profile_(profile),
list_controller_(list_controller),
contents_(contents) {
DCHECK(!stripped_result_url.empty());
const GURL& potential_card_url,
const GURL& search_result_url,
const GURL& stripped_search_result_url)
: profile_(profile), list_controller_(list_controller) {
DCHECK(!stripped_search_result_url.is_empty());
SetDisplayType(ash::SearchResultDisplayType::kCard);
SetResultType(ash::SearchResultType::kAnswerCard);
set_id(result_url);
set_comparable_id(stripped_result_url);
SetQueryUrl(potential_card_url);
set_id(search_result_url.spec());
set_comparable_id(stripped_search_result_url.spec());
set_relevance(1);
SetAnswerCardContentsToken(contents ? contents->GetToken()
: base::UnguessableToken());
SetAnswerCardSize(contents ? contents->GetPreferredSize() : gfx::Size());
SetTitle(result_title);
if (contents)
contents->RegisterResult(this);
}
AnswerCardResult::~AnswerCardResult() {
if (contents_)
contents_->UnregisterResult(this);
}
void AnswerCardResult::OnContentsDestroying() {
contents_ = nullptr;
}
AnswerCardResult::~AnswerCardResult() = default;
void AnswerCardResult::Open(int event_flags) {
list_controller_->OpenURL(profile_, GURL(id()), ui::PAGE_TRANSITION_GENERATED,
......
......@@ -9,34 +9,29 @@
#include <string>
#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "url/gurl.h"
class AppListControllerDelegate;
class Profile;
namespace app_list {
class AnswerCardContents;
// Result of AnswerCardSearchProvider.
class AnswerCardResult : public ChromeSearchResult {
public:
AnswerCardResult(Profile* profile,
AppListControllerDelegate* list_controller,
const std::string& result_url,
const std::string& stripped_result_url,
const base::string16& result_title,
AnswerCardContents* contents);
const GURL& potential_card_url,
const GURL& search_result_url,
const GURL& stripped_search_result_url);
~AnswerCardResult() override;
void OnContentsDestroying();
void Open(int event_flags) override;
private:
Profile* const profile_; // Unowned
AppListControllerDelegate* const list_controller_; // Unowned
AnswerCardContents* contents_; // Unowned
DISALLOW_COPY_AND_ASSIGN(AnswerCardResult);
};
......
......@@ -9,7 +9,6 @@
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/app_list/app_list_test_util.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_contents.h"
#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
#include "chrome/test/base/testing_profile.h"
#include "ui/views/view.h"
......@@ -19,62 +18,40 @@ namespace test {
namespace {
constexpr char kResultUrl[] =
constexpr char kSearchResultUrl[] =
"http://www.google.com/search?q=weather&strippable_part=something";
constexpr char kResultUrlStripped[] = "http://google.com/search?q=weather";
constexpr char kResultTitle[] = "The weather is fine";
class AnswerCardTestContents : public AnswerCardContents {
public:
AnswerCardTestContents() {}
// AnswerCardContents overrides:
void LoadURL(const GURL& url) override { NOTREACHED(); }
const base::UnguessableToken& GetToken() const override { return token_; }
gfx::Size GetPreferredSize() const override { return gfx::Size(); }
private:
base::UnguessableToken token_;
DISALLOW_COPY_AND_ASSIGN(AnswerCardTestContents);
};
constexpr char kStrippedSearchResultUrl[] =
"http://google.com/search?q=weather";
constexpr char kCardUrl[] = "http://google.com/coac?q=weather";
} // namespace
class AnswerCardResultTest : public AppListTestBase {
public:
AnswerCardResultTest() {}
void DeleteContents() { contents_.reset(nullptr); }
AnswerCardResultTest() = default;
std::unique_ptr<AnswerCardResult> CreateResult(
const std::string& result_url,
const std::string& stripped_result_url,
const base::string16& result_title) const {
const GURL& potential_card_url,
const GURL& search_result_url,
const GURL& stripped_search_result_url) const {
return std::make_unique<AnswerCardResult>(
profile_.get(), app_list_controller_delegate_.get(), result_url,
stripped_result_url, result_title, contents_.get());
profile_.get(), app_list_controller_delegate_.get(), potential_card_url,
search_result_url, stripped_search_result_url);
}
const GURL& GetLastOpenedUrl() const {
return app_list_controller_delegate_->last_opened_url();
}
const base::UnguessableToken& GetToken() const {
return contents_->GetToken();
}
// AppListTestBase overrides:
void SetUp() override {
AppListTestBase::SetUp();
contents_ = std::make_unique<AnswerCardTestContents>();
app_list_controller_delegate_ =
std::make_unique<::test::TestAppListControllerDelegate>();
}
private:
std::unique_ptr<AnswerCardTestContents> contents_;
std::unique_ptr<::test::TestAppListControllerDelegate>
app_list_controller_delegate_;
......@@ -83,32 +60,16 @@ class AnswerCardResultTest : public AppListTestBase {
TEST_F(AnswerCardResultTest, Basic) {
std::unique_ptr<AnswerCardResult> result = CreateResult(
kResultUrl, kResultUrlStripped, base::ASCIIToUTF16(kResultTitle));
GURL(kCardUrl), GURL(kSearchResultUrl), GURL(kStrippedSearchResultUrl));
EXPECT_EQ(kResultUrl, result->id());
EXPECT_EQ(base::ASCIIToUTF16(kResultTitle), result->title());
EXPECT_EQ(kSearchResultUrl, result->id());
EXPECT_EQ(kStrippedSearchResultUrl, result->comparable_id());
EXPECT_EQ(kCardUrl, result->query_url()->spec());
EXPECT_EQ(ash::SearchResultDisplayType::kCard, result->display_type());
EXPECT_EQ(1, result->relevance());
EXPECT_EQ(GetToken(), result->answer_card_contents_token());
result->Open(ui::EF_NONE);
EXPECT_EQ(kResultUrl, GetLastOpenedUrl().spec());
}
TEST_F(AnswerCardResultTest, NullContents) {
DeleteContents();
// Shouldn't crash with null contents.
std::unique_ptr<AnswerCardResult> result = CreateResult(
kResultUrl, kResultUrlStripped, base::ASCIIToUTF16(kResultTitle));
}
TEST_F(AnswerCardResultTest, EarlyDeleteContents) {
// Shouldn't crash with contents gets deleted while search result exists.
std::unique_ptr<AnswerCardResult> result = CreateResult(
kResultUrl, kResultUrlStripped, base::ASCIIToUTF16(kResultTitle));
DeleteContents();
EXPECT_EQ(kSearchResultUrl, GetLastOpenedUrl().spec());
}
} // namespace test
......
......@@ -22,214 +22,60 @@
namespace app_list {
namespace {
enum class SearchAnswerRequestResult {
REQUEST_RESULT_ANOTHER_REQUEST_STARTED = 0,
REQUEST_RESULT_REQUEST_FAILED = 1,
REQUEST_RESULT_NO_ANSWER = 2,
REQUEST_RESULT_RECEIVED_ANSWER = 3,
REQUEST_RESULT_RECEIVED_ANSWER_TOO_LARGE = 4,
REQUEST_RESULT_MAX = 5
};
void RecordRequestResult(SearchAnswerRequestResult request_result) {
UMA_HISTOGRAM_ENUMERATION("SearchAnswer.RequestResult", request_result,
SearchAnswerRequestResult::REQUEST_RESULT_MAX);
}
} // namespace
AnswerCardSearchProvider::NavigationContext::NavigationContext() {}
AnswerCardSearchProvider::NavigationContext::~NavigationContext() {}
void AnswerCardSearchProvider::NavigationContext::StartServerRequest(
const GURL& url) {
contents->LoadURL(url);
state = RequestState::NO_RESULT;
}
void AnswerCardSearchProvider::NavigationContext::Clear() {
result_url.clear();
result_title.clear();
state = RequestState::NO_RESULT;
// We are not clearing |preferred_size| since the |contents| remains
// unchanged, and |preferred_size| always corresponds to the contents's size.
}
AnswerCardSearchProvider::AnswerCardSearchProvider(
Profile* profile,
AppListModelUpdater* model_updater,
AppListControllerDelegate* list_controller,
std::unique_ptr<AnswerCardContents> contents0,
std::unique_ptr<AnswerCardContents> contents1)
AppListControllerDelegate* list_controller)
: profile_(profile),
model_updater_(model_updater),
list_controller_(list_controller),
answer_server_url_(app_list_features::AnswerServerUrl()),
template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)) {
navigation_contexts_[0].contents = std::move(contents0);
navigation_contexts_[1].contents = std::move(contents1);
navigation_contexts_[0].contents->SetDelegate(this);
navigation_contexts_[1].contents->SetDelegate(this);
}
AnswerCardSearchProvider::~AnswerCardSearchProvider() {
}
AnswerCardSearchProvider::~AnswerCardSearchProvider() = default;
void AnswerCardSearchProvider::Start(const base::string16& query) {
// Reset the state.
current_request_url_ = GURL();
server_request_start_time_ = answer_loaded_time_ = base::TimeTicks();
if (query.empty() || !model_updater_->SearchEngineIsGoogle()) {
DeleteCurrentResult();
if (!model_updater_->SearchEngineIsGoogle()) {
UpdateQuery(base::string16());
return;
}
// Start a request to the answer server.
UpdateQuery(query);
}
void AnswerCardSearchProvider::UpdateQuery(const base::string16& query) {
if (query.empty()) {
ClearResults();
current_potential_answer_card_url_ = GURL();
return;
}
// Lifetime of |prefixed_query| should be longer than the one of
// |replacements|.
const std::string prefixed_query(
"q=" + net::EscapeQueryParamValue(base::UTF16ToUTF8(query), true) +
app_list_features::AnswerServerQuerySuffix());
GURL::Replacements replacements;
replacements.SetQueryStr(prefixed_query);
current_request_url_ = answer_server_url_.ReplaceComponents(replacements);
GetNavigationContextForLoading().StartServerRequest(current_request_url_);
server_request_start_time_ = base::TimeTicks::Now();
}
void AnswerCardSearchProvider::UpdatePreferredSize(
const AnswerCardContents* source) {
if (source != GetCurrentNavigationContext().contents.get())
return;
// Contents' size changed for the current card. Updating the result to cause
// relayout.
UpdateResult();
if (!answer_loaded_time_.is_null()) {
UMA_HISTOGRAM_TIMES("SearchAnswer.ResizeAfterLoadTime",
base::TimeTicks::Now() - answer_loaded_time_);
}
}
void AnswerCardSearchProvider::DidFinishNavigation(
const AnswerCardContents* source,
const GURL& url,
bool has_error,
bool has_answer_card,
const std::string& result_title,
const std::string& issued_query) {
NavigationContext& context_for_loading = GetNavigationContextForLoading();
DCHECK_EQ(source, context_for_loading.contents.get());
if (url != current_request_url_) {
RecordRequestResult(
SearchAnswerRequestResult::REQUEST_RESULT_ANOTHER_REQUEST_STARTED);
return;
}
if (has_error) {
RecordRequestResult(
SearchAnswerRequestResult::REQUEST_RESULT_REQUEST_FAILED);
// Loading new card has failed. This invalidates the currently shown result.
DeleteCurrentResult();
return;
}
if (!has_answer_card) {
RecordRequestResult(SearchAnswerRequestResult::REQUEST_RESULT_NO_ANSWER);
// No answer card in the server response. This invalidates the currently
// shown result.
DeleteCurrentResult();
return;
}
DCHECK(!result_title.empty());
DCHECK(!issued_query.empty());
context_for_loading.result_title = result_title;
context_for_loading.result_url =
GetResultUrl(base::UTF8ToUTF16(issued_query));
RecordRequestResult(
SearchAnswerRequestResult::REQUEST_RESULT_RECEIVED_ANSWER);
context_for_loading.state = RequestState::HAVE_RESULT_LOADING;
UMA_HISTOGRAM_TIMES("SearchAnswer.NavigationTime",
base::TimeTicks::Now() - server_request_start_time_);
}
void AnswerCardSearchProvider::OnContentsReady(
const AnswerCardContents* source) {
NavigationContext& context_for_loading = GetNavigationContextForLoading();
DCHECK_EQ(source, context_for_loading.contents.get());
if (context_for_loading.state != RequestState::HAVE_RESULT_LOADING) {
// This stop-loading event is either for a navigation that was intercepted
// by another navigation, or for a failed navigation. In both cases, there
// is nothing we need to do about it.
GURL potential_answer_card_url =
answer_server_url_.ReplaceComponents(replacements);
if (potential_answer_card_url == current_potential_answer_card_url_)
return;
}
context_for_loading.state = RequestState::HAVE_RESULT_LOADED;
// Prepare for loading card into the other contents. Loading will start when
// the user modifies the query string.
GetCurrentNavigationContext().Clear();
current_navigation_context_ = 1 - current_navigation_context_;
// Show the result.
UpdateResult();
current_potential_answer_card_url_ = potential_answer_card_url;
GURL search_result_url =
GURL(template_url_service_->GetDefaultSearchProvider()
->url_ref()
.ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(query),
template_url_service_->search_terms_data()));
const GURL stripped_search_result_url = AutocompleteMatch::GURLToStrippedGURL(
GURL(search_result_url), AutocompleteInput(), template_url_service_,
base::string16() /* keyword */);
answer_loaded_time_ = base::TimeTicks::Now();
UMA_HISTOGRAM_TIMES("SearchAnswer.LoadingTime",
answer_loaded_time_ - server_request_start_time_);
}
void AnswerCardSearchProvider::UpdateResult() {
SearchProvider::Results results;
const NavigationContext& current_context = GetCurrentNavigationContext();
if (current_context.state == RequestState::HAVE_RESULT_LOADED) {
results.reserve(1);
const GURL stripped_result_url = AutocompleteMatch::GURLToStrippedGURL(
GURL(current_context.result_url), AutocompleteInput(),
template_url_service_, base::string16() /* keyword */);
results.emplace_back(std::make_unique<AnswerCardResult>(
profile_, list_controller_, current_context.result_url,
stripped_result_url.spec(),
base::UTF8ToUTF16(current_context.result_title),
current_context.contents.get()));
}
results.emplace_back(std::make_unique<AnswerCardResult>(
profile_, list_controller_, potential_answer_card_url, search_result_url,
stripped_search_result_url));
SwapResults(&results);
}
std::string AnswerCardSearchProvider::GetResultUrl(
const base::string16& query) const {
return template_url_service_->GetDefaultSearchProvider()
->url_ref()
.ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(query),
template_url_service_->search_terms_data());
}
void AnswerCardSearchProvider::DeleteCurrentResult() {
GetCurrentNavigationContext().Clear();
UpdateResult();
}
AnswerCardSearchProvider::NavigationContext&
AnswerCardSearchProvider::GetCurrentNavigationContext() {
return navigation_contexts_[current_navigation_context_];
}
AnswerCardSearchProvider::NavigationContext&
AnswerCardSearchProvider::GetNavigationContextForLoading() {
return navigation_contexts_[1 - current_navigation_context_];
}
} // namespace app_list
......@@ -8,8 +8,6 @@
#include <memory>
#include <string>
#include "base/time/time.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_contents.h"
#include "chrome/browser/ui/app_list/search/search_provider.h"
#include "url/gurl.h"
......@@ -21,69 +19,19 @@ class TemplateURLService;
namespace app_list {
// Search provider for the answer card.
class AnswerCardSearchProvider : public SearchProvider,
public AnswerCardContents::Delegate {
class AnswerCardSearchProvider : public SearchProvider {
public:
AnswerCardSearchProvider(Profile* profile,
AppListModelUpdater* model_updater,
AppListControllerDelegate* list_controller,
std::unique_ptr<AnswerCardContents> contents0,
std::unique_ptr<AnswerCardContents> contents1);
AppListControllerDelegate* list_controller);
~AnswerCardSearchProvider() override;
// SearchProvider overrides:
void Start(const base::string16& query) override;
// AnswerCardContents::Delegate overrides:
void UpdatePreferredSize(const AnswerCardContents* source) override;
void DidFinishNavigation(const AnswerCardContents* source,
const GURL& url,
bool has_error,
bool has_answer_card,
const std::string& result_title,
const std::string& issued_query) override;
void OnContentsReady(const AnswerCardContents* source) override;
private:
enum class RequestState {
NO_RESULT,
HAVE_RESULT_LOADING,
HAVE_RESULT_LOADED
};
// State of navigation for a single AnswerCardContents. There are 2 instances
// of AnswerCardContents: one is used to show the current answer, and the
// other for loading an answer for the next query. Once the answer has been
// loaded, the roles of contents instances get swapped.
struct NavigationContext {
NavigationContext();
~NavigationContext();
void StartServerRequest(const GURL& url);
void Clear();
// The source of answer card contents.
std::unique_ptr<AnswerCardContents> contents;
// State of a server request.
RequestState state = RequestState::NO_RESULT;
// Url to open when the user clicks at the result.
std::string result_url;
// Title of the result.
std::string result_title;
DISALLOW_COPY_AND_ASSIGN(NavigationContext);
};
void UpdateResult();
// Returns Url to open when the user clicks at the result for |query|.
std::string GetResultUrl(const base::string16& query) const;
void DeleteCurrentResult();
NavigationContext& GetCurrentNavigationContext();
NavigationContext& GetNavigationContextForLoading();
void UpdateQuery(const base::string16& query);
// Unowned pointer to the associated profile.
Profile* const profile_;
......@@ -94,27 +42,11 @@ class AnswerCardSearchProvider : public SearchProvider,
// Unowned pointer to app list controller.
AppListControllerDelegate* const list_controller_;
// Index of the navigation contents corresponding to the current result. 1 -
// |current_navigation_context_| will be used for loading the next card, or is
// already used loading a new card. This pointer switches to the other
// contents after the card gets successfully loaded.
int current_navigation_context_ = 0;
// States of card navigation. one is used to show the current answer, and
// another for loading an answer for the next query.
NavigationContext navigation_contexts_[2];
// If valid, URL of the answer server. Otherwise, search answers are disabled.
GURL answer_server_url_;
// URL of the current answer server request.
GURL current_request_url_;
// Time when the current server request started.
base::TimeTicks server_request_start_time_;
const GURL answer_server_url_;
// Time when the current server response loaded.
base::TimeTicks answer_loaded_time_;
// URL of the current answer server query.
GURL current_potential_answer_card_url_;
// Unowned pointer to template URL service.
TemplateURLService* const template_url_service_;
......
// Copyright 2017 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_UI_APP_LIST_SEARCH_ANSWER_CARD_ANSWER_CARD_WEB_CONTENTS_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_ANSWER_CARD_WEB_CONTENTS_H_
#include <memory>
#include "base/memory/weak_ptr.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_contents.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
class Profile;
namespace views {
class RemoteViewProvider;
class WebView;
}
namespace app_list {
// Web source of contents for AnswerCardSearchProvider.
class AnswerCardWebContents : public AnswerCardContents,
public content::WebContentsDelegate,
public content::WebContentsObserver {
public:
explicit AnswerCardWebContents(Profile* profile);
~AnswerCardWebContents() override;
// AnswerCardContents overrides:
void LoadURL(const GURL& url) override;
const base::UnguessableToken& GetToken() const override;
gfx::Size GetPreferredSize() const override;
// content::WebContentsDelegate overrides:
void ResizeDueToAutoResize(content::WebContents* web_contents,
const gfx::Size& new_size) override;
content::WebContents* OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) override;
bool HandleContextMenu(const content::ContextMenuParams& params) override;
// content::WebContentsObserver overrides:
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void DidStopLoading() override;
void DidGetUserInteraction(const blink::WebInputEvent::Type type) override;
void RenderViewCreated(content::RenderViewHost* host) override;
void RenderViewDeleted(content::RenderViewHost* host) override;
void RenderViewHostChanged(content::RenderViewHost* old_host,
content::RenderViewHost* new_host) override;
private:
void AttachToHost(content::RenderWidgetHost* host);
void DetachFromHost();
void OnGotEmbedTokenAndNotify(const base::UnguessableToken& token);
// Web contents managed by this class.
const std::unique_ptr<content::WebContents> web_contents_;
// Web view for the web contents managed by this class.
// Note this is only used for classic ash.
std::unique_ptr<views::WebView> web_view_;
// Current widget host.
content::RenderWidgetHost* host_ = nullptr;
Profile* const profile_; // Unowned
// Token to embed the web contents. On classic ash, it is a token registered
// with AnswerCardContentsRegistry. On mash, it is the embedding token for
// the native window of |web_contents_|.
base::UnguessableToken token_;
// Preferred size of web contents.
gfx::Size preferred_size_;
// Helper to prepare the native view of |web_contents_| to be embedded under
// mash.
std::unique_ptr<views::RemoteViewProvider> remote_view_provider_;
base::WeakPtrFactory<AnswerCardWebContents> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(AnswerCardWebContents);
};
} // namespace app_list
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_ANSWER_CARD_WEB_CONTENTS_H_
......@@ -108,27 +108,19 @@ void ChromeSearchResult::SetIsOmniboxSearch(bool is_omnibox_search) {
updater->SetSearchResultMetadata(id(), CloneMetadata());
}
void ChromeSearchResult::SetAnswerCardContentsToken(
const base::UnguessableToken& token) {
metadata_->answer_card_contents_token = token;
void ChromeSearchResult::SetPercentDownloaded(int percent_downloaded) {
AppListModelUpdater* updater = model_updater();
if (updater)
updater->SetSearchResultMetadata(id(), CloneMetadata());
updater->SetSearchResultPercentDownloaded(id(), percent_downloaded);
}
void ChromeSearchResult::SetAnswerCardSize(const gfx::Size& size) {
metadata_->answer_card_size = size;
AppListModelUpdater* updater = model_updater();
void ChromeSearchResult::SetQueryUrl(const GURL& url) {
metadata_->query_url = url;
auto* updater = model_updater();
if (updater)
updater->SetSearchResultMetadata(id(), CloneMetadata());
}
void ChromeSearchResult::SetPercentDownloaded(int percent_downloaded) {
AppListModelUpdater* updater = model_updater();
if (updater)
updater->SetSearchResultPercentDownloaded(id(), percent_downloaded);
}
void ChromeSearchResult::SetIcon(const gfx::ImageSkia& icon) {
icon.EnsureRepsForSupportedScales();
metadata_->icon = icon;
......
......@@ -12,7 +12,6 @@
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/interfaces/app_list.mojom.h"
#include "base/macros.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ui/app_list/app_list_model_updater.h"
namespace app_list {
......@@ -50,9 +49,7 @@ class ChromeSearchResult {
const Actions& actions() const { return metadata_->actions; }
double display_score() const { return metadata_->display_score; }
bool is_installing() const { return metadata_->is_installing; }
const base::UnguessableToken& answer_card_contents_token() const {
return metadata_->answer_card_contents_token.value();
}
const base::Optional<GURL>& query_url() const { return metadata_->query_url; }
const gfx::ImageSkia& icon() const { return metadata_->icon; }
const gfx::ImageSkia& chip_icon() const { return metadata_->chip_icon; }
const gfx::ImageSkia& badge_icon() const { return metadata_->badge_icon; }
......@@ -73,9 +70,8 @@ class ChromeSearchResult {
void SetDisplayScore(double display_score);
void SetActions(const Actions& actions);
void SetIsOmniboxSearch(bool is_omnibox_search);
void SetAnswerCardContentsToken(const base::UnguessableToken& token);
void SetAnswerCardSize(const gfx::Size& size);
void SetIsInstalling(bool is_installing);
void SetQueryUrl(const GURL& url);
void SetIcon(const gfx::ImageSkia& icon);
void SetChipIcon(const gfx::ImageSkia& icon);
void SetBadgeIcon(const gfx::ImageSkia& badge_icon);
......
......@@ -14,7 +14,6 @@
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider.h"
#include "chrome/browser/ui/app_list/search/answer_card/answer_card_web_contents.h"
#include "chrome/browser/ui/app_list/search/app_search_provider.h"
#include "chrome/browser/ui/app_list/search/arc/arc_app_data_search_provider.h"
#include "chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.h"
......@@ -87,12 +86,9 @@ std::unique_ptr<SearchController> CreateSearchController(
controller->AddProvider(omnibox_group_id, std::make_unique<OmniboxProvider>(
profile, list_controller));
if (app_list_features::IsAnswerCardEnabled()) {
controller->AddProvider(
answer_card_group_id,
std::make_unique<AnswerCardSearchProvider>(
profile, model_updater, list_controller,
std::make_unique<AnswerCardWebContents>(profile),
std::make_unique<AnswerCardWebContents>(profile)));
controller->AddProvider(answer_card_group_id,
std::make_unique<AnswerCardSearchProvider>(
profile, model_updater, list_controller));
}
// LauncherSearchProvider is added only when flag is enabled, not in guest
......
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