// 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 #include #include #include #include "base/containers/flat_map.h" #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/common/url_constants.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 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 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(mv_item.title_source)) .Set("tileSource", static_cast(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::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 CoerceToInt(v8::Isolate* isolate, v8::Value* value) { DCHECK(value); v8::MaybeLocal 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 SkColorToArray(v8::Isolate* isolate, const SkColor& color) { v8::Local context = isolate->GetCurrentContext(); v8::Local 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 color, SkColor* color_result) { if (color->Length() != 4) return false; v8::Local context = isolate->GetCurrentContext(); v8::Local r_value; v8::Local g_value; v8::Local b_value; v8::Local 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 r = CoerceToInt(isolate, *r_value); base::Optional g = CoerceToInt(isolate, *g_value); base::Optional b = CoerceToInt(isolate, *b_value); base::Optional 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 GenerateNtpTheme(v8::Isolate* isolate, const NtpTheme& theme) { 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.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.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.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.text_color_light)); // The theme alternate logo value indicates same color when TRUE and a // colorful one when FALSE. builder.Set("alternateLogo", theme.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.has_theme_image) { builder.Set("imageUrl", base::StringPrintf(kCSSBackgroundImageFormat, theme.theme_id.c_str(), theme.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.image_horizontal_alignment == THEME_BKGRND_IMAGE_ALIGN_LEFT) { alignment = kCSSBackgroundPositionLeft; } else if (theme.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.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) { alignment = kCSSBackgroundPositionTop; } else if (theme.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.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.has_attribution) { builder.Set("attributionUrl", base::StringPrintf(kThemeAttributionFormat, theme.theme_id.c_str(), theme.theme_id.c_str())); } } builder.Set("themeId", theme.theme_id); builder.Set("themeName", theme.theme_name); builder.Set("customBackgroundDisabledByPolicy", theme.custom_background_disabled_by_policy); builder.Set("customBackgroundConfigured", !theme.custom_background_url.is_empty()); // If a custom background has been set provide the relevant information to the // page. if (!theme.custom_background_url.is_empty()) { builder.Set("imageUrl", theme.custom_background_url.spec()); builder.Set("attributionActionUrl", theme.custom_background_attribution_action_url.spec()); builder.Set("attribution1", theme.custom_background_attribution_line_1); builder.Set("attribution2", theme.custom_background_attribution_line_2); builder.Set("collectionId", theme.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.text_color)); builder.Set("useTitleContainer", theme.has_theme_image); // TODO(gayane): Rename icon color to shortcut color in JS for consitancy. builder.Set("iconBackgroundColor", SkColorToArray(isolate, theme.shortcut_color)); builder.Set("useWhiteAddIcon", color_utils::IsDark(theme.shortcut_color)); builder.Set("logoColor", SkColorToArray(isolate, theme.logo_color)); builder.Set("colorId", theme.color_id); if (theme.color_id != -1) { builder.Set("colorDark", SkColorToArray(isolate, theme.color_dark)); builder.Set("colorLight", SkColorToArray(isolate, theme.color_light)); builder.Set("colorPicked", SkColorToArray(isolate, theme.color_picked)); } gin::DataObjectBuilder search_box(isolate); search_box.Set("bg", SkColorToArray(isolate, theme.search_box.bg)); search_box.Set("icon", SkColorToArray(isolate, theme.search_box.icon)); search_box.Set("iconSelected", SkColorToArray(isolate, theme.search_box.icon_selected)); search_box.Set("placeholder", SkColorToArray(isolate, theme.search_box.placeholder)); search_box.Set("resultsBg", SkColorToArray(isolate, theme.search_box.results_bg)); search_box.Set("resultsBgHovered", SkColorToArray(isolate, theme.search_box.results_bg_hovered)); search_box.Set("resultsBgSelected", SkColorToArray(isolate, theme.search_box.results_bg_selected)); search_box.Set("resultsDim", SkColorToArray(isolate, theme.search_box.results_dim)); search_box.Set( "resultsDimSelected", SkColorToArray(isolate, theme.search_box.results_dim_selected)); search_box.Set("resultsText", SkColorToArray(isolate, theme.search_box.results_text)); search_box.Set( "resultsTextSelected", SkColorToArray(isolate, theme.search_box.results_text_selected)); search_box.Set("resultsUrl", SkColorToArray(isolate, theme.search_box.results_url)); search_box.Set( "resultsUrlSelected", SkColorToArray(isolate, theme.search_box.results_url_selected)); search_box.Set("text", SkColorToArray(isolate, theme.search_box.text)); builder.Set("searchBox", search_box.Build()); 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& matches) { base::Value list(base::Value::Type::LIST); for (const search::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.spec()); dict.SetIntKey("suggestionGroupId", match->suggestion_group_id); dict.SetStringKey("inlineAutocompletion", match->inline_autocompletion); dict.SetBoolKey("isSearchType", match->is_search_type); dict.SetStringKey("fillIntoEdit", match->fill_into_edit); dict.SetStringKey("iconUrl", match->icon_url); dict.SetStringKey("imageDominantColor", match->image_dominant_color); dict.SetStringKey("imageUrl", match->image_url); 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; } base::Value CreateSuggestionGroupsMap( const base::flat_map& suggestion_groups_map) { base::Value result_map(base::Value::Type::DICTIONARY); for (const auto& pair : suggestion_groups_map) { base::Value suggestion_group(base::Value::Type::DICTIONARY); suggestion_group.SetStringKey("header", pair.second->header); suggestion_group.SetBoolKey("hidden", pair.second->hidden); result_map.SetPath(base::NumberToString(pair.first), std::move(suggestion_group)); } return result_map; } 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 kDispatchAutocompleteResultChanged[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.searchBox &&" " window.chrome.embeddedSearch.searchBox.autocompleteresultchanged &&" " typeof window.chrome.embeddedSearch.searchBox" " .autocompleteresultchanged === 'function') {" " window.chrome.embeddedSearch.searchBox.autocompleteresultchanged(%s);" " true;" "}"; static const char kDispatchAutocompleteMatchImageAvailable[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.searchBox &&" " " "window.chrome.embeddedSearch.searchBox.autocompletematchimageavailable &&" " typeof window.chrome.embeddedSearch.searchBox" " .autocompletematchimageavailable === 'function') {" " window.chrome.embeddedSearch.searchBox" " .autocompletematchimageavailable(%d, '%s', '%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 { 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, bool prevent_inline_autocomplete); static void StopAutocomplete(bool clear_result); static void LogCharTypedToRepaintLatency(uint32_t latency_ms); static void StartCapturingKeyStrokes(); static void StopCapturingKeyStrokes(); static void OpenAutocompleteMatch(int line, const std::string& url, bool are_matches_showing, double time_elapsed_since_last_focus, double button, bool alt_key, bool ctrl_key, bool meta_key, bool shift_key); static void ToggleSuggestionGroupIdVisibility(int32_t suggestion_group_id); 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::GetObjectTemplateBuilder(isolate) .SetProperty("rtl", &base::i18n::IsRTL) .SetProperty("isFocused", &SearchBoxBindings::IsFocused) .SetProperty("isKeyCaptureEnabled", &SearchBoxBindings::IsKeyCaptureEnabled) .SetMethod("deleteAutocompleteMatch", &SearchBoxBindings::DeleteAutocompleteMatch) .SetMethod("logCharTypedToRepaintLatency", &SearchBoxBindings::LogCharTypedToRepaintLatency) .SetMethod("openAutocompleteMatch", &SearchBoxBindings::OpenAutocompleteMatch) .SetMethod("paste", &SearchBoxBindings::Paste) .SetMethod("queryAutocomplete", &SearchBoxBindings::QueryAutocomplete) .SetMethod("stopAutocomplete", &SearchBoxBindings::StopAutocomplete) .SetMethod("startCapturingKeyStrokes", &SearchBoxBindings::StartCapturingKeyStrokes) .SetMethod("stopCapturingKeyStrokes", &SearchBoxBindings::StopCapturingKeyStrokes) .SetMethod("toggleSuggestionGroupIdVisibility", &SearchBoxBindings::ToggleSuggestionGroupIdVisibility); } // 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::ToggleSuggestionGroupIdVisibility( int32_t suggestion_group_id) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->ToggleSuggestionGroupIdVisibility(suggestion_group_id); } // static void SearchBoxBindings::OpenAutocompleteMatch( int line, const std::string& url, bool are_matches_showing, double time_elapsed_since_last_focus, double button, bool alt_key, bool ctrl_key, bool meta_key, bool shift_key) { DCHECK_GE(line, 0); DCHECK_LE(line, 255); SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->OpenAutocompleteMatch(line, GURL(url), are_matches_showing, time_elapsed_since_last_focus, button, alt_key, ctrl_key, meta_key, shift_key); } // 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, bool prevent_inline_autocomplete) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->QueryAutocomplete(input, prevent_inline_autocomplete); } // static void SearchBoxBindings::StopAutocomplete(bool clear_result) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->StopAutocomplete(clear_result); } // static void SearchBoxBindings::LogCharTypedToRepaintLatency(uint32_t latency_ms) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->LogCharTypedToRepaintLatency(latency_ms); } // 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 { 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 GetMostVisited(v8::Isolate* isolate); static bool GetMostVisitedAvailable(v8::Isolate* isolate); static v8::Local GetNtpTheme(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 rid); static void UndoAllMostVisitedDeletions(); static void UndoMostVisitedDeletion(v8::Isolate* isolate, v8::Local 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 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 data_generation_time); static void LogMostVisitedNavigation( int position, int tile_title_source, int tile_source, int tile_type, v8::Local 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 color); static void RevertThemeChanges(); static void ConfirmThemeChanges(); static void BlocklistPromo(const std::string& promo_id); static void OpenExtensionsPage(double button, bool alt_key, bool ctrl_key, bool meta_key, bool shift_key); static v8::Local 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::GetObjectTemplateBuilder(isolate) .SetProperty("isInputInProgress", &NewTabPageBindings::IsInputInProgress) .SetProperty("mostVisited", &NewTabPageBindings::GetMostVisited) .SetProperty("mostVisitedAvailable", &NewTabPageBindings::GetMostVisitedAvailable) .SetProperty("ntpTheme", &NewTabPageBindings::GetNtpTheme) // TODO(https://crbug.com/1020450): remove "themeBackgroundInfo" legacy // name when we're sure no third-party NTP needs it. .SetProperty("themeBackgroundInfo", &NewTabPageBindings::GetNtpTheme) .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) .SetMethod("openExtensionsPage", &NewTabPageBindings::OpenExtensionsPage); } // 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 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 instant_mv_items; search_box->GetMostVisitedItems(&instant_mv_items); v8::Local context = isolate->GetCurrentContext(); v8::Local 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 NewTabPageBindings::GetNtpTheme(v8::Isolate* isolate) { const SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return v8::Null(isolate); const NtpTheme* theme = search_box->GetNtpTheme(); if (!theme) return v8::Null(isolate); return GenerateNtpTheme(isolate, *theme); } // 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 rid_value) { // Manually convert to integer, so that the string "\"1\"" is also accepted. base::Optional 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 rid_value) { // Manually convert to integer, so that the string "\"1\"" is also accepted. base::Optional 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 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(event)); } // static void NewTabPageBindings::LogSuggestionEventWithValue(int event, int data) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) { return; } if (event <= static_cast(NTPSuggestionsLoggingEventType::kMaxValue)) { search_box->LogSuggestionEventWithValue( static_cast(event), data); } } // static void NewTabPageBindings::LogMostVisitedImpression( int position, int tile_title_source, int tile_source, int tile_type, v8::Local data_generation_time) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl))) return; if (tile_title_source <= static_cast(ntp_tiles::TileTitleSource::LAST) && tile_source <= static_cast(ntp_tiles::TileSource::LAST) && tile_type <= ntp_tiles::TileVisualType::TILE_TYPE_MAX) { const ntp_tiles::NTPTileImpression impression( position, static_cast(tile_source), static_cast(tile_title_source), static_cast(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 data_generation_time) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box || !HasOrigin(GURL(chrome::kChromeSearchMostVisitedUrl))) return; if (tile_title_source <= static_cast(ntp_tiles::TileTitleSource::LAST) && tile_source <= static_cast(ntp_tiles::TileSource::LAST) && tile_type <= ntp_tiles::TileVisualType::TILE_TYPE_MAX) { const ntp_tiles::NTPTileImpression impression( position, static_cast(tile_source), static_cast(tile_title_source), static_cast(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 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 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(); } // static void NewTabPageBindings::UseDefaultTheme() { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->ApplyDefaultTheme(); search_box->ConfirmThemeChanges(); } // static void NewTabPageBindings::ApplyAutogeneratedTheme(v8::Isolate* isolate, int id, v8::Local value) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box || !value->IsArray()) return; SkColor color; if (!ArrayToSkColor(isolate, value.As(), &color)) return; search_box->ApplyAutogeneratedTheme(color); } // 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 NewTabPageBindings::GetColorsInfo(v8::Isolate* isolate) { v8::Local context = isolate->GetCurrentContext(); v8::Local v8_colors = v8::Array::New(isolate, chrome_colors::kNumColorsInfo); int i = 0; for (chrome_colors::ColorInfo color_info : chrome_colors::kGeneratedColorsInfo) { v8::Local 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); } void NewTabPageBindings::OpenExtensionsPage(double button, bool alt_key, bool ctrl_key, bool meta_key, bool shift_key) { SearchBox* search_box = GetSearchBoxForCurrentContext(); if (!search_box) return; search_box->OpenExtensionsPage(button, alt_key, ctrl_key, meta_key, shift_key); } } // namespace // static void SearchBoxExtension::Install(blink::WebLocalFrame* frame) { v8::Isolate* isolate = blink::MainThreadIsolate(); v8::HandleScope handle_scope(isolate); v8::Local context = frame->MainWorldScriptContext(); if (context.IsEmpty()) return; v8::Context::Scope context_scope(context); gin::Handle searchbox_controller = gin::CreateHandle(isolate, new SearchBoxBindings()); if (searchbox_controller.IsEmpty()) return; gin::Handle newtabpage_controller = gin::CreateHandle(isolate, new NewTabPageBindings()); if (newtabpage_controller.IsEmpty()) return; v8::Local chrome = content::GetOrCreateChromeObject(isolate, context); v8::Local 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); } // static void SearchBoxExtension::DispatchAutocompleteResultChanged( blink::WebLocalFrame* frame, search::mojom::AutocompleteResultPtr result) { base::Value dict(base::Value::Type::DICTIONARY); dict.SetStringKey("input", result->input); dict.SetKey("matches", CreateAutocompleteMatches(result->matches)); dict.SetKey("suggestionGroupsMap", CreateSuggestionGroupsMap(result->suggestion_groups_map)); std::string json; base::JSONWriter::Write(dict, &json); Dispatch(frame, blink::WebString::FromUTF8(base::StringPrintf( kDispatchAutocompleteResultChanged, json.c_str()))); } // static void SearchBoxExtension::DispatchAutocompleteMatchImageAvailable( blink::WebLocalFrame* frame, uint32_t match_index, const std::string& image_url, const std::string& data_url) { blink::WebString script(blink::WebString::FromUTF8( base::StringPrintf(kDispatchAutocompleteMatchImageAvailable, match_index, image_url.c_str(), data_url.c_str()))); Dispatch(frame, script); } // 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); }