summaryrefslogtreecommitdiff
path: root/chromium/chrome/renderer/searchbox
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-02-13 10:55:42 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-03-05 13:33:38 +0000
commit248b70b82a40964d5594eb04feca0fa36716185d (patch)
tree44e31d9dd0ac2cb79f48633eefbc5496e013c347 /chromium/chrome/renderer/searchbox
parentcabfcdd1db482729ded525feae56911a99792773 (diff)
downloadqtwebengine-chromium-248b70b82a40964d5594eb04feca0fa36716185d.tar.gz
BASELINE: Update Chromium to 79.0.3945.147
And new simplified snapshot filter Change-Id: I7c692bedd5b3833f05565bd6f6939115350b233a Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/chrome/renderer/searchbox')
-rw-r--r--chromium/chrome/renderer/searchbox/DEPS4
-rw-r--r--chromium/chrome/renderer/searchbox/OWNERS4
-rw-r--r--chromium/chrome/renderer/searchbox/search_bouncer.cc56
-rw-r--r--chromium/chrome/renderer/searchbox/search_bouncer.h48
-rw-r--r--chromium/chrome/renderer/searchbox/search_bouncer_unittest.cc29
-rw-r--r--chromium/chrome/renderer/searchbox/searchbox.cc596
-rw-r--r--chromium/chrome/renderer/searchbox/searchbox.h274
-rw-r--r--chromium/chrome/renderer/searchbox/searchbox_extension.cc1469
-rw-r--r--chromium/chrome/renderer/searchbox/searchbox_extension.h57
-rw-r--r--chromium/chrome/renderer/searchbox/searchbox_unittest.cc271
10 files changed, 2808 insertions, 0 deletions
diff --git a/chromium/chrome/renderer/searchbox/DEPS b/chromium/chrome/renderer/searchbox/DEPS
new file mode 100644
index 00000000000..03093492dc7
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+components/favicon_base",
+ "+components/ntp_tiles",
+]
diff --git a/chromium/chrome/renderer/searchbox/OWNERS b/chromium/chrome/renderer/searchbox/OWNERS
new file mode 100644
index 00000000000..fa6ab8f1fb5
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/OWNERS
@@ -0,0 +1,4 @@
+file://chrome/browser/search/OWNERS
+
+dcblack@chromium.org
+# COMPONENT: UI>Browser>NewTabPage
diff --git a/chromium/chrome/renderer/searchbox/search_bouncer.cc b/chromium/chrome/renderer/searchbox/search_bouncer.cc
new file mode 100644
index 00000000000..126a99f82f8
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/search_bouncer.cc
@@ -0,0 +1,56 @@
+// Copyright 2013 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/renderer/searchbox/search_bouncer.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+
+namespace {
+base::LazyInstance<SearchBouncer>::Leaky g_search_bouncer =
+ LAZY_INSTANCE_INITIALIZER;
+
+GURL RemoveQueryAndRef(const GURL& url) {
+ url::Replacements<char> replacements;
+ replacements.ClearQuery();
+ replacements.ClearRef();
+ return url.ReplaceComponents(replacements);
+}
+
+} // namespace
+
+SearchBouncer::SearchBouncer() = default;
+
+SearchBouncer::~SearchBouncer() = default;
+
+// static
+SearchBouncer* SearchBouncer::GetInstance() {
+ return g_search_bouncer.Pointer();
+}
+
+void SearchBouncer::RegisterMojoInterfaces(
+ blink::AssociatedInterfaceRegistry* associated_interfaces) {
+ // Note: Unretained is safe here because this class is a leaky LazyInstance.
+ // For the same reason, UnregisterMojoInterfaces isn't required.
+ associated_interfaces->AddInterface(base::Bind(
+ &SearchBouncer::BindSearchBouncerReceiver, base::Unretained(this)));
+}
+
+bool SearchBouncer::IsNewTabPage(const GURL& url) const {
+ GURL url_no_query_or_ref = RemoveQueryAndRef(url);
+ return url_no_query_or_ref.is_valid() &&
+ url_no_query_or_ref == new_tab_page_url_;
+}
+
+void SearchBouncer::SetNewTabPageURL(const GURL& new_tab_page_url) {
+ new_tab_page_url_ = new_tab_page_url;
+}
+
+void SearchBouncer::BindSearchBouncerReceiver(
+ mojo::PendingAssociatedReceiver<chrome::mojom::SearchBouncer> receiver) {
+ search_bouncer_receiver_.Bind(std::move(receiver));
+}
diff --git a/chromium/chrome/renderer/searchbox/search_bouncer.h b/chromium/chrome/renderer/searchbox/search_bouncer.h
new file mode 100644
index 00000000000..929a85fb38f
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/search_bouncer.h
@@ -0,0 +1,48 @@
+// Copyright 2013 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_RENDERER_SEARCHBOX_SEARCH_BOUNCER_H_
+#define CHROME_RENDERER_SEARCHBOX_SEARCH_BOUNCER_H_
+
+#include "base/macros.h"
+#include "chrome/common/search.mojom.h"
+#include "content/public/renderer/render_thread_observer.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "url/gurl.h"
+
+// SearchBouncer tracks a set of URLs which should be transferred back to the
+// browser process for potential reassignment to an Instant renderer process.
+class SearchBouncer : public content::RenderThreadObserver,
+ public chrome::mojom::SearchBouncer {
+ public:
+ SearchBouncer();
+ ~SearchBouncer() override;
+
+ static SearchBouncer* GetInstance();
+
+ // RenderThreadObserver:
+ void RegisterMojoInterfaces(
+ blink::AssociatedInterfaceRegistry* associated_interfaces) override;
+
+ // Returns whether |url| is a valid Instant new tab page URL.
+ bool IsNewTabPage(const GURL& url) const;
+
+ // chrome::mojom::SearchBouncer:
+ void SetNewTabPageURL(const GURL& new_tab_page_url) override;
+
+ private:
+ void BindSearchBouncerReceiver(
+ mojo::PendingAssociatedReceiver<chrome::mojom::SearchBouncer> receiver);
+
+ GURL new_tab_page_url_;
+
+ mojo::AssociatedReceiver<chrome::mojom::SearchBouncer>
+ search_bouncer_receiver_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(SearchBouncer);
+};
+
+#endif // CHROME_RENDERER_SEARCHBOX_SEARCH_BOUNCER_H_
diff --git a/chromium/chrome/renderer/searchbox/search_bouncer_unittest.cc b/chromium/chrome/renderer/searchbox/search_bouncer_unittest.cc
new file mode 100644
index 00000000000..0d18a93e2a7
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/search_bouncer_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 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/renderer/searchbox/search_bouncer.h"
+
+#include <vector>
+
+#include "chrome/common/url_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+class SearchBouncerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ bouncer_.SetNewTabPageURL(GURL("http://example.com/newtab"));
+ }
+
+ SearchBouncer bouncer_;
+};
+
+TEST_F(SearchBouncerTest, IsNewTabPage) {
+ EXPECT_FALSE(bouncer_.IsNewTabPage(GURL("http://example.com/foo")));
+ EXPECT_TRUE(bouncer_.IsNewTabPage(GURL("http://example.com/newtab")));
+ EXPECT_TRUE(bouncer_.IsNewTabPage(GURL("http://example.com/newtab?q=foo")));
+ EXPECT_TRUE(bouncer_.IsNewTabPage(GURL("http://example.com/newtab#q=foo")));
+ EXPECT_TRUE(
+ bouncer_.IsNewTabPage(GURL("http://example.com/newtab#q=foo?q=foo")));
+}
diff --git a/chromium/chrome/renderer/searchbox/searchbox.cc b/chromium/chrome/renderer/searchbox/searchbox.cc
new file mode 100644
index 00000000000..ebb238442cf
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/searchbox.cc
@@ -0,0 +1,596 @@
+// Copyright 2012 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/renderer/searchbox/searchbox.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "chrome/common/search.mojom.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/renderer/searchbox/searchbox_extension.h"
+#include "components/favicon_base/favicon_types.h"
+#include "components/favicon_base/favicon_url_parser.h"
+#include "components/url_formatter/url_fixer.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_performance.h"
+
+namespace {
+
+// The size of the InstantMostVisitedItem cache.
+const size_t kMaxInstantMostVisitedItemCacheSize = 100;
+
+// Returns true if items stored in |old_item_id_pairs| and |new_items| are
+// equal.
+bool AreMostVisitedItemsEqual(
+ const std::vector<InstantMostVisitedItemIDPair>& old_item_id_pairs,
+ const std::vector<InstantMostVisitedItem>& new_items) {
+ if (old_item_id_pairs.size() != new_items.size())
+ return false;
+
+ for (size_t i = 0; i < new_items.size(); ++i) {
+ if (new_items[i].url != old_item_id_pairs[i].second.url ||
+ new_items[i].title != old_item_id_pairs[i].second.title ||
+ new_items[i].source != old_item_id_pairs[i].second.source) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Helper for SearchBox::GenerateImageURLFromTransientURL().
+class SearchBoxIconURLHelper: public SearchBox::IconURLHelper {
+ public:
+ explicit SearchBoxIconURLHelper(const SearchBox* search_box);
+ ~SearchBoxIconURLHelper() override;
+ int GetViewID() const override;
+ std::string GetURLStringFromRestrictedID(InstantRestrictedID rid) const
+ override;
+
+ private:
+ const SearchBox* search_box_;
+};
+
+SearchBoxIconURLHelper::SearchBoxIconURLHelper(const SearchBox* search_box)
+ : search_box_(search_box) {
+}
+
+SearchBoxIconURLHelper::~SearchBoxIconURLHelper() {
+}
+
+int SearchBoxIconURLHelper::GetViewID() const {
+ return search_box_->render_frame()->GetRenderView()->GetRoutingID();
+}
+
+std::string SearchBoxIconURLHelper::GetURLStringFromRestrictedID(
+ InstantRestrictedID rid) const {
+ InstantMostVisitedItem item;
+ if (!search_box_->GetMostVisitedItemWithID(rid, &item))
+ return std::string();
+
+ return item.url.spec();
+}
+
+} // namespace
+
+namespace internal { // for testing
+
+// Parses "<view_id>/<restricted_id>". If successful, assigns
+// |*view_id| := "<view_id>", |*rid| := "<restricted_id>", and returns true.
+bool ParseViewIdAndRestrictedId(const std::string& id_part,
+ int* view_id_out,
+ InstantRestrictedID* rid_out) {
+ DCHECK(view_id_out);
+ DCHECK(rid_out);
+ // Check that the path is of Most visited item ID form.
+ std::vector<base::StringPiece> tokens = base::SplitStringPiece(
+ id_part, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (tokens.size() != 2)
+ return false;
+
+ int view_id;
+ InstantRestrictedID rid;
+ if (!base::StringToInt(tokens[0], &view_id) || view_id < 0 ||
+ !base::StringToInt(tokens[1], &rid) || rid < 0)
+ return false;
+
+ *view_id_out = view_id;
+ *rid_out = rid;
+ return true;
+}
+
+// Takes a favicon |url| that looks like:
+//
+// chrome-search://favicon/<view_id>/<restricted_id>
+// chrome-search://favicon/<parameters>/<view_id>/<restricted_id>
+//
+// If successful, assigns |*param_part| := "" or "<parameters>/" (note trailing
+// slash), |*view_id| := "<view_id>", |*rid| := "rid", and returns true.
+bool ParseIconRestrictedUrl(const GURL& url,
+ std::string* param_part,
+ int* view_id,
+ InstantRestrictedID* rid) {
+ DCHECK(param_part);
+ DCHECK(view_id);
+ DCHECK(rid);
+ // Strip leading slash.
+ std::string raw_path = url.path();
+ DCHECK_GT(raw_path.length(), (size_t) 0);
+ DCHECK_EQ(raw_path[0], '/');
+ raw_path = raw_path.substr(1);
+
+ // Get the starting index of the page URL.
+ chrome::ParsedFaviconPath parsed;
+ if (!chrome::ParseFaviconPath(
+ raw_path, chrome::FaviconUrlFormat::kFaviconLegacy, &parsed)) {
+ return false;
+ }
+ int path_index = parsed.path_index;
+
+ std::string id_part = raw_path.substr(path_index);
+ if (!ParseViewIdAndRestrictedId(id_part, view_id, rid))
+ return false;
+
+ *param_part = raw_path.substr(0, path_index);
+ return true;
+}
+
+void TranslateIconRestrictedUrl(const GURL& transient_url,
+ const SearchBox::IconURLHelper& helper,
+ GURL* url) {
+ std::string params;
+ int view_id = -1;
+ InstantRestrictedID rid = -1;
+
+ if (!internal::ParseIconRestrictedUrl(transient_url, &params, &view_id,
+ &rid) ||
+ view_id != helper.GetViewID()) {
+ *url = GURL(base::StringPrintf("chrome-search://%s/",
+ chrome::kChromeUIFaviconHost));
+ } else {
+ std::string item_url = helper.GetURLStringFromRestrictedID(rid);
+ *url = GURL(base::StringPrintf("chrome-search://%s/%s%s",
+ chrome::kChromeUIFaviconHost, params.c_str(),
+ item_url.c_str()));
+ }
+}
+
+std::string FixupAndValidateUrl(const std::string& url) {
+ GURL gurl = url_formatter::FixupURL(url, /*desired_tld=*/std::string());
+ if (!gurl.is_valid())
+ return std::string();
+
+ // Unless "http" was specified, replaces FixupURL's default "http" with
+ // "https".
+ if (url.find(std::string("http://")) == std::string::npos &&
+ gurl.SchemeIs(url::kHttpScheme)) {
+ GURL::Replacements replacements;
+ replacements.SetSchemeStr(url::kHttpsScheme);
+ gurl = gurl.ReplaceComponents(replacements);
+ }
+
+ return gurl.spec();
+}
+
+} // namespace internal
+
+SearchBox::IconURLHelper::IconURLHelper() = default;
+
+SearchBox::IconURLHelper::~IconURLHelper() = default;
+
+SearchBox::SearchBox(content::RenderFrame* render_frame)
+ : content::RenderFrameObserver(render_frame),
+ content::RenderFrameObserverTracker<SearchBox>(render_frame),
+ binding_(this),
+ can_run_js_in_renderframe_(false),
+ page_seq_no_(0),
+ is_focused_(false),
+ is_input_in_progress_(false),
+ is_key_capture_enabled_(false),
+ most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize),
+ has_received_most_visited_(false) {
+ // Connect to the embedded search interface in the browser.
+ mojo::AssociatedRemote<chrome::mojom::EmbeddedSearchConnector> connector;
+ render_frame->GetRemoteAssociatedInterfaces()->GetInterface(&connector);
+ chrome::mojom::EmbeddedSearchClientAssociatedPtrInfo embedded_search_client;
+ binding_.Bind(mojo::MakeRequest(&embedded_search_client));
+ connector->Connect(mojo::MakeRequest(&embedded_search_service_),
+ std::move(embedded_search_client));
+}
+
+SearchBox::~SearchBox() = default;
+
+void SearchBox::LogEvent(NTPLoggingEventType event) {
+ base::Time navigation_start = base::Time::FromDoubleT(
+ render_frame()->GetWebFrame()->Performance().NavigationStart());
+ base::Time now = base::Time::Now();
+ base::TimeDelta delta = now - navigation_start;
+ embedded_search_service_->LogEvent(page_seq_no_, event, delta);
+}
+
+void SearchBox::LogSuggestionEventWithValue(
+ NTPSuggestionsLoggingEventType event,
+ int data) {
+ base::Time navigation_start = base::Time::FromDoubleT(
+ render_frame()->GetWebFrame()->Performance().NavigationStart());
+ base::Time now = base::Time::Now();
+ base::TimeDelta delta = now - navigation_start;
+ embedded_search_service_->LogSuggestionEventWithValue(page_seq_no_, event,
+ data, delta);
+}
+
+void SearchBox::LogMostVisitedImpression(
+ const ntp_tiles::NTPTileImpression& impression) {
+ embedded_search_service_->LogMostVisitedImpression(page_seq_no_, impression);
+}
+
+void SearchBox::LogMostVisitedNavigation(
+ const ntp_tiles::NTPTileImpression& impression) {
+ embedded_search_service_->LogMostVisitedNavigation(page_seq_no_, impression);
+}
+
+void SearchBox::DeleteMostVisitedItem(
+ InstantRestrictedID most_visited_item_id) {
+ GURL url = GetURLForMostVisitedItem(most_visited_item_id);
+ if (!url.is_valid())
+ return;
+ embedded_search_service_->DeleteMostVisitedItem(page_seq_no_, url);
+}
+
+void SearchBox::GenerateImageURLFromTransientURL(const GURL& transient_url,
+ GURL* url) const {
+ SearchBoxIconURLHelper helper(this);
+ internal::TranslateIconRestrictedUrl(transient_url, helper, url);
+}
+
+void SearchBox::GetMostVisitedItems(
+ std::vector<InstantMostVisitedItemIDPair>* items) const {
+ most_visited_items_cache_.GetCurrentItems(items);
+}
+
+bool SearchBox::AreMostVisitedItemsAvailable() const {
+ return has_received_most_visited_;
+}
+
+bool SearchBox::GetMostVisitedItemWithID(
+ InstantRestrictedID most_visited_item_id,
+ InstantMostVisitedItem* item) const {
+ return most_visited_items_cache_.GetItemWithRestrictedID(most_visited_item_id,
+ item);
+}
+
+const ThemeBackgroundInfo* SearchBox::GetThemeBackgroundInfo() const {
+ return base::OptionalOrNullptr(theme_info_);
+}
+
+void SearchBox::Paste(const base::string16& text) {
+ embedded_search_service_->PasteAndOpenDropdown(page_seq_no_, text);
+}
+
+void SearchBox::StartCapturingKeyStrokes() {
+ embedded_search_service_->FocusOmnibox(page_seq_no_, OMNIBOX_FOCUS_INVISIBLE);
+}
+
+void SearchBox::StopCapturingKeyStrokes() {
+ embedded_search_service_->FocusOmnibox(page_seq_no_, OMNIBOX_FOCUS_NONE);
+}
+
+void SearchBox::UndoAllMostVisitedDeletions() {
+ embedded_search_service_->UndoAllMostVisitedDeletions(page_seq_no_);
+}
+
+void SearchBox::UndoMostVisitedDeletion(
+ InstantRestrictedID most_visited_item_id) {
+ GURL url = GetURLForMostVisitedItem(most_visited_item_id);
+ if (!url.is_valid())
+ return;
+ embedded_search_service_->UndoMostVisitedDeletion(page_seq_no_, url);
+}
+
+bool SearchBox::IsCustomLinks() const {
+ return most_visited_info_.items_are_custom_links;
+}
+
+bool SearchBox::IsUsingMostVisited() const {
+ return most_visited_info_.use_most_visited;
+}
+
+bool SearchBox::AreShortcutsVisible() const {
+ return most_visited_info_.is_visible;
+}
+
+void SearchBox::AddCustomLink(const GURL& url, const std::string& title) {
+ if (!url.is_valid()) {
+ AddCustomLinkResult(false);
+ return;
+ }
+ embedded_search_service_->AddCustomLink(
+ page_seq_no_, url, title,
+ base::BindOnce(&SearchBox::AddCustomLinkResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SearchBox::UpdateCustomLink(InstantRestrictedID link_id,
+ const GURL& new_url,
+ const std::string& new_title) {
+ GURL url = GetURLForMostVisitedItem(link_id);
+ if (!url.is_valid()) {
+ UpdateCustomLinkResult(false);
+ return;
+ }
+ embedded_search_service_->UpdateCustomLink(
+ page_seq_no_, url, new_url, new_title,
+ base::BindOnce(&SearchBox::UpdateCustomLinkResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SearchBox::ReorderCustomLink(InstantRestrictedID link_id, int new_pos) {
+ GURL url = GetURLForMostVisitedItem(link_id);
+ if (!url.is_valid())
+ return;
+ embedded_search_service_->ReorderCustomLink(page_seq_no_, url, new_pos);
+}
+
+void SearchBox::DeleteCustomLink(InstantRestrictedID most_visited_item_id) {
+ GURL url = GetURLForMostVisitedItem(most_visited_item_id);
+ if (!url.is_valid()) {
+ DeleteCustomLinkResult(false);
+ return;
+ }
+ embedded_search_service_->DeleteCustomLink(
+ page_seq_no_, url,
+ base::BindOnce(&SearchBox::DeleteCustomLinkResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SearchBox::UndoCustomLinkAction() {
+ embedded_search_service_->UndoCustomLinkAction(page_seq_no_);
+}
+
+void SearchBox::ResetCustomLinks() {
+ embedded_search_service_->ResetCustomLinks(page_seq_no_);
+}
+
+void SearchBox::ToggleMostVisitedOrCustomLinks() {
+ embedded_search_service_->ToggleMostVisitedOrCustomLinks(page_seq_no_);
+}
+
+void SearchBox::ToggleShortcutsVisibility(bool do_notify) {
+ embedded_search_service_->ToggleShortcutsVisibility(page_seq_no_, do_notify);
+}
+
+std::string SearchBox::FixupAndValidateUrl(const std::string& url) const {
+ return internal::FixupAndValidateUrl(url);
+}
+
+void SearchBox::SetCustomBackgroundInfo(const GURL& background_url,
+ const std::string& attribution_line_1,
+ const std::string& attribution_line_2,
+ const GURL& action_url,
+ const std::string& collection_id) {
+ embedded_search_service_->SetCustomBackgroundInfo(
+ background_url, attribution_line_1, attribution_line_2, action_url,
+ collection_id);
+}
+
+void SearchBox::SelectLocalBackgroundImage() {
+ embedded_search_service_->SelectLocalBackgroundImage();
+}
+
+void SearchBox::BlocklistSearchSuggestion(int task_version, long task_id) {
+ embedded_search_service_->BlocklistSearchSuggestion(task_version, task_id);
+}
+
+void SearchBox::BlocklistSearchSuggestionWithHash(
+ int task_version,
+ long task_id,
+ const std::vector<uint8_t>& hash) {
+ embedded_search_service_->BlocklistSearchSuggestionWithHash(task_version,
+ task_id, hash);
+}
+
+void SearchBox::SearchSuggestionSelected(int task_version,
+ long task_id,
+ const std::vector<uint8_t>& hash) {
+ embedded_search_service_->SearchSuggestionSelected(task_version, task_id,
+ hash);
+}
+
+void SearchBox::OptOutOfSearchSuggestions() {
+ embedded_search_service_->OptOutOfSearchSuggestions();
+}
+
+void SearchBox::ApplyDefaultTheme() {
+ embedded_search_service_->ApplyDefaultTheme();
+}
+
+void SearchBox::ApplyAutogeneratedTheme(SkColor color) {
+ embedded_search_service_->ApplyAutogeneratedTheme(color);
+}
+
+void SearchBox::RevertThemeChanges() {
+ embedded_search_service_->RevertThemeChanges();
+}
+
+void SearchBox::ConfirmThemeChanges() {
+ embedded_search_service_->ConfirmThemeChanges();
+}
+
+void SearchBox::QueryAutocomplete(const base::string16& input) {
+ embedded_search_service_->QueryAutocomplete(
+ input, base::BindOnce(&SearchBox::QueryAutocompleteResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SearchBox::DeleteAutocompleteMatch(uint8_t line) {
+ embedded_search_service_->DeleteAutocompleteMatch(
+ line, base::BindOnce(&SearchBox::OnDeleteAutocompleteMatch,
+ base::Unretained(this)));
+}
+
+void SearchBox::StopAutocomplete(bool clear_result) {
+ embedded_search_service_->StopAutocomplete(clear_result);
+}
+
+void SearchBox::BlocklistPromo(const std::string& promo_id) {
+ embedded_search_service_->BlocklistPromo(promo_id);
+}
+
+void SearchBox::QueryAutocompleteResult(
+ chrome::mojom::AutocompleteResultPtr result) {
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchQueryAutocompleteResult(
+ render_frame()->GetWebFrame(), std::move(result));
+ }
+}
+
+void SearchBox::OnDeleteAutocompleteMatch(
+ chrome::mojom::DeleteAutocompleteMatchResultPtr result) {
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchDeleteAutocompleteMatchResult(
+ render_frame()->GetWebFrame(), std::move(result));
+ }
+}
+
+void SearchBox::SetPageSequenceNumber(int page_seq_no) {
+ page_seq_no_ = page_seq_no;
+}
+
+void SearchBox::FocusChanged(OmniboxFocusState new_focus_state,
+ OmniboxFocusChangeReason reason) {
+ bool key_capture_enabled = new_focus_state == OMNIBOX_FOCUS_INVISIBLE;
+ if (key_capture_enabled != is_key_capture_enabled_) {
+ // Tell the page if the key capture mode changed unless the focus state
+ // changed because of TYPING. This is because in that case, the browser
+ // hasn't really stopped capturing key strokes.
+ //
+ // (More practically, if we don't do this check, the page would receive
+ // onkeycapturechange before the corresponding onchange, and the page would
+ // have no way of telling whether the keycapturechange happened because of
+ // some actual user action or just because they started typing.)
+ if (reason != OMNIBOX_FOCUS_CHANGE_TYPING) {
+ is_key_capture_enabled_ = key_capture_enabled;
+ DVLOG(1) << render_frame() << " KeyCaptureChange";
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchKeyCaptureChange(
+ render_frame()->GetWebFrame());
+ }
+ }
+ }
+ bool is_focused = new_focus_state == OMNIBOX_FOCUS_VISIBLE;
+ if (is_focused != is_focused_) {
+ is_focused_ = is_focused;
+ DVLOG(1) << render_frame() << " FocusChange";
+ if (can_run_js_in_renderframe_)
+ SearchBoxExtension::DispatchFocusChange(render_frame()->GetWebFrame());
+ }
+}
+
+void SearchBox::AddCustomLinkResult(bool success) {
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchAddCustomLinkResult(
+ render_frame()->GetWebFrame(), success);
+ }
+}
+
+void SearchBox::UpdateCustomLinkResult(bool success) {
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchUpdateCustomLinkResult(
+ render_frame()->GetWebFrame(), success);
+ }
+}
+
+void SearchBox::DeleteCustomLinkResult(bool success) {
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchDeleteCustomLinkResult(
+ render_frame()->GetWebFrame(), success);
+ }
+}
+
+void SearchBox::MostVisitedInfoChanged(
+ const InstantMostVisitedInfo& most_visited_info) {
+ has_received_most_visited_ = true;
+ most_visited_info_.items_are_custom_links =
+ most_visited_info.items_are_custom_links;
+
+ std::vector<InstantMostVisitedItemIDPair> last_known_items;
+ GetMostVisitedItems(&last_known_items);
+
+ if (AreMostVisitedItemsEqual(last_known_items, most_visited_info.items) &&
+ most_visited_info_.use_most_visited ==
+ most_visited_info.use_most_visited &&
+ most_visited_info_.is_visible == most_visited_info.is_visible) {
+ return; // Do not send duplicate onmostvisitedchange events.
+ }
+
+ most_visited_info_.use_most_visited = most_visited_info.use_most_visited;
+ most_visited_info_.is_visible = most_visited_info.is_visible;
+
+ most_visited_items_cache_.AddItems(most_visited_info.items);
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchMostVisitedChanged(
+ render_frame()->GetWebFrame());
+ }
+}
+
+void SearchBox::SetInputInProgress(bool is_input_in_progress) {
+ if (is_input_in_progress_ == is_input_in_progress)
+ return;
+
+ is_input_in_progress_ = is_input_in_progress;
+ DVLOG(1) << render_frame() << " SetInputInProgress";
+ if (can_run_js_in_renderframe_) {
+ if (is_input_in_progress_)
+ SearchBoxExtension::DispatchInputStart(render_frame()->GetWebFrame());
+ else
+ SearchBoxExtension::DispatchInputCancel(render_frame()->GetWebFrame());
+ }
+}
+
+void SearchBox::ThemeChanged(const ThemeBackgroundInfo& theme_info) {
+ // Do not send duplicate notifications.
+ if (theme_info_ == theme_info)
+ return;
+
+ theme_info_ = theme_info;
+ if (can_run_js_in_renderframe_)
+ SearchBoxExtension::DispatchThemeChange(render_frame()->GetWebFrame());
+}
+
+void SearchBox::LocalBackgroundSelected() {
+ if (can_run_js_in_renderframe_) {
+ SearchBoxExtension::DispatchLocalBackgroundSelected(
+ render_frame()->GetWebFrame());
+ }
+}
+
+GURL SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id) const {
+ InstantMostVisitedItem item;
+ return GetMostVisitedItemWithID(item_id, &item) ? item.url : GURL();
+}
+
+void SearchBox::DidCommitProvisionalLoad(bool is_same_document_navigation,
+ ui::PageTransition transition) {
+ can_run_js_in_renderframe_ = true;
+}
+
+void SearchBox::OnDestruct() {
+ delete this;
+}
diff --git a/chromium/chrome/renderer/searchbox/searchbox.h b/chromium/chrome/renderer/searchbox/searchbox.h
new file mode 100644
index 00000000000..c7755aab6aa
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/searchbox.h
@@ -0,0 +1,274 @@
+// Copyright 2012 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_RENDERER_SEARCHBOX_SEARCHBOX_H_
+#define CHROME_RENDERER_SEARCHBOX_SEARCHBOX_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "chrome/common/search.mojom.h"
+#include "chrome/common/search/instant_types.h"
+#include "chrome/common/search/ntp_logging_events.h"
+#include "chrome/renderer/instant_restricted_id_cache.h"
+#include "components/ntp_tiles/ntp_tile_impression.h"
+#include "components/omnibox/common/omnibox_focus_state.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "content/public/renderer/render_frame_observer_tracker.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "url/gurl.h"
+
+// The renderer-side implementation of the embeddedSearch API (see
+// https://www.chromium.org/embeddedsearch).
+class SearchBox : public content::RenderFrameObserver,
+ public content::RenderFrameObserverTracker<SearchBox>,
+ public chrome::mojom::EmbeddedSearchClient {
+ public:
+ // Helper class for GenerateImageURLFromTransientURL() to adapt SearchBox's
+ // instance, thereby allow mocking for unit tests.
+ class IconURLHelper {
+ public:
+ IconURLHelper();
+ virtual ~IconURLHelper();
+ // Retruns view id for validating icon URL.
+ virtual int GetViewID() const = 0;
+ // Returns the page URL string for |rid|, or empty string for invalid |rid|.
+ virtual std::string GetURLStringFromRestrictedID(InstantRestrictedID rid)
+ const = 0;
+ };
+
+ explicit SearchBox(content::RenderFrame* render_frame);
+ ~SearchBox() override;
+
+ // Sends LogEvent to the browser.
+ void LogEvent(NTPLoggingEventType event);
+
+ // Sends LogSuggestionEventWithValue to the browser.
+ void LogSuggestionEventWithValue(NTPSuggestionsLoggingEventType event,
+ int data);
+
+ // Sends LogMostVisitedImpression to the browser.
+ void LogMostVisitedImpression(const ntp_tiles::NTPTileImpression& impression);
+
+ // Sends LogMostVisitedNavigation to the browser.
+ void LogMostVisitedNavigation(const ntp_tiles::NTPTileImpression& impression);
+
+ // Sends DeleteMostVisitedItem to the browser.
+ void DeleteMostVisitedItem(InstantRestrictedID most_visited_item_id);
+
+ // Generates the image URL of the most visited item favicon specified by
+ // |transient_url|. If |transient_url| is valid, |url| is set with a
+ // translated URL. Otherwise, |url| is set the the default favicon
+ // ("chrome-search://favicon/").
+ //
+ // Valid forms of |transient_url|:
+ // chrome-search://favicon/<view_id>/<restricted_id>
+ // chrome-search://favicon/<favicon_parameters>/<view_id>/<restricted_id>
+ //
+ // We do this to prevent search providers from abusing image URLs and deduce
+ // whether the user has visited a particular page. For example, if
+ // "chrome-search://favicon/http://www.secretsite.com" is accessible, then
+ // the search provider can use its return code to determine whether the user
+ // has visited "http://www.secretsite.com". Therefore we require search
+ // providers to specify URL by "<view_id>/<restricted_id>". We then translate
+ // this to the original |url|, and pass the request to the proper endpoint.
+ void GenerateImageURLFromTransientURL(const GURL& transient_url,
+ GURL* url) const;
+
+ // Returns the latest most visited items sent by the browser.
+ void GetMostVisitedItems(
+ std::vector<InstantMostVisitedItemIDPair>* items) const;
+
+ bool AreMostVisitedItemsAvailable() const;
+
+ // If the |most_visited_item_id| is found in the cache, sets |item| to it
+ // and returns true.
+ bool GetMostVisitedItemWithID(InstantRestrictedID most_visited_item_id,
+ InstantMostVisitedItem* item) const;
+
+ // Sends PasteAndOpenDropdown to the browser.
+ void Paste(const base::string16& text);
+
+ // Will return null if the theme info hasn't been set yet.
+ const ThemeBackgroundInfo* GetThemeBackgroundInfo() const;
+
+ // Sends FocusOmnibox(OMNIBOX_FOCUS_INVISIBLE) to the browser.
+ void StartCapturingKeyStrokes();
+
+ // Sends FocusOmnibox(OMNIBOX_FOCUS_NONE) to the browser.
+ void StopCapturingKeyStrokes();
+
+ // Sends UndoAllMostVisitedDeletions to the browser.
+ void UndoAllMostVisitedDeletions();
+
+ // Sends UndoMostVisitedDeletion to the browser.
+ void UndoMostVisitedDeletion(InstantRestrictedID most_visited_item_id);
+
+ // Returns true if the most visited items are custom links.
+ bool IsCustomLinks() const;
+
+ // Returns true if most visited is enabled instead of custom links.
+ bool IsUsingMostVisited() const;
+
+ // Returns true if the shortcuts are visible and not hidden by the user.
+ bool AreShortcutsVisible() const;
+
+ // Sends AddCustomLink to the browser.
+ void AddCustomLink(const GURL& url, const std::string& title);
+
+ // Sends UpdateCustomLink to the browser.
+ void UpdateCustomLink(InstantRestrictedID link_id,
+ const GURL& new_url,
+ const std::string& new_title);
+
+ // Sends ReorderCustomLink to the browser.
+ void ReorderCustomLink(InstantRestrictedID link_id, int new_pos);
+
+ // Sends DeleteCustomLink to the browser.
+ void DeleteCustomLink(InstantRestrictedID most_visited_item_id);
+
+ // Sends UndoCustomLinkAction to the browser.
+ void UndoCustomLinkAction();
+
+ // Sends ResetCustomLinks to the browser.
+ void ResetCustomLinks();
+
+ // Sends ToggleMostVisitedOrCustomLinks to the browser.
+ void ToggleMostVisitedOrCustomLinks();
+
+ // Sends ToggleShortcutsVisibility to the browser.
+ void ToggleShortcutsVisibility(bool do_notify);
+
+ // Attempts to fix obviously invalid URLs. Uses the "https" scheme unless
+ // otherwise specified. Returns the fixed URL if valid, otherwise returns an
+ // empty string.
+ std::string FixupAndValidateUrl(const std::string& url) const;
+
+ // Updates the NTP custom background preferences, sometimes this includes
+ // image attributions.
+ void SetCustomBackgroundInfo(const GURL& background_url,
+ const std::string& attribution_line_1,
+ const std::string& attribution_line_2,
+ const GURL& action_url,
+ const std::string& collection_id);
+
+ // Let the user select a local file for the NTP background.
+ void SelectLocalBackgroundImage();
+
+ // Add a search suggestion task id to the blocklist.
+ void BlocklistSearchSuggestion(int task_version, long task_id);
+
+ // Add a search suggestion task id and hash to the blocklist.
+ void BlocklistSearchSuggestionWithHash(int task_version,
+ long task_id,
+ const std::vector<uint8_t>& hash);
+
+ // A suggestion collected, issue a new request with the suggestion
+ // temporarily added to the blocklist.
+ void SearchSuggestionSelected(int task_version,
+ long task_id,
+ const std::vector<uint8_t>& hash);
+
+ // Opts the user out of receiving search suggestions.
+ void OptOutOfSearchSuggestions();
+
+ // Applies the default theme.
+ void ApplyDefaultTheme();
+
+ // Applies autogenerated theme for the given color.
+ void ApplyAutogeneratedTheme(SkColor color);
+
+ // Reverts applied theme changes.
+ void RevertThemeChanges();
+
+ // Confirms applied theme changes.
+ void ConfirmThemeChanges();
+
+ // Queries the autocomplete backend for realbox results for |input| as a
+ // search term. Handled by |QueryAutocompleteResult|.
+ void QueryAutocomplete(const base::string16& input);
+
+ // Deletes |AutocompleteMatch| by index of the result.
+ void DeleteAutocompleteMatch(uint8_t line);
+
+ // Cancels the current autocomplete query. Clears the result set if
+ // |clear_result| is true.
+ void StopAutocomplete(bool clear_result);
+
+ // Called when a user dismisses a promo.
+ void BlocklistPromo(const std::string& promo_id);
+
+ bool is_focused() const { return is_focused_; }
+ bool is_input_in_progress() const { return is_input_in_progress_; }
+ bool is_key_capture_enabled() const { return is_key_capture_enabled_; }
+
+ private:
+ // Overridden from content::RenderFrameObserver:
+ void DidCommitProvisionalLoad(bool is_same_document_navigation,
+ ui::PageTransition transition) override;
+ void OnDestruct() override;
+
+ // Overridden from chrome::mojom::EmbeddedSearchClient:
+ void SetPageSequenceNumber(int page_seq_no) override;
+ void FocusChanged(OmniboxFocusState new_focus_state,
+ OmniboxFocusChangeReason reason) override;
+ void MostVisitedInfoChanged(
+ const InstantMostVisitedInfo& most_visited_info) override;
+ void SetInputInProgress(bool input_in_progress) override;
+ void ThemeChanged(const ThemeBackgroundInfo& theme_info) override;
+ void LocalBackgroundSelected() override;
+
+ void AddCustomLinkResult(bool success);
+ void UpdateCustomLinkResult(bool success);
+ void DeleteCustomLinkResult(bool success);
+
+ // Returns the URL of the Most Visited item specified by the |item_id|.
+ GURL GetURLForMostVisitedItem(InstantRestrictedID item_id) const;
+
+ // Asynchronous callback for autocomplete query results. Sends to renderer.
+ void QueryAutocompleteResult(chrome::mojom::AutocompleteResultPtr result);
+
+ // Asynchronous callback for results of attempting to delete an autocomplete
+ // result.
+ void OnDeleteAutocompleteMatch(
+ chrome::mojom::DeleteAutocompleteMatchResultPtr result);
+
+ // The connection to the EmbeddedSearch service in the browser process.
+ chrome::mojom::EmbeddedSearchAssociatedPtr embedded_search_service_;
+ mojo::AssociatedBinding<chrome::mojom::EmbeddedSearchClient> binding_;
+
+ // Whether it's legal to execute JavaScript in |render_frame()|.
+ // This class may want to execute JS in response to IPCs (via the
+ // SearchBoxExtension::Dispatch* methods). However, for cross-process
+ // navigations, a "provisional frame" is created at first, and it's illegal
+ // to execute any JS in it before it is actually swapped in, i.e. before the
+ // navigation has committed. So this only gets set to true in
+ // RenderFrameObserver::DidCommitProvisionalLoad. See crbug.com/765101.
+ // Note: If crbug.com/794942 ever gets resolved, then it might be possible to
+ // move the mojo connection code from the ctor to DidCommitProvisionalLoad and
+ // avoid this bool.
+ bool can_run_js_in_renderframe_;
+
+ // The Instant state.
+ int page_seq_no_;
+ bool is_focused_;
+ bool is_input_in_progress_;
+ bool is_key_capture_enabled_;
+ InstantRestrictedIDCache<InstantMostVisitedItem> most_visited_items_cache_;
+ // Use |most_visited_items_cache_| instead of |most_visited_info_.items| when
+ // comparing most visited items.
+ InstantMostVisitedInfo most_visited_info_;
+ bool has_received_most_visited_;
+ base::Optional<ThemeBackgroundInfo> theme_info_;
+
+ base::WeakPtrFactory<SearchBox> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(SearchBox);
+};
+
+#endif // CHROME_RENDERER_SEARCHBOX_SEARCHBOX_H_
diff --git a/chromium/chrome/renderer/searchbox/searchbox_extension.cc b/chromium/chrome/renderer/searchbox/searchbox_extension.cc
new file mode 100644
index 00000000000..5412c5566fd
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/searchbox_extension.cc
@@ -0,0 +1,1469 @@
+// Copyright 2012 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/renderer/searchbox/searchbox_extension.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "base/i18n/rtl.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "chrome/common/search/generated_colors_info.h"
+#include "chrome/common/search/instant_types.h"
+#include "chrome/common/search/ntp_logging_events.h"
+#include "chrome/common/search/selected_colors_info.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/renderer_resources.h"
+#include "chrome/renderer/searchbox/searchbox.h"
+#include "components/crx_file/id_util.h"
+#include "components/ntp_tiles/features.h"
+#include "components/ntp_tiles/ntp_tile_impression.h"
+#include "components/ntp_tiles/tile_source.h"
+#include "components/ntp_tiles/tile_visual_type.h"
+#include "content/public/renderer/chrome_object_extensions_utils.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "gin/data_object_builder.h"
+#include "gin/handle.h"
+#include "gin/object_template_builder.h"
+#include "gin/wrappable.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_script_source.h"
+#include "third_party/blink/public/web/web_view.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/window_open_disposition.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/gfx/text_elider.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+#include "v8/include/v8.h"
+
+namespace {
+
+const char kCSSBackgroundImageFormat[] = "-webkit-image-set("
+ "url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND?%s) 1x, "
+ "url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND@2x?%s) 2x)";
+
+const char kCSSBackgroundPositionCenter[] = "center";
+const char kCSSBackgroundPositionLeft[] = "left";
+const char kCSSBackgroundPositionTop[] = "top";
+const char kCSSBackgroundPositionRight[] = "right";
+const char kCSSBackgroundPositionBottom[] = "bottom";
+
+const char kCSSBackgroundRepeatNo[] = "no-repeat";
+const char kCSSBackgroundRepeatX[] = "repeat-x";
+const char kCSSBackgroundRepeatY[] = "repeat-y";
+const char kCSSBackgroundRepeat[] = "repeat";
+
+const char kThemeAttributionFormat[] = "-webkit-image-set("
+ "url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION?%s) 1x, "
+ "url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION@2x?%s) 2x)";
+
+const char kLTRHtmlTextDirection[] = "ltr";
+const char kRTLHtmlTextDirection[] = "rtl";
+
+// Max character limit for custom link titles.
+const size_t kMaxCustomLinkTitleLength = 150;
+
+void Dispatch(blink::WebLocalFrame* frame, const blink::WebString& script) {
+ if (!frame)
+ return;
+ frame->ExecuteScript(blink::WebScriptSource(script));
+}
+
+// Populates a Javascript MostVisitedItem object for returning from
+// newTabPage.mostVisited. This does not include private data such as "url" or
+// "title".
+v8::Local<v8::Object> GenerateMostVisitedItem(
+ v8::Isolate* isolate,
+ float device_pixel_ratio,
+ int render_view_id,
+ InstantRestrictedID restricted_id) {
+ return gin::DataObjectBuilder(isolate)
+ .Set("rid", restricted_id)
+ .Set("faviconUrl", base::StringPrintf(
+ "chrome-search://favicon/size/16@%fx/%d/%d",
+ device_pixel_ratio, render_view_id, restricted_id))
+ .Build();
+}
+
+// Populates a Javascript MostVisitedItem object appropriate for returning from
+// newTabPage.getMostVisitedItemData.
+// NOTE: Includes private data such as "url", "title", and "domain", so this
+// should not be returned to the host page (via newTabPage.mostVisited). It is
+// only accessible to most-visited iframes via getMostVisitedItemData.
+v8::Local<v8::Object> GenerateMostVisitedItemData(
+ v8::Isolate* isolate,
+ int render_view_id,
+ InstantRestrictedID restricted_id,
+ const InstantMostVisitedItem& mv_item) {
+ // We set the "dir" attribute of the title, so that in RTL locales, a LTR
+ // title is rendered left-to-right and truncated from the right. For
+ // example, the title of http://msdn.microsoft.com/en-us/default.aspx is
+ // "MSDN: Microsoft developer network". In RTL locales, in the New Tab
+ // page, if the "dir" of this title is not specified, it takes Chrome UI's
+ // directionality. So the title will be truncated as "soft developer
+ // network". Setting the "dir" attribute as "ltr" renders the truncated
+ // title as "MSDN: Microsoft D...". As another example, the title of
+ // http://yahoo.com is "Yahoo!". In RTL locales, in the New Tab page, the
+ // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
+ // "ltr".
+ const char* direction;
+ if (base::i18n::GetFirstStrongCharacterDirection(mv_item.title) ==
+ base::i18n::RIGHT_TO_LEFT) {
+ direction = kRTLHtmlTextDirection;
+ } else {
+ direction = kLTRHtmlTextDirection;
+ }
+
+ std::string title = base::UTF16ToUTF8(mv_item.title);
+ if (title.empty())
+ title = mv_item.url.spec();
+
+ gin::DataObjectBuilder builder(isolate);
+ builder.Set("renderViewId", render_view_id)
+ .Set("rid", restricted_id)
+ .Set("tileTitleSource", static_cast<int>(mv_item.title_source))
+ .Set("tileSource", static_cast<int>(mv_item.source))
+ .Set("title", title)
+ .Set("domain", mv_item.url.host())
+ .Set("direction", base::StringPiece(direction))
+ .Set("url", mv_item.url.spec())
+ .Set("dataGenerationTime",
+ mv_item.data_generation_time.is_null()
+ ? v8::Local<v8::Value>(v8::Null(isolate))
+ : v8::Date::New(isolate->GetCurrentContext(),
+ mv_item.data_generation_time.ToJsTime())
+ .ToLocalChecked());
+
+ // If the suggestion already has a favicon, we populate the element with it.
+ if (!mv_item.favicon.spec().empty())
+ builder.Set("faviconUrl", mv_item.favicon.spec());
+
+ return builder.Build();
+}
+
+base::Time ConvertDateValueToTime(v8::Value* value) {
+ DCHECK(value);
+ if (value->IsNull() || !value->IsDate())
+ return base::Time();
+
+ return base::Time::FromJsTime(v8::Date::Cast(value)->ValueOf());
+}
+
+base::Optional<int> CoerceToInt(v8::Isolate* isolate, v8::Value* value) {
+ DCHECK(value);
+ v8::MaybeLocal<v8::Int32> maybe_int =
+ value->ToInt32(isolate->GetCurrentContext());
+ if (maybe_int.IsEmpty())
+ return base::nullopt;
+ return maybe_int.ToLocalChecked()->Value();
+}
+
+// Returns an array with the RGBA color components.
+v8::Local<v8::Value> SkColorToArray(v8::Isolate* isolate,
+ const SkColor& color) {
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Array> color_array = v8::Array::New(isolate, 4);
+ color_array
+ ->CreateDataProperty(context, 0,
+ v8::Int32::New(isolate, SkColorGetR(color)))
+ .Check();
+ color_array
+ ->CreateDataProperty(context, 1,
+ v8::Int32::New(isolate, SkColorGetG(color)))
+ .Check();
+ color_array
+ ->CreateDataProperty(context, 2,
+ v8::Int32::New(isolate, SkColorGetB(color)))
+ .Check();
+ color_array
+ ->CreateDataProperty(context, 3,
+ v8::Int32::New(isolate, SkColorGetA(color)))
+ .Check();
+ return color_array;
+}
+
+// Converts given array to SkColor and returns whether the conversion is
+// successful.
+bool ArrayToSkColor(v8::Isolate* isolate,
+ v8::Local<v8::Array> color,
+ SkColor* color_result) {
+ if (color->Length() != 4)
+ return false;
+
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Value> r_value;
+ v8::Local<v8::Value> g_value;
+ v8::Local<v8::Value> b_value;
+ v8::Local<v8::Value> a_value;
+
+ if (!color->Get(context, 0).ToLocal(&r_value) ||
+ !color->Get(context, 1).ToLocal(&g_value) ||
+ !color->Get(context, 2).ToLocal(&b_value) ||
+ !color->Get(context, 3).ToLocal(&a_value))
+ return false;
+
+ base::Optional<int> r = CoerceToInt(isolate, *r_value);
+ base::Optional<int> g = CoerceToInt(isolate, *g_value);
+ base::Optional<int> b = CoerceToInt(isolate, *b_value);
+ base::Optional<int> a = CoerceToInt(isolate, *a_value);
+
+ if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
+ return false;
+
+ if (*a > 255 || *r > 255 || *g > 255 || *b > 255)
+ return false;
+
+ *color_result = SkColorSetARGB(*a, *r, *g, *b);
+ return true;
+}
+
+v8::Local<v8::Object> GenerateThemeBackgroundInfo(
+ v8::Isolate* isolate,
+ const ThemeBackgroundInfo& theme_info) {
+ gin::DataObjectBuilder builder(isolate);
+
+ // True if the theme is the system default and no custom theme has been
+ // applied.
+ // Value is always valid.
+ builder.Set("usingDefaultTheme", theme_info.using_default_theme);
+
+ // Theme color for background as an array with the RGBA components in order.
+ // Value is always valid.
+ builder.Set("backgroundColorRgba",
+ SkColorToArray(isolate, theme_info.background_color));
+
+ // Theme color for text as an array with the RGBA components in order.
+ // Value is always valid.
+ builder.Set("textColorRgba", SkColorToArray(isolate, theme_info.text_color));
+
+ // Theme color for light text as an array with the RGBA components in order.
+ // Value is always valid.
+ builder.Set("textColorLightRgba",
+ SkColorToArray(isolate, theme_info.text_color_light));
+
+ // The theme alternate logo value indicates same color when TRUE and a
+ // colorful one when FALSE.
+ builder.Set("alternateLogo", theme_info.logo_alternate);
+
+ // The theme background image url is of format kCSSBackgroundImageFormat
+ // where both instances of "%s" are replaced with the id that identifies the
+ // theme.
+ // This is the CSS "background-image" format.
+ // Value is only valid if there's a custom theme background image.
+ if (theme_info.has_theme_image) {
+ builder.Set("imageUrl", base::StringPrintf(kCSSBackgroundImageFormat,
+ theme_info.theme_id.c_str(),
+ theme_info.theme_id.c_str()));
+
+ // The theme background image horizontal alignment is one of "left",
+ // "right", "center".
+ // This is the horizontal component of the CSS "background-position" format.
+ // Value is only valid if |imageUrl| is not empty.
+ std::string alignment = kCSSBackgroundPositionCenter;
+ if (theme_info.image_horizontal_alignment ==
+ THEME_BKGRND_IMAGE_ALIGN_LEFT) {
+ alignment = kCSSBackgroundPositionLeft;
+ } else if (theme_info.image_horizontal_alignment ==
+ THEME_BKGRND_IMAGE_ALIGN_RIGHT) {
+ alignment = kCSSBackgroundPositionRight;
+ }
+ builder.Set("imageHorizontalAlignment", alignment);
+
+ // The theme background image vertical alignment is one of "top", "bottom",
+ // "center".
+ // This is the vertical component of the CSS "background-position" format.
+ // Value is only valid if |image_url| is not empty.
+ if (theme_info.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) {
+ alignment = kCSSBackgroundPositionTop;
+ } else if (theme_info.image_vertical_alignment ==
+ THEME_BKGRND_IMAGE_ALIGN_BOTTOM) {
+ alignment = kCSSBackgroundPositionBottom;
+ } else {
+ alignment = kCSSBackgroundPositionCenter;
+ }
+ builder.Set("imageVerticalAlignment", alignment);
+
+ // The tiling of the theme background image is one of "no-repeat",
+ // "repeat-x", "repeat-y", "repeat".
+ // This is the CSS "background-repeat" format.
+ // Value is only valid if |image_url| is not empty.
+ std::string tiling = kCSSBackgroundRepeatNo;
+ switch (theme_info.image_tiling) {
+ case THEME_BKGRND_IMAGE_NO_REPEAT:
+ tiling = kCSSBackgroundRepeatNo;
+ break;
+ case THEME_BKGRND_IMAGE_REPEAT_X:
+ tiling = kCSSBackgroundRepeatX;
+ break;
+ case THEME_BKGRND_IMAGE_REPEAT_Y:
+ tiling = kCSSBackgroundRepeatY;
+ break;
+ case THEME_BKGRND_IMAGE_REPEAT:
+ tiling = kCSSBackgroundRepeat;
+ break;
+ }
+ builder.Set("imageTiling", tiling);
+
+ // The attribution URL is only valid if the theme has attribution logo.
+ if (theme_info.has_attribution) {
+ builder.Set("attributionUrl",
+ base::StringPrintf(kThemeAttributionFormat,
+ theme_info.theme_id.c_str(),
+ theme_info.theme_id.c_str()));
+ }
+ }
+
+ builder.Set("themeId", theme_info.theme_id);
+ builder.Set("themeName", theme_info.theme_name);
+
+ builder.Set("customBackgroundConfigured",
+ !theme_info.custom_background_url.is_empty());
+
+ // If a custom background has been set provide the relevant information to the
+ // page.
+ if (!theme_info.custom_background_url.is_empty()) {
+ builder.Set("imageUrl", theme_info.custom_background_url.spec());
+ builder.Set("attributionActionUrl",
+ theme_info.custom_background_attribution_action_url.spec());
+ builder.Set("attribution1",
+ theme_info.custom_background_attribution_line_1);
+ builder.Set("attribution2",
+ theme_info.custom_background_attribution_line_2);
+ builder.Set("collectionId", theme_info.collection_id);
+ // Clear the theme attribution url, as it shouldn't be shown when
+ // a custom background is set.
+ builder.Set("attributionUrl", std::string());
+ }
+
+ // Set fields for themeing NTP elements.
+ builder.Set("isNtpBackgroundDark",
+ !color_utils::IsDark(theme_info.text_color));
+ builder.Set("useTitleContainer", theme_info.has_theme_image);
+
+ // TODO(gayane): Rename icon color to shortcut color in JS for consitancy.
+ builder.Set("iconBackgroundColor",
+ SkColorToArray(isolate, theme_info.shortcut_color));
+ builder.Set("useWhiteAddIcon",
+ color_utils::IsDark(theme_info.shortcut_color));
+
+ builder.Set("logoColor", SkColorToArray(isolate, theme_info.logo_color));
+
+ builder.Set("colorId", theme_info.color_id);
+ if (theme_info.color_id != -1) {
+ builder.Set("colorDark", SkColorToArray(isolate, theme_info.color_dark));
+ builder.Set("colorLight", SkColorToArray(isolate, theme_info.color_light));
+ builder.Set("colorPicked",
+ SkColorToArray(isolate, theme_info.color_picked));
+ }
+
+ return builder.Build();
+}
+
+content::RenderFrame* GetMainRenderFrameForCurrentContext() {
+ blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForCurrentContext();
+ if (!frame)
+ return nullptr;
+ content::RenderFrame* main_frame =
+ content::RenderFrame::FromWebFrame(frame->LocalRoot());
+ if (!main_frame || !main_frame->IsMainFrame())
+ return nullptr;
+ return main_frame;
+}
+
+SearchBox* GetSearchBoxForCurrentContext() {
+ content::RenderFrame* main_frame = GetMainRenderFrameForCurrentContext();
+ if (!main_frame)
+ return nullptr;
+ return SearchBox::Get(main_frame);
+}
+
+base::Value CreateAutocompleteMatches(
+ const std::vector<chrome::mojom::AutocompleteMatchPtr>& matches) {
+ base::Value list(base::Value::Type::LIST);
+ for (const chrome::mojom::AutocompleteMatchPtr& match : matches) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetBoolKey("allowedToBeDefaultMatch",
+ match->allowed_to_be_default_match);
+ dict.SetStringKey("contents", match->contents);
+ base::Value contents_class(base::Value::Type::LIST);
+ for (const auto& classification : match->contents_class) {
+ base::Value entry(base::Value::Type::DICTIONARY);
+ entry.SetIntKey("offset", classification->offset);
+ entry.SetIntKey("style", classification->style);
+ contents_class.Append(std::move(entry));
+ }
+ dict.SetKey("contentsClass", std::move(contents_class));
+ dict.SetStringKey("description", match->description);
+ base::Value description_class(base::Value::Type::LIST);
+ for (const auto& classification : match->description_class) {
+ base::Value entry(base::Value::Type::DICTIONARY);
+ entry.SetIntKey("offset", classification->offset);
+ entry.SetIntKey("style", classification->style);
+ description_class.Append(std::move(entry));
+ }
+ dict.SetKey("descriptionClass", std::move(description_class));
+ dict.SetStringKey("destinationUrl", match->destination_url);
+ dict.SetStringKey("inlineAutocompletion", match->inline_autocompletion);
+ dict.SetBoolKey("isSearchType", match->is_search_type);
+ dict.SetStringKey("fillIntoEdit", match->fill_into_edit);
+ dict.SetBoolKey("swapContentsAndDescription",
+ match->swap_contents_and_description);
+ dict.SetStringKey("type", match->type);
+ dict.SetBoolKey("supportsDeletion", match->supports_deletion);
+ list.Append(std::move(dict));
+ }
+ return list;
+}
+
+static const char kDispatchFocusChangedScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.searchBox &&"
+ " window.chrome.embeddedSearch.searchBox.onfocuschange &&"
+ " typeof window.chrome.embeddedSearch.searchBox.onfocuschange =="
+ " 'function') {"
+ " window.chrome.embeddedSearch.searchBox.onfocuschange();"
+ " true;"
+ "}";
+
+static const char kDispatchAddCustomLinkResult[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.onaddcustomlinkdone &&"
+ " typeof window.chrome.embeddedSearch.newTabPage"
+ " .onaddcustomlinkdone === 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.onaddcustomlinkdone(%s);"
+ " true;"
+ "}";
+
+static const char kDispatchUpdateCustomLinkResult[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.onupdatecustomlinkdone &&"
+ " typeof window.chrome.embeddedSearch.newTabPage"
+ " .onupdatecustomlinkdone === 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.onupdatecustomlinkdone(%s);"
+ " true;"
+ "}";
+
+static const char kDispatchQueryAutocompleteResult[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.searchBox &&"
+ " window.chrome.embeddedSearch.searchBox.onqueryautocompletedone &&"
+ " typeof window.chrome.embeddedSearch.searchBox"
+ " .onqueryautocompletedone === 'function') {"
+ " window.chrome.embeddedSearch.searchBox.onqueryautocompletedone(%s);"
+ " true;"
+ "}";
+
+static const char kDispatchDeleteAutocompleteMatchResult[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.searchBox &&"
+ " window.chrome.embeddedSearch.searchBox.ondeleteautocompletematch &&"
+ " typeof window.chrome.embeddedSearch.searchBox"
+ " .ondeleteautocompletematch === 'function') {"
+ " window.chrome.embeddedSearch.searchBox.ondeleteautocompletematch(%s);"
+ " true;"
+ "}";
+
+static const char kDispatchDeleteCustomLinkResult[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.ondeletecustomlinkdone &&"
+ " typeof window.chrome.embeddedSearch.newTabPage"
+ " .ondeletecustomlinkdone === 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.ondeletecustomlinkdone(%s);"
+ " true;"
+ "}";
+
+static const char kDispatchInputCancelScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.oninputcancel &&"
+ " typeof window.chrome.embeddedSearch.newTabPage.oninputcancel =="
+ " 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.oninputcancel();"
+ " true;"
+ "}";
+
+static const char kDispatchInputStartScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.oninputstart &&"
+ " typeof window.chrome.embeddedSearch.newTabPage.oninputstart =="
+ " 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.oninputstart();"
+ " true;"
+ "}";
+
+static const char kDispatchKeyCaptureChangeScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.searchBox &&"
+ " window.chrome.embeddedSearch.searchBox.onkeycapturechange &&"
+ " typeof window.chrome.embeddedSearch.searchBox.onkeycapturechange =="
+ " 'function') {"
+ " window.chrome.embeddedSearch.searchBox.onkeycapturechange();"
+ " true;"
+ "}";
+
+static const char kDispatchMostVisitedChangedScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange &&"
+ " typeof window.chrome.embeddedSearch.newTabPage.onmostvisitedchange =="
+ " 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange();"
+ " true;"
+ "}";
+
+static const char kDispatchThemeChangeEventScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.onthemechange &&"
+ " typeof window.chrome.embeddedSearch.newTabPage.onthemechange =="
+ " 'function') {"
+ " window.chrome.embeddedSearch.newTabPage.onthemechange();"
+ " true;"
+ "}";
+
+static const char kDispatchLocalBackgroundSelectedScript[] =
+ "if (window.chrome &&"
+ " window.chrome.embeddedSearch &&"
+ " window.chrome.embeddedSearch.newTabPage &&"
+ " window.chrome.embeddedSearch.newTabPage.onlocalbackgroundselected &&"
+ " typeof "
+ "window.chrome.embeddedSearch.newTabPage.onlocalbackgroundselected =="
+ " 'function') {"
+ " "
+ "window.chrome.embeddedSearch.newTabPage."
+ "onlocalbackgroundselected();"
+ " true;"
+ "}";
+
+// ----------------------------------------------------------------------------
+
+class SearchBoxBindings : public gin::Wrappable<SearchBoxBindings> {
+ public:
+ static gin::WrapperInfo kWrapperInfo;
+
+ SearchBoxBindings();
+ ~SearchBoxBindings() override;
+
+ private:
+ // gin::Wrappable.
+ gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+ v8::Isolate* isolate) final;
+
+ // Handlers for JS properties.
+ static bool IsFocused();
+ static bool IsKeyCaptureEnabled();
+
+ // Handlers for JS functions.
+ static void DeleteAutocompleteMatch(int line);
+ static void Paste(const std::string& text);
+ static void QueryAutocomplete(const base::string16& input);
+ static void StopAutocomplete(bool clear_result);
+ static void StartCapturingKeyStrokes();
+ static void StopCapturingKeyStrokes();
+
+ DISALLOW_COPY_AND_ASSIGN(SearchBoxBindings);
+};
+
+gin::WrapperInfo SearchBoxBindings::kWrapperInfo = {gin::kEmbedderNativeGin};
+
+SearchBoxBindings::SearchBoxBindings() = default;
+
+SearchBoxBindings::~SearchBoxBindings() = default;
+
+gin::ObjectTemplateBuilder SearchBoxBindings::GetObjectTemplateBuilder(
+ v8::Isolate* isolate) {
+ return gin::Wrappable<SearchBoxBindings>::GetObjectTemplateBuilder(isolate)
+ .SetProperty("rtl", &base::i18n::IsRTL)
+ .SetProperty("isFocused", &SearchBoxBindings::IsFocused)
+ .SetProperty("isKeyCaptureEnabled",
+ &SearchBoxBindings::IsKeyCaptureEnabled)
+ .SetMethod("deleteAutocompleteMatch",
+ &SearchBoxBindings::DeleteAutocompleteMatch)
+ .SetMethod("paste", &SearchBoxBindings::Paste)
+ .SetMethod("queryAutocomplete", &SearchBoxBindings::QueryAutocomplete)
+ .SetMethod("stopAutocomplete", &SearchBoxBindings::StopAutocomplete)
+ .SetMethod("startCapturingKeyStrokes",
+ &SearchBoxBindings::StartCapturingKeyStrokes)
+ .SetMethod("stopCapturingKeyStrokes",
+ &SearchBoxBindings::StopCapturingKeyStrokes);
+}
+
+// static
+bool SearchBoxBindings::IsFocused() {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return false;
+ return search_box->is_focused();
+}
+
+// static
+bool SearchBoxBindings::IsKeyCaptureEnabled() {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return false;
+ return search_box->is_key_capture_enabled();
+}
+
+// static
+void SearchBoxBindings::DeleteAutocompleteMatch(int line) {
+ DCHECK_GE(line, 0);
+ DCHECK_LE(line, 255);
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->DeleteAutocompleteMatch(line);
+}
+
+// static
+void SearchBoxBindings::Paste(const std::string& text) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->Paste(base::UTF8ToUTF16(text));
+}
+
+// static
+void SearchBoxBindings::QueryAutocomplete(const base::string16& input) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->QueryAutocomplete(input);
+}
+
+// static
+void SearchBoxBindings::StopAutocomplete(bool clear_result) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->StopAutocomplete(clear_result);
+}
+
+// static
+void SearchBoxBindings::StartCapturingKeyStrokes() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->StartCapturingKeyStrokes();
+}
+
+// static
+void SearchBoxBindings::StopCapturingKeyStrokes() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->StopCapturingKeyStrokes();
+}
+
+class NewTabPageBindings : public gin::Wrappable<NewTabPageBindings> {
+ public:
+ static gin::WrapperInfo kWrapperInfo;
+
+ NewTabPageBindings();
+ ~NewTabPageBindings() override;
+
+ private:
+ // gin::Wrappable.
+ gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+ v8::Isolate* isolate) final;
+
+ static bool HasOrigin(const GURL& origin);
+
+ // Handlers for JS properties.
+ static bool IsInputInProgress();
+ static v8::Local<v8::Value> GetMostVisited(v8::Isolate* isolate);
+ static bool GetMostVisitedAvailable(v8::Isolate* isolate);
+ static v8::Local<v8::Value> GetThemeBackgroundInfo(v8::Isolate* isolate);
+ static bool GetIsCustomLinks();
+ static bool GetIsUsingMostVisited();
+ static bool GetAreShortcutsVisible();
+
+ // Handlers for JS functions visible to all NTPs.
+ static void DeleteMostVisitedItem(v8::Isolate* isolate,
+ v8::Local<v8::Value> rid);
+ static void UndoAllMostVisitedDeletions();
+ static void UndoMostVisitedDeletion(v8::Isolate* isolate,
+ v8::Local<v8::Value> rid);
+
+ // Handlers for JS functions visible only to the most visited iframe, the edit
+ // custom links iframe, and/or the local NTP.
+ static v8::Local<v8::Value> GetMostVisitedItemData(v8::Isolate* isolate,
+ int rid);
+ static void UpdateCustomLink(int rid,
+ const std::string& url,
+ const std::string& title);
+ static void ReorderCustomLink(int rid, int new_pos);
+ static void UndoCustomLinkAction();
+ static void ResetCustomLinks();
+ static void ToggleMostVisitedOrCustomLinks();
+ static void ToggleShortcutsVisibility(bool do_notify);
+ static std::string FixupAndValidateUrl(const std::string& url);
+ static void LogEvent(int event);
+ static void LogSuggestionEventWithValue(int event, int data);
+ static void LogMostVisitedImpression(
+ int position,
+ int tile_title_source,
+ int tile_source,
+ int tile_type,
+ v8::Local<v8::Value> data_generation_time);
+ static void LogMostVisitedNavigation(
+ int position,
+ int tile_title_source,
+ int tile_source,
+ int tile_type,
+ v8::Local<v8::Value> data_generation_time);
+ static void ResetCustomBackgroundInfo();
+ static void SetCustomBackgroundInfo(const std::string& background_url,
+ const std::string& attribution_line_1,
+ const std::string& attribution_line_2,
+ const std::string& attributionActionUrl,
+ const std::string& collection_id);
+ static void SelectLocalBackgroundImage();
+ static void BlocklistSearchSuggestion(int task_version, int task_id);
+ static void BlocklistSearchSuggestionWithHash(int task_version,
+ int task_id,
+ const std::string& hash);
+ static void SearchSuggestionSelected(int task_version,
+ int task_id,
+ const std::string& hash);
+ static void OptOutOfSearchSuggestions();
+ static void UseDefaultTheme();
+ static void ApplyDefaultTheme();
+ static void ApplyAutogeneratedTheme(v8::Isolate* isolate,
+ int id,
+ v8::Local<v8::Value> color);
+ static void RevertThemeChanges();
+ static void ConfirmThemeChanges();
+ static void BlocklistPromo(const std::string& promo_id);
+ static v8::Local<v8::Value> GetColorsInfo(v8::Isolate* isolate);
+
+ DISALLOW_COPY_AND_ASSIGN(NewTabPageBindings);
+};
+
+gin::WrapperInfo NewTabPageBindings::kWrapperInfo = {gin::kEmbedderNativeGin};
+
+NewTabPageBindings::NewTabPageBindings() = default;
+
+NewTabPageBindings::~NewTabPageBindings() = default;
+
+gin::ObjectTemplateBuilder NewTabPageBindings::GetObjectTemplateBuilder(
+ v8::Isolate* isolate) {
+ return gin::Wrappable<NewTabPageBindings>::GetObjectTemplateBuilder(isolate)
+ .SetProperty("isInputInProgress", &NewTabPageBindings::IsInputInProgress)
+ .SetProperty("mostVisited", &NewTabPageBindings::GetMostVisited)
+ .SetProperty("mostVisitedAvailable",
+ &NewTabPageBindings::GetMostVisitedAvailable)
+ .SetProperty("themeBackgroundInfo",
+ &NewTabPageBindings::GetThemeBackgroundInfo)
+ .SetProperty("isCustomLinks", &NewTabPageBindings::GetIsCustomLinks)
+ .SetProperty("isUsingMostVisited",
+ &NewTabPageBindings::GetIsUsingMostVisited)
+ .SetProperty("areShortcutsVisible",
+ &NewTabPageBindings::GetAreShortcutsVisible)
+ .SetMethod("deleteMostVisitedItem",
+ &NewTabPageBindings::DeleteMostVisitedItem)
+ .SetMethod("undoAllMostVisitedDeletions",
+ &NewTabPageBindings::UndoAllMostVisitedDeletions)
+ .SetMethod("undoMostVisitedDeletion",
+ &NewTabPageBindings::UndoMostVisitedDeletion)
+ .SetMethod("getMostVisitedItemData",
+ &NewTabPageBindings::GetMostVisitedItemData)
+ .SetMethod("updateCustomLink", &NewTabPageBindings::UpdateCustomLink)
+ .SetMethod("reorderCustomLink", &NewTabPageBindings::ReorderCustomLink)
+ .SetMethod("undoCustomLinkAction",
+ &NewTabPageBindings::UndoCustomLinkAction)
+ .SetMethod("resetCustomLinks", &NewTabPageBindings::ResetCustomLinks)
+ .SetMethod("toggleMostVisitedOrCustomLinks",
+ &NewTabPageBindings::ToggleMostVisitedOrCustomLinks)
+ .SetMethod("toggleShortcutsVisibility",
+ &NewTabPageBindings::ToggleShortcutsVisibility)
+ .SetMethod("fixupAndValidateUrl",
+ &NewTabPageBindings::FixupAndValidateUrl)
+ .SetMethod("logEvent", &NewTabPageBindings::LogEvent)
+ .SetMethod("logSuggestionEventWithValue",
+ &NewTabPageBindings::LogSuggestionEventWithValue)
+ .SetMethod("logMostVisitedImpression",
+ &NewTabPageBindings::LogMostVisitedImpression)
+ .SetMethod("logMostVisitedNavigation",
+ &NewTabPageBindings::LogMostVisitedNavigation)
+ .SetMethod("resetBackgroundInfo",
+ &NewTabPageBindings::ResetCustomBackgroundInfo)
+ .SetMethod("setBackgroundInfo",
+ &NewTabPageBindings::SetCustomBackgroundInfo)
+ .SetMethod("selectLocalBackgroundImage",
+ &NewTabPageBindings::SelectLocalBackgroundImage)
+ .SetMethod("blacklistSearchSuggestion",
+ &NewTabPageBindings::BlocklistSearchSuggestion)
+ .SetMethod("blacklistSearchSuggestionWithHash",
+ &NewTabPageBindings::BlocklistSearchSuggestionWithHash)
+ .SetMethod("searchSuggestionSelected",
+ &NewTabPageBindings::SearchSuggestionSelected)
+ .SetMethod("optOutOfSearchSuggestions",
+ &NewTabPageBindings::OptOutOfSearchSuggestions)
+ .SetMethod("useDefaultTheme", &NewTabPageBindings::UseDefaultTheme)
+ .SetMethod("applyDefaultTheme", &NewTabPageBindings::ApplyDefaultTheme)
+ .SetMethod("applyAutogeneratedTheme",
+ &NewTabPageBindings::ApplyAutogeneratedTheme)
+ .SetMethod("revertThemeChanges", &NewTabPageBindings::RevertThemeChanges)
+ .SetMethod("confirmThemeChanges",
+ &NewTabPageBindings::ConfirmThemeChanges)
+ .SetMethod("getColorsInfo", &NewTabPageBindings::GetColorsInfo)
+ .SetMethod("blocklistPromo", &NewTabPageBindings::BlocklistPromo);
+}
+
+// static
+bool NewTabPageBindings::HasOrigin(const GURL& origin) {
+ blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForCurrentContext();
+ if (!frame)
+ return false;
+ GURL url(frame->GetDocument().Url());
+ return url.GetOrigin() == origin.GetOrigin();
+}
+
+// static
+bool NewTabPageBindings::IsInputInProgress() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return false;
+ return search_box->is_input_in_progress();
+}
+
+// static
+v8::Local<v8::Value> NewTabPageBindings::GetMostVisited(v8::Isolate* isolate) {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return v8::Null(isolate);
+
+ content::RenderFrame* render_frame = GetMainRenderFrameForCurrentContext();
+ content::RenderView* render_view = render_frame->GetRenderView();
+
+ // This corresponds to "window.devicePixelRatio" in JavaScript.
+ float zoom_factor =
+ blink::PageZoomLevelToZoomFactor(render_view->GetZoomLevel());
+ float device_pixel_ratio = render_frame->GetDeviceScaleFactor() * zoom_factor;
+
+ int render_view_id = render_view->GetRoutingID();
+
+ std::vector<InstantMostVisitedItemIDPair> instant_mv_items;
+ search_box->GetMostVisitedItems(&instant_mv_items);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> v8_mv_items =
+ v8::Array::New(isolate, instant_mv_items.size());
+ for (size_t i = 0; i < instant_mv_items.size(); ++i) {
+ InstantRestrictedID rid = instant_mv_items[i].first;
+ v8_mv_items
+ ->CreateDataProperty(
+ context, i,
+ GenerateMostVisitedItem(isolate, device_pixel_ratio, render_view_id,
+ rid))
+ .Check();
+ }
+ return v8_mv_items;
+}
+
+// static
+bool NewTabPageBindings::GetMostVisitedAvailable(v8::Isolate* isolate) {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return false;
+
+ return search_box->AreMostVisitedItemsAvailable();
+}
+
+// static
+v8::Local<v8::Value> NewTabPageBindings::GetThemeBackgroundInfo(
+ v8::Isolate* isolate) {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return v8::Null(isolate);
+ const ThemeBackgroundInfo* theme_info = search_box->GetThemeBackgroundInfo();
+ if (!theme_info)
+ return v8::Null(isolate);
+ return GenerateThemeBackgroundInfo(isolate, *theme_info);
+}
+
+// static
+bool NewTabPageBindings::GetIsCustomLinks() {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return false;
+
+ return search_box->IsCustomLinks();
+}
+
+// static
+bool NewTabPageBindings::GetIsUsingMostVisited() {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !(HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)) ||
+ HasOrigin(GURL(chrome::kChromeSearchLocalNtpUrl)))) {
+ return false;
+ }
+
+ return search_box->IsUsingMostVisited();
+}
+
+// static
+bool NewTabPageBindings::GetAreShortcutsVisible() {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !(HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)) ||
+ HasOrigin(GURL(chrome::kChromeSearchLocalNtpUrl)))) {
+ return true;
+ }
+
+ return search_box->AreShortcutsVisible();
+}
+
+// static
+void NewTabPageBindings::DeleteMostVisitedItem(v8::Isolate* isolate,
+ v8::Local<v8::Value> rid_value) {
+ // Manually convert to integer, so that the string "\"1\"" is also accepted.
+ base::Optional<int> rid = CoerceToInt(isolate, *rid_value);
+ if (!rid.has_value())
+ return;
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+
+ // Treat the Most Visited item as a custom link if called from the Most
+ // Visited or edit custom link iframes. This will initialize custom links if
+ // they have not already been initialized.
+ if (HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl))) {
+ search_box->DeleteCustomLink(*rid);
+ search_box->LogEvent(NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_REMOVE);
+ } else {
+ search_box->DeleteMostVisitedItem(*rid);
+ }
+}
+
+// static
+void NewTabPageBindings::UndoAllMostVisitedDeletions() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->UndoAllMostVisitedDeletions();
+}
+
+// static
+void NewTabPageBindings::UndoMostVisitedDeletion(
+ v8::Isolate* isolate,
+ v8::Local<v8::Value> rid_value) {
+ // Manually convert to integer, so that the string "\"1\"" is also accepted.
+ base::Optional<int> rid = CoerceToInt(isolate, *rid_value);
+ if (!rid.has_value())
+ return;
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+
+ search_box->UndoMostVisitedDeletion(*rid);
+}
+
+// static
+v8::Local<v8::Value> NewTabPageBindings::GetMostVisitedItemData(
+ v8::Isolate* isolate,
+ int rid) {
+ const SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return v8::Null(isolate);
+
+ InstantMostVisitedItem item;
+ if (!search_box->GetMostVisitedItemWithID(rid, &item))
+ return v8::Null(isolate);
+
+ int render_view_id =
+ GetMainRenderFrameForCurrentContext()->GetRenderView()->GetRoutingID();
+ return GenerateMostVisitedItemData(isolate, render_view_id, rid, item);
+}
+
+// static
+void NewTabPageBindings::UpdateCustomLink(int rid,
+ const std::string& url,
+ const std::string& title) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return;
+
+ // Limit the title to |kMaxCustomLinkTitleLength| characters. If truncated,
+ // adds an ellipsis.
+ base::string16 truncated_title =
+ gfx::TruncateString(base::UTF8ToUTF16(title), kMaxCustomLinkTitleLength,
+ gfx::CHARACTER_BREAK);
+
+ const GURL gurl(url);
+ // If rid is -1, adds a new link. Otherwise, updates the existing link
+ // indicated by the rid (empty fields will passed as empty strings). This will
+ // initialize custom links if they have not already been initialized.
+ if (rid == -1) {
+ if (!gurl.is_valid() || truncated_title.empty())
+ return;
+ search_box->AddCustomLink(gurl, base::UTF16ToUTF8(truncated_title));
+ search_box->LogEvent(NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_ADD);
+ } else {
+ // Check that the URL, if provided, is valid.
+ if (!url.empty() && !gurl.is_valid())
+ return;
+ search_box->UpdateCustomLink(rid, gurl, base::UTF16ToUTF8(truncated_title));
+ search_box->LogEvent(NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_UPDATE);
+ }
+}
+
+// static
+void NewTabPageBindings::ReorderCustomLink(int rid, int new_pos) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return;
+ search_box->ReorderCustomLink(rid, new_pos);
+}
+
+// static
+void NewTabPageBindings::UndoCustomLinkAction() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->UndoCustomLinkAction();
+ search_box->LogEvent(NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_UNDO);
+}
+
+// static
+void NewTabPageBindings::ResetCustomLinks() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->ResetCustomLinks();
+ search_box->LogEvent(NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_RESTORE_ALL);
+}
+
+// static
+void NewTabPageBindings::ToggleMostVisitedOrCustomLinks() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->ToggleMostVisitedOrCustomLinks();
+ search_box->LogEvent(NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_TOGGLE_TYPE);
+}
+
+// static
+void NewTabPageBindings::ToggleShortcutsVisibility(bool do_notify) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->ToggleShortcutsVisibility(do_notify);
+ search_box->LogEvent(
+ NTPLoggingEventType::NTP_CUSTOMIZE_SHORTCUT_TOGGLE_VISIBILITY);
+}
+
+// static
+std::string NewTabPageBindings::FixupAndValidateUrl(const std::string& url) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return std::string();
+ return search_box->FixupAndValidateUrl(url);
+}
+
+// static
+void NewTabPageBindings::LogEvent(int event) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box) {
+ return;
+ }
+ if (event <= NTP_EVENT_TYPE_LAST)
+ search_box->LogEvent(static_cast<NTPLoggingEventType>(event));
+}
+
+// static
+void NewTabPageBindings::LogSuggestionEventWithValue(int event, int data) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box) {
+ return;
+ }
+ if (event <= static_cast<int>(NTPSuggestionsLoggingEventType::kMaxValue)) {
+ search_box->LogSuggestionEventWithValue(
+ static_cast<NTPSuggestionsLoggingEventType>(event), data);
+ }
+}
+
+// static
+void NewTabPageBindings::LogMostVisitedImpression(
+ int position,
+ int tile_title_source,
+ int tile_source,
+ int tile_type,
+ v8::Local<v8::Value> data_generation_time) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return;
+
+ if (tile_title_source <= static_cast<int>(ntp_tiles::TileTitleSource::LAST) &&
+ tile_source <= static_cast<int>(ntp_tiles::TileSource::LAST) &&
+ tile_type <= ntp_tiles::TileVisualType::TILE_TYPE_MAX) {
+ const ntp_tiles::NTPTileImpression impression(
+ position, static_cast<ntp_tiles::TileSource>(tile_source),
+ static_cast<ntp_tiles::TileTitleSource>(tile_title_source),
+ static_cast<ntp_tiles::TileVisualType>(tile_type),
+ favicon_base::IconType::kInvalid,
+ ConvertDateValueToTime(*data_generation_time),
+ /*url_for_rappor=*/GURL());
+ search_box->LogMostVisitedImpression(impression);
+ }
+}
+
+// static
+void NewTabPageBindings::LogMostVisitedNavigation(
+ int position,
+ int tile_title_source,
+ int tile_source,
+ int tile_type,
+ v8::Local<v8::Value> data_generation_time) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl)))
+ return;
+
+ if (tile_title_source <= static_cast<int>(ntp_tiles::TileTitleSource::LAST) &&
+ tile_source <= static_cast<int>(ntp_tiles::TileSource::LAST) &&
+ tile_type <= ntp_tiles::TileVisualType::TILE_TYPE_MAX) {
+ const ntp_tiles::NTPTileImpression impression(
+ position, static_cast<ntp_tiles::TileSource>(tile_source),
+ static_cast<ntp_tiles::TileTitleSource>(tile_title_source),
+ static_cast<ntp_tiles::TileVisualType>(tile_type),
+ favicon_base::IconType::kInvalid,
+ ConvertDateValueToTime(*data_generation_time),
+ /*url_for_rappor=*/GURL());
+ search_box->LogMostVisitedNavigation(impression);
+ }
+}
+
+// static
+void NewTabPageBindings::ResetCustomBackgroundInfo() {
+ SetCustomBackgroundInfo(std::string(), std::string(), std::string(),
+ std::string(), std::string());
+}
+
+// static
+void NewTabPageBindings::SetCustomBackgroundInfo(
+ const std::string& background_url,
+ const std::string& attribution_line_1,
+ const std::string& attribution_line_2,
+ const std::string& attribution_action_url,
+ const std::string& collection_id) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ search_box->SetCustomBackgroundInfo(
+ GURL(background_url), attribution_line_1, attribution_line_2,
+ GURL(attribution_action_url), collection_id);
+ // Captures different events that occur when a background selection is made
+ // and 'Done' is clicked on the dialog.
+ if (!collection_id.empty()) {
+ search_box->LogEvent(
+ NTPLoggingEventType::NTP_BACKGROUND_DAILY_REFRESH_ENABLED);
+ } else if (background_url.empty()) {
+ search_box->LogEvent(
+ NTPLoggingEventType::NTP_CUSTOMIZE_RESTORE_BACKGROUND_CLICKED);
+ search_box->LogEvent(NTPLoggingEventType::NTP_BACKGROUND_IMAGE_RESET);
+ } else {
+ search_box->LogEvent(
+ NTPLoggingEventType::NTP_CUSTOMIZE_CHROME_BACKGROUND_DONE);
+ search_box->LogEvent(NTPLoggingEventType::NTP_BACKGROUND_IMAGE_SET);
+ }
+}
+
+// static
+void NewTabPageBindings::SelectLocalBackgroundImage() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ search_box->SelectLocalBackgroundImage();
+}
+
+// static
+void NewTabPageBindings::BlocklistSearchSuggestion(const int task_version,
+ const int task_id) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->BlocklistSearchSuggestion(task_version, task_id);
+}
+
+// static
+void NewTabPageBindings::BlocklistSearchSuggestionWithHash(
+ int task_version,
+ int task_id,
+ const std::string& hash) {
+ if (hash.length() != 4) {
+ return;
+ }
+
+ std::vector<uint8_t> data(hash.begin(), hash.end());
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->BlocklistSearchSuggestionWithHash(task_version, task_id, data);
+}
+
+// static
+void NewTabPageBindings::SearchSuggestionSelected(int task_version,
+ int task_id,
+ const std::string& hash) {
+ if (hash.length() > 4) {
+ return;
+ }
+
+ std::vector<uint8_t> data(hash.begin(), hash.end());
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->SearchSuggestionSelected(task_version, task_id, data);
+}
+
+// static
+void NewTabPageBindings::OptOutOfSearchSuggestions() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->OptOutOfSearchSuggestions();
+}
+
+// static
+void NewTabPageBindings::ApplyDefaultTheme() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->ApplyDefaultTheme();
+ content::RenderThread::Get()->RecordAction(
+ base::UserMetricsAction("ChromeColors_DefaultApplied"));
+}
+
+// static
+void NewTabPageBindings::UseDefaultTheme() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->ApplyDefaultTheme();
+ search_box->ConfirmThemeChanges();
+ content::RenderThread::Get()->RecordAction(
+ base::UserMetricsAction("ChromeColors_ThemeUninstalled"));
+}
+
+// static
+void NewTabPageBindings::ApplyAutogeneratedTheme(v8::Isolate* isolate,
+ int id,
+ v8::Local<v8::Value> value) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box || !value->IsArray())
+ return;
+ SkColor color;
+ if (ArrayToSkColor(isolate, value.As<v8::Array>(), &color)) {
+ search_box->ApplyAutogeneratedTheme(color);
+ content::RenderThread::Get()->RecordAction(
+ base::UserMetricsAction("ChromeColors_ColorApplied"));
+ if (id > 0 && id < static_cast<int>(chrome_colors::kNumColorsInfo)) {
+ UMA_HISTOGRAM_ENUMERATION("ChromeColors.AppliedColor", id,
+ chrome_colors::kNumColorsInfo);
+ }
+ }
+}
+
+// static
+void NewTabPageBindings::RevertThemeChanges() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->RevertThemeChanges();
+}
+
+// static
+void NewTabPageBindings::ConfirmThemeChanges() {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->ConfirmThemeChanges();
+}
+
+v8::Local<v8::Value> NewTabPageBindings::GetColorsInfo(v8::Isolate* isolate) {
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Object> v8_colors =
+ v8::Array::New(isolate, chrome_colors::kNumColorsInfo);
+ int i = 0;
+ for (chrome_colors::ColorInfo color_info :
+ chrome_colors::kGeneratedColorsInfo) {
+ v8::Local<v8::Object> v8_color_info =
+ gin::DataObjectBuilder(isolate)
+ .Set("id", color_info.id)
+ .Set("color", SkColorToArray(isolate, color_info.color))
+ .Set("label", l10n_util::GetStringUTF16(color_info.label_id))
+ .Set("icon", std::string(color_info.icon_data))
+ .Build();
+ v8_colors->CreateDataProperty(context, i++, v8_color_info).Check();
+ }
+ return v8_colors;
+}
+
+void NewTabPageBindings::BlocklistPromo(const std::string& promo_id) {
+ SearchBox* search_box = GetSearchBoxForCurrentContext();
+ if (!search_box)
+ return;
+ search_box->BlocklistPromo(promo_id);
+}
+
+} // namespace
+
+// static
+void SearchBoxExtension::Install(blink::WebLocalFrame* frame) {
+ v8::Isolate* isolate = blink::MainThreadIsolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context = frame->MainWorldScriptContext();
+ if (context.IsEmpty())
+ return;
+
+ v8::Context::Scope context_scope(context);
+
+ gin::Handle<SearchBoxBindings> searchbox_controller =
+ gin::CreateHandle(isolate, new SearchBoxBindings());
+ if (searchbox_controller.IsEmpty())
+ return;
+
+ gin::Handle<NewTabPageBindings> newtabpage_controller =
+ gin::CreateHandle(isolate, new NewTabPageBindings());
+ if (newtabpage_controller.IsEmpty())
+ return;
+
+ v8::Local<v8::Object> chrome =
+ content::GetOrCreateChromeObject(isolate, context);
+ v8::Local<v8::Object> embedded_search = v8::Object::New(isolate);
+ embedded_search
+ ->Set(context, gin::StringToV8(isolate, "searchBox"),
+ searchbox_controller.ToV8())
+ .ToChecked();
+ embedded_search
+ ->Set(context, gin::StringToV8(isolate, "newTabPage"),
+ newtabpage_controller.ToV8())
+ .ToChecked();
+ chrome
+ ->Set(context, gin::StringToSymbol(isolate, "embeddedSearch"),
+ embedded_search)
+ .ToChecked();
+}
+
+// static
+void SearchBoxExtension::DispatchFocusChange(blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchFocusChangedScript);
+}
+
+// static
+void SearchBoxExtension::DispatchAddCustomLinkResult(
+ blink::WebLocalFrame* frame,
+ bool success) {
+ blink::WebString script(blink::WebString::FromUTF8(base::StringPrintf(
+ kDispatchAddCustomLinkResult, success ? "true" : "false")));
+ Dispatch(frame, script);
+}
+
+// static
+void SearchBoxExtension::DispatchUpdateCustomLinkResult(
+ blink::WebLocalFrame* frame,
+ bool success) {
+ blink::WebString script(blink::WebString::FromUTF8(base::StringPrintf(
+ kDispatchUpdateCustomLinkResult, success ? "true" : "false")));
+ Dispatch(frame, script);
+}
+
+// static
+void SearchBoxExtension::DispatchDeleteCustomLinkResult(
+ blink::WebLocalFrame* frame,
+ bool success) {
+ blink::WebString script(blink::WebString::FromUTF8(base::StringPrintf(
+ kDispatchDeleteCustomLinkResult, success ? "true" : "false")));
+ Dispatch(frame, script);
+}
+
+void SearchBoxExtension::DispatchQueryAutocompleteResult(
+ blink::WebLocalFrame* frame,
+ chrome::mojom::AutocompleteResultPtr result) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetStringKey("input", result->input);
+ dict.SetDoubleKey("status", static_cast<double>(result->status));
+ dict.SetKey("matches", CreateAutocompleteMatches(result->matches));
+
+ std::string json;
+ base::JSONWriter::Write(dict, &json);
+ Dispatch(frame, blink::WebString::FromUTF8(base::StringPrintf(
+ kDispatchQueryAutocompleteResult, json.c_str())));
+}
+
+void SearchBoxExtension::DispatchDeleteAutocompleteMatchResult(
+ blink::WebLocalFrame* frame,
+ chrome::mojom::DeleteAutocompleteMatchResultPtr result) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetBoolKey("success", result->success);
+ dict.SetKey("matches", CreateAutocompleteMatches(result->matches));
+
+ std::string json;
+ base::JSONWriter::Write(dict, &json);
+ Dispatch(frame, blink::WebString::FromUTF8(base::StringPrintf(
+ kDispatchDeleteAutocompleteMatchResult, json.c_str())));
+}
+
+// static
+void SearchBoxExtension::DispatchInputCancel(blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchInputCancelScript);
+}
+
+// static
+void SearchBoxExtension::DispatchInputStart(blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchInputStartScript);
+}
+
+// static
+void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchKeyCaptureChangeScript);
+}
+
+// static
+void SearchBoxExtension::DispatchMostVisitedChanged(
+ blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchMostVisitedChangedScript);
+}
+
+// static
+void SearchBoxExtension::DispatchThemeChange(blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchThemeChangeEventScript);
+}
+
+// static
+void SearchBoxExtension::DispatchLocalBackgroundSelected(
+ blink::WebLocalFrame* frame) {
+ Dispatch(frame, kDispatchLocalBackgroundSelectedScript);
+}
diff --git a/chromium/chrome/renderer/searchbox/searchbox_extension.h b/chromium/chrome/renderer/searchbox/searchbox_extension.h
new file mode 100644
index 00000000000..5d2052e21ec
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/searchbox_extension.h
@@ -0,0 +1,57 @@
+// Copyright 2012 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_RENDERER_SEARCHBOX_SEARCHBOX_EXTENSION_H_
+#define CHROME_RENDERER_SEARCHBOX_SEARCHBOX_EXTENSION_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "chrome/common/search.mojom.h"
+#include "ui/gfx/color_palette.h"
+
+namespace blink {
+class WebLocalFrame;
+}
+
+constexpr SkColor kNTPLightLogoColor = SkColorSetRGB(238, 238, 238);
+constexpr SkColor kNTPLightIconColor = gfx::kGoogleGrey100;
+constexpr SkColor kNTPDarkIconColor = gfx::kGoogleGrey900;
+
+// Javascript bindings for the chrome.embeddedSearch APIs. See
+// https://www.chromium.org/embeddedsearch.
+class SearchBoxExtension {
+ public:
+ static void Install(blink::WebLocalFrame* frame);
+
+ // Helpers to dispatch Javascript events.
+ static void DispatchChromeIdentityCheckResult(blink::WebLocalFrame* frame,
+ const base::string16& identity,
+ bool identity_match);
+ static void DispatchFocusChange(blink::WebLocalFrame* frame);
+ static void DispatchAddCustomLinkResult(blink::WebLocalFrame* frame,
+ bool success);
+ static void DispatchUpdateCustomLinkResult(blink::WebLocalFrame* frame,
+ bool success);
+ static void DispatchDeleteCustomLinkResult(blink::WebLocalFrame* frame,
+ bool success);
+ static void DispatchQueryAutocompleteResult(
+ blink::WebLocalFrame* frame,
+ chrome::mojom::AutocompleteResultPtr result);
+ static void DispatchDeleteAutocompleteMatchResult(
+ blink::WebLocalFrame* frame,
+ chrome::mojom::DeleteAutocompleteMatchResultPtr result);
+ static void DispatchInputCancel(blink::WebLocalFrame* frame);
+ static void DispatchInputStart(blink::WebLocalFrame* frame);
+ static void DispatchKeyCaptureChange(blink::WebLocalFrame* frame);
+ static void DispatchMostVisitedChanged(blink::WebLocalFrame* frame);
+ static void DispatchThemeChange(blink::WebLocalFrame* frame);
+ static void DispatchLocalBackgroundSelected(blink::WebLocalFrame* frame);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SearchBoxExtension);
+};
+
+#endif // CHROME_RENDERER_SEARCHBOX_SEARCHBOX_EXTENSION_H_
diff --git a/chromium/chrome/renderer/searchbox/searchbox_unittest.cc b/chromium/chrome/renderer/searchbox/searchbox_unittest.cc
new file mode 100644
index 00000000000..747fe743cc5
--- /dev/null
+++ b/chromium/chrome/renderer/searchbox/searchbox_unittest.cc
@@ -0,0 +1,271 @@
+// Copyright 2013 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/renderer/searchbox/searchbox.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <string>
+
+#include "base/stl_util.h"
+#include "chrome/common/search/instant_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+const char* kUrlString1 = "http://www.google.com";
+const char* kUrlString2 = "http://www.chromium.org/path/q=3#r=4";
+const char* kUrlString3 = "http://www.youtube.com:8080/hosps";
+
+// Mock helper to test internal::TranslateIconRestrictedUrl().
+class MockIconURLHelper: public SearchBox::IconURLHelper {
+ public:
+ MockIconURLHelper();
+ ~MockIconURLHelper() override;
+ int GetViewID() const override;
+ std::string GetURLStringFromRestrictedID(InstantRestrictedID rid) const
+ override;
+
+ private:
+ std::map<InstantRestrictedID, std::string> rid_to_url_string_;
+};
+
+MockIconURLHelper::MockIconURLHelper() {
+ rid_to_url_string_[1] = kUrlString1;
+ rid_to_url_string_[2] = kUrlString2;
+ rid_to_url_string_[3] = kUrlString3;
+}
+
+MockIconURLHelper::~MockIconURLHelper() {
+}
+
+int MockIconURLHelper::GetViewID() const {
+ return 137;
+}
+
+std::string MockIconURLHelper::GetURLStringFromRestrictedID(
+ InstantRestrictedID rid) const {
+ auto it = rid_to_url_string_.find(rid);
+ return it == rid_to_url_string_.end() ? std::string() : it->second;
+}
+
+} // namespace
+
+namespace internal {
+
+// Defined in searchbox.cc
+bool ParseViewIdAndRestrictedId(const std::string& id_part,
+ int* view_id_out,
+ InstantRestrictedID* rid_out);
+
+// Defined in searchbox.cc
+bool ParseIconRestrictedUrl(const GURL& url,
+ std::string* param_part,
+ int* view_id,
+ InstantRestrictedID* rid);
+
+// Defined in searchbox.cc
+void TranslateIconRestrictedUrl(const GURL& transient_url,
+ const SearchBox::IconURLHelper& helper,
+ GURL* url);
+
+// Defined in searchbox.cc
+std::string FixupAndValidateUrl(const std::string& url);
+
+TEST(SearchBoxUtilTest, ParseViewIdAndRestrictedIdSuccess) {
+ int view_id = -1;
+ InstantRestrictedID rid = -1;
+
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("2/3", &view_id, &rid));
+ EXPECT_EQ(2, view_id);
+ EXPECT_EQ(3, rid);
+
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("0/0", &view_id, &rid));
+ EXPECT_EQ(0, view_id);
+ EXPECT_EQ(0, rid);
+
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("1048576/314", &view_id, &rid));
+ EXPECT_EQ(1048576, view_id);
+ EXPECT_EQ(314, rid);
+
+ // Odd but not fatal.
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("00/09", &view_id, &rid));
+ EXPECT_EQ(0, view_id);
+ EXPECT_EQ(9, rid);
+
+ // Tolerates multiple, leading, and trailing "/".
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("2////3", &view_id, &rid));
+ EXPECT_EQ(2, view_id);
+ EXPECT_EQ(3, rid);
+
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("5/6/", &view_id, &rid));
+ EXPECT_EQ(5, view_id);
+ EXPECT_EQ(6, rid);
+
+ EXPECT_TRUE(ParseViewIdAndRestrictedId("/7/8", &view_id, &rid));
+ EXPECT_EQ(7, view_id);
+ EXPECT_EQ(8, rid);
+}
+
+TEST(SearchBoxUtilTest, ParseViewIdAndRestrictedIdFailure) {
+ const char* test_cases[] = {
+ "",
+ " ",
+ "/",
+ "2/",
+ "/3",
+ "2a/3",
+ "2/3a",
+ " 2/3",
+ "2/ 3",
+ "2 /3 ",
+ "23",
+ "2,3",
+ "-2/3",
+ "2/-3",
+ "2/3/1",
+ "blahblah",
+ "0xA/0x10",
+ };
+ for (size_t i = 0; i < base::size(test_cases); ++i) {
+ int view_id = -1;
+ InstantRestrictedID rid = -1;
+ EXPECT_FALSE(ParseViewIdAndRestrictedId(test_cases[i], &view_id, &rid))
+ << " for test_cases[" << i << "]";
+ EXPECT_EQ(-1, view_id);
+ EXPECT_EQ(-1, rid);
+ }
+}
+
+TEST(SearchBoxUtilTest, ParseIconRestrictedUrlFaviconSuccess) {
+ struct {
+ const char* transient_url_str;
+ const char* expected_param_part;
+ int expected_view_id;
+ InstantRestrictedID expected_rid;
+ } test_cases[] = {
+ {"chrome-search://favicon/1/2", "", 1, 2},
+ {"chrome-search://favicon/size/16@2x/3/4", "size/16@2x/", 3, 4},
+ {"chrome-search://favicon/iconurl/9/10", "iconurl/", 9, 10},
+ };
+ for (size_t i = 0; i < base::size(test_cases); ++i) {
+ std::string param_part = "(unwritten)";
+ int view_id = -1;
+ InstantRestrictedID rid = -1;
+ EXPECT_TRUE(ParseIconRestrictedUrl(GURL(test_cases[i].transient_url_str),
+ &param_part, &view_id, &rid))
+ << " for test_cases[" << i << "]";
+ EXPECT_EQ(test_cases[i].expected_param_part, param_part)
+ << " for test_cases[" << i << "]";
+ EXPECT_EQ(test_cases[i].expected_view_id, view_id)
+ << " for test_cases[" << i << "]";
+ EXPECT_EQ(test_cases[i].expected_rid, rid)
+ << " for test_cases[" << i << "]";
+ }
+}
+
+TEST(SearchBoxUtilTest, ParseIconRestrictedUrlFailure) {
+ struct {
+ const char* transient_url_str;
+ } test_cases[] = {
+ {"chrome-search://favicon/"},
+ {"chrome-search://favicon/3/"},
+ {"chrome-search://favicon/size/3/4"},
+ {"chrome-search://favicon/largest/http://www.google.com"},
+ {"chrome-search://favicon/size/16@2x/-1/10"},
+ };
+ for (size_t i = 0; i < base::size(test_cases); ++i) {
+ std::string param_part = "(unwritten)";
+ int view_id = -1;
+ InstantRestrictedID rid = -1;
+ EXPECT_FALSE(ParseIconRestrictedUrl(GURL(test_cases[i].transient_url_str),
+ &param_part, &view_id, &rid))
+ << " for test_cases[" << i << "]";
+ EXPECT_EQ("(unwritten)", param_part);
+ EXPECT_EQ(-1, view_id);
+ EXPECT_EQ(-1, rid);
+ }
+}
+
+TEST(SearchBoxUtilTest, TranslateIconRestrictedUrlSuccess) {
+ struct {
+ const char* transient_url_str;
+ std::string expected_url_str;
+ } test_cases[] = {
+ {"chrome-search://favicon/137/1",
+ std::string("chrome-search://favicon/") + kUrlString1},
+ {"chrome-search://favicon/", "chrome-search://favicon/"},
+ {"chrome-search://favicon/314", "chrome-search://favicon/"},
+ {"chrome-search://favicon/314/1", "chrome-search://favicon/"},
+ {"chrome-search://favicon/137/255", "chrome-search://favicon/"},
+ {"chrome-search://favicon/-3/-1", "chrome-search://favicon/"},
+ {"chrome-search://favicon/invalidstuff", "chrome-search://favicon/"},
+ {"chrome-search://favicon/size/16@2x/http://www.google.com",
+ "chrome-search://favicon/"},
+ };
+
+ MockIconURLHelper helper;
+ for (size_t i = 0; i < base::size(test_cases); ++i) {
+ GURL url;
+ GURL transient_url(test_cases[i].transient_url_str);
+ TranslateIconRestrictedUrl(transient_url, helper, &url);
+ EXPECT_EQ(GURL(test_cases[i].expected_url_str), url)
+ << " for test_cases[" << i << "]";
+ }
+}
+
+TEST(SearchBoxUtilTest, FixupAndValidateUrlReturnsEmptyIfInvalid) {
+ struct TestCase {
+ const char* url;
+ bool is_valid;
+ } test_cases[] = {
+ {" ", false},
+ {"^&*@)^)", false},
+ {"foo", true},
+ {"http://foo", true},
+ {"\thttp://foo", true},
+ {" http://foo", true},
+ {"https://foo", true},
+ {"foo.com", true},
+ {"http://foo.com", true},
+ {"https://foo.com", true},
+ {"blob://foo", true},
+
+ };
+
+ for (const TestCase& test_case : test_cases) {
+ const std::string& url = FixupAndValidateUrl(test_case.url);
+ EXPECT_EQ(!url.empty(), test_case.is_valid)
+ << " for test_case '" << test_case.url << "'";
+ }
+}
+
+TEST(SearchBoxUtilTest, FixupAndValidateUrlDefaultsToHttps) {
+ struct TestCase {
+ const char* url;
+ const char* expected_scheme;
+ } test_cases[] = {
+ // No scheme.
+ {"foo.com", url::kHttpsScheme},
+ // With "http".
+ {"http://foo.com", url::kHttpScheme},
+ // With "http" and whitespace.
+ {"\thttp://foo", url::kHttpScheme},
+ {" http://foo", url::kHttpScheme},
+ // With "https".
+ {"https://foo.com", url::kHttpsScheme},
+ // Non "http"/"https".
+ {"blob://foo", url::kBlobScheme},
+ };
+
+ for (const TestCase& test_case : test_cases) {
+ const GURL url(FixupAndValidateUrl(test_case.url));
+ EXPECT_TRUE(url.SchemeIs(test_case.expected_scheme))
+ << " for test case '" << test_case.url << "'";
+ }
+}
+
+} // namespace internal