// 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 "base/command_line.h" #include "base/i18n/rtl.h" #include "base/json/string_escape.h" #include "base/macros.h" #include "base/metrics/field_trial.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 "chrome/common/instant_types.h" #include "chrome/common/ntp_logging_events.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 "content/public/renderer/render_view.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebView.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" #include "ui/base/window_open_disposition.h" #include "ui/events/keycodes/keyboard_codes.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 kCSSBackgroundColorFormat[] = "rgba(%d,%d,%d,%s)"; 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"; // Converts a V8 value to a string16. base::string16 V8ValueToUTF16(v8::Local v) { v8::String::Value s(v); return base::string16(reinterpret_cast(*s), s.length()); } // Returns whether icon NTP is enabled by experiment. // TODO(huangs): Remove all 3 copies of this routine once Icon NTP launches. bool IsIconNTPEnabled() { // Note: It's important to query the field trial state first, to ensure that // UMA reports the correct group. const std::string group_name = base::FieldTrialList::FindFullName("IconNTP"); using base::CommandLine; if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableIconNtp)) return false; if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableIconNtp)) return true; return base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE); } // Converts string16 to V8 String. v8::Local UTF16ToV8String(v8::Isolate* isolate, const base::string16& s) { return v8::String::NewFromTwoByte(isolate, reinterpret_cast(s.data()), v8::String::kNormalString, s.size()); } // Converts std::string to V8 String. v8::Local UTF8ToV8String(v8::Isolate* isolate, const std::string& s) { return v8::String::NewFromUtf8( isolate, s.data(), v8::String::kNormalString, s.size()); } // Throws a TypeError on the current V8 context if the args are invalid. void ThrowInvalidParameters(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); isolate->ThrowException(v8::Exception::TypeError( v8::String::NewFromUtf8(isolate, "Invalid parameters"))); } void Dispatch(blink::WebFrame* frame, const blink::WebString& script) { if (!frame) return; frame->executeScript(blink::WebScriptSource(script)); } v8::Local GenerateThumbnailURL( v8::Isolate* isolate, int render_view_id, InstantRestrictedID most_visited_item_id) { return UTF8ToV8String( isolate, base::StringPrintf( "chrome-search://thumb/%d/%d", render_view_id, most_visited_item_id)); } v8::Local GenerateThumb2URL(v8::Isolate* isolate, const std::string& url) { return UTF8ToV8String( isolate, base::StringPrintf("chrome-search://thumb2/%s", url.c_str())); } // Populates a Javascript MostVisitedItem object from |mv_item|. // NOTE: Includes "url", "title" and "domain" which are private data, so should // not be returned to the Instant page. These should be erased before returning // the object. See GetMostVisitedItemsWrapper() in searchbox_api.js. v8::Local GenerateMostVisitedItem( 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". std::string direction; if (base::i18n::StringContainsStrongRTLChars(mv_item.title)) direction = kRTLHtmlTextDirection; else direction = kLTRHtmlTextDirection; base::string16 title = mv_item.title; if (title.empty()) title = base::UTF8ToUTF16(mv_item.url.spec()); v8::Local obj = v8::Object::New(isolate); obj->Set(v8::String::NewFromUtf8(isolate, "renderViewId"), v8::Int32::New(isolate, render_view_id)); obj->Set(v8::String::NewFromUtf8(isolate, "rid"), v8::Int32::New(isolate, restricted_id)); // If the suggestion already has a suggested thumbnail, we create an thumbnail // array with both the local thumbnail and the proposed one. // Otherwise, we just create an array with the generated one. if (!mv_item.thumbnail.spec().empty()) { v8::Local thumbs = v8::Array::New(isolate, 2); thumbs->Set(0, GenerateThumb2URL(isolate, mv_item.url.spec())); thumbs->Set(1, UTF8ToV8String(isolate, mv_item.thumbnail.spec())); obj->Set(v8::String::NewFromUtf8(isolate, "thumbnailUrls"), thumbs); } else { v8::Local thumbs = v8::Array::New(isolate, 1); thumbs->Set(0, GenerateThumbnailURL(isolate, render_view_id, restricted_id)); obj->Set(v8::String::NewFromUtf8(isolate, "thumbnailUrls"), thumbs); } // If the suggestion already has a favicon, we populate the element with it. if (!mv_item.favicon.spec().empty()) { obj->Set(v8::String::NewFromUtf8(isolate, "faviconUrl"), UTF8ToV8String(isolate, mv_item.favicon.spec())); } // If the suggestion has an impression url, we populate the element with it. if (!mv_item.impression_url.spec().empty()) { obj->Set(v8::String::NewFromUtf8(isolate, "impressionUrl"), UTF8ToV8String(isolate, mv_item.impression_url.spec())); } // If the suggestion has a click url, we populate the element with it. if (!mv_item.click_url.spec().empty()) { obj->Set(v8::String::NewFromUtf8(isolate, "pingUrl"), UTF8ToV8String(isolate, mv_item.click_url.spec())); } if (IsIconNTPEnabled()) { // Update website http://www.chromium.org/embeddedsearch when we make this // permanent. // Large icon size is 48px * window.devicePixelRatio. This is easier to set // from JS, where IsIconNTPEnabled() is not available. So we add stubs // here, and let JS fill in details. obj->Set(v8::String::NewFromUtf8(isolate, "largeIconUrl"), v8::String::NewFromUtf8(isolate, "chrome-search://large-icon/")); obj->Set(v8::String::NewFromUtf8(isolate, "fallbackIconUrl"), v8::String::NewFromUtf8(isolate, "chrome-search://fallback-icon/")); } obj->Set(v8::String::NewFromUtf8(isolate, "title"), UTF16ToV8String(isolate, title)); obj->Set(v8::String::NewFromUtf8(isolate, "domain"), UTF8ToV8String(isolate, mv_item.url.host())); obj->Set(v8::String::NewFromUtf8(isolate, "direction"), UTF8ToV8String(isolate, direction)); obj->Set(v8::String::NewFromUtf8(isolate, "url"), UTF8ToV8String(isolate, mv_item.url.spec())); return obj; } // Returns the render view for the current JS context if it matches |origin|, // otherwise returns NULL. Used to restrict methods that access suggestions and // most visited data to pages with origin chrome-search://most-visited and // chrome-search://suggestions. content::RenderView* GetRenderViewWithCheckedOrigin(const GURL& origin) { blink::WebLocalFrame* webframe = blink::WebLocalFrame::frameForCurrentContext(); if (!webframe) return NULL; blink::WebView* webview = webframe->view(); if (!webview) return NULL; // Can happen during closing. content::RenderView* render_view = content::RenderView::FromWebView(webview); if (!render_view) return NULL; GURL url(webframe->document().url()); if (url.GetOrigin() != origin.GetOrigin()) return NULL; return render_view; } // Returns the current URL. GURL GetCurrentURL(content::RenderView* render_view) { blink::WebView* webview = render_view->GetWebView(); return webview ? GURL(webview->mainFrame()->document().url()) : GURL(); } } // namespace namespace internal { // for testing. // Returns an array with the RGBA color components. v8::Local RGBAColorToArray(v8::Isolate* isolate, const RGBAColor& color) { v8::Local color_array = v8::Array::New(isolate, 4); color_array->Set(0, v8::Int32::New(isolate, color.r)); color_array->Set(1, v8::Int32::New(isolate, color.g)); color_array->Set(2, v8::Int32::New(isolate, color.b)); color_array->Set(3, v8::Int32::New(isolate, color.a)); return color_array; } // Resolves a possibly relative URL using the current URL. GURL ResolveURL(const GURL& current_url, const base::string16& possibly_relative_url) { if (current_url.is_valid() && !possibly_relative_url.empty()) return current_url.Resolve(possibly_relative_url); return GURL(possibly_relative_url); } } // namespace internal namespace extensions_v8 { static const char kSearchBoxExtensionName[] = "v8/EmbeddedSearch"; // We first send this script down to determine if the page supports instant. static const char kSupportsInstantScript[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.searchBox &&" " window.chrome.embeddedSearch.searchBox.onsubmit &&" " typeof window.chrome.embeddedSearch.searchBox.onsubmit ==" " 'function') {" " true;" "} else {" " false;" "}"; static const char kDispatchChromeIdentityCheckResult[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.newTabPage &&" " window.chrome.embeddedSearch.newTabPage.onsignedincheckdone &&" " typeof window.chrome.embeddedSearch.newTabPage" " .onsignedincheckdone === 'function') {" " window.chrome.embeddedSearch.newTabPage.onsignedincheckdone(%s, %s);" " true;" "}"; 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 kDispatchHistorySyncCheckResult[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.newTabPage &&" " window.chrome.embeddedSearch.newTabPage.onhistorysynccheckdone &&" " typeof window.chrome.embeddedSearch.newTabPage" " .onhistorysynccheckdone === 'function') {" " window.chrome.embeddedSearch.newTabPage.onhistorysynccheckdone(%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 kDispatchSubmitEventScript[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.searchBox &&" " window.chrome.embeddedSearch.searchBox.onsubmit &&" " typeof window.chrome.embeddedSearch.searchBox.onsubmit ==" " 'function') {" " window.chrome.embeddedSearch.searchBox.onsubmit();" " true;" "}"; static const char kDispatchSuggestionChangeEventScript[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.searchBox &&" " window.chrome.embeddedSearch.searchBox.onsuggestionchange &&" " typeof window.chrome.embeddedSearch.searchBox.onsuggestionchange ==" " 'function') {" " window.chrome.embeddedSearch.searchBox.onsuggestionchange();" " 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;" "}"; // ---------------------------------------------------------------------------- class SearchBoxExtensionWrapper : public v8::Extension { public: explicit SearchBoxExtensionWrapper(const base::StringPiece& code); // Allows v8's javascript code to call the native functions defined // in this class for window.chrome. v8::Local GetNativeFunctionTemplate( v8::Isolate*, v8::Local name) override; // Helper function to find the RenderView. May return NULL. static content::RenderView* GetRenderView(); // Sends a Chrome identity check to the browser. static void CheckIsUserSignedInToChromeAs( const v8::FunctionCallbackInfo& args); // Checks whether the user sync his history. static void CheckIsUserSyncingHistory( const v8::FunctionCallbackInfo& args); // Deletes a Most Visited item. static void DeleteMostVisitedItem( const v8::FunctionCallbackInfo& args); // Focuses the omnibox. static void Focus(const v8::FunctionCallbackInfo& args); // Gets whether or not the app launcher is enabled. static void GetAppLauncherEnabled( const v8::FunctionCallbackInfo& args); // Gets the desired navigation behavior from a click event. static void GetDispositionFromClick( const v8::FunctionCallbackInfo& args); // Gets Most Visited Items. static void GetMostVisitedItems( const v8::FunctionCallbackInfo& args); // Gets the raw data for a most visited item including its raw URL. // GetRenderViewWithCheckedOrigin() enforces that only code in the origin // chrome-search://most-visited can call this function. static void GetMostVisitedItemData( const v8::FunctionCallbackInfo& args); // Gets the submitted value of the user's search query. static void GetQuery(const v8::FunctionCallbackInfo& args); // Returns true if the Searchbox itself is oriented right-to-left. static void GetRightToLeft(const v8::FunctionCallbackInfo& args); // Gets the Embedded Search request params. Used for logging purposes. static void GetSearchRequestParams( const v8::FunctionCallbackInfo& args); // Gets the current top suggestion to prefetch search results. static void GetSuggestionToPrefetch( const v8::FunctionCallbackInfo& args); // Gets the background info of the theme currently adopted by browser. // Call only when overlay is showing NTP page. static void GetThemeBackgroundInfo( const v8::FunctionCallbackInfo& args); // Gets whether the omnibox has focus or not. static void IsFocused(const v8::FunctionCallbackInfo& args); // Gets whether user input is in progress. static void IsInputInProgress( const v8::FunctionCallbackInfo& args); // Gets whether the browser is capturing key strokes. static void IsKeyCaptureEnabled( const v8::FunctionCallbackInfo& args); // Logs information from the iframes/titles on the NTP. static void LogEvent(const v8::FunctionCallbackInfo& args); // Logs an impression on one of the Most Visited tile on the NTP. static void LogMostVisitedImpression( const v8::FunctionCallbackInfo& args); // Logs a navigation on one of the Most Visited tile on the NTP. static void LogMostVisitedNavigation( const v8::FunctionCallbackInfo& args); // Navigates the window to a URL represented by either a URL string or a // restricted ID. static void NavigateContentWindow( const v8::FunctionCallbackInfo& args); // Pastes provided value or clipboard's content into the omnibox. static void Paste(const v8::FunctionCallbackInfo& args); // Start capturing user key strokes. static void StartCapturingKeyStrokes( const v8::FunctionCallbackInfo& args); // Stop capturing user key strokes. static void StopCapturingKeyStrokes( const v8::FunctionCallbackInfo& args); // Undoes the deletion of all Most Visited itens. static void UndoAllMostVisitedDeletions( const v8::FunctionCallbackInfo& args); // Undoes the deletion of a Most Visited item. static void UndoMostVisitedDeletion( const v8::FunctionCallbackInfo& args); // Indicates whether the page supports Instant. static void GetDisplayInstantResults( const v8::FunctionCallbackInfo& args); private: DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); }; // static v8::Extension* SearchBoxExtension::Get() { return new SearchBoxExtensionWrapper(ResourceBundle::GetSharedInstance(). GetRawDataResource(IDR_SEARCHBOX_API)); } // static bool SearchBoxExtension::PageSupportsInstant(blink::WebFrame* frame) { if (!frame) return false; v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); v8::Local v = frame->executeScriptAndReturnValue( blink::WebScriptSource(kSupportsInstantScript)); return !v.IsEmpty() && v->BooleanValue(); } // static void SearchBoxExtension::DispatchChromeIdentityCheckResult( blink::WebFrame* frame, const base::string16& identity, bool identity_match) { std::string escaped_identity = base::GetQuotedJSONString(identity); blink::WebString script(base::UTF8ToUTF16(base::StringPrintf( kDispatchChromeIdentityCheckResult, escaped_identity.c_str(), identity_match ? "true" : "false"))); Dispatch(frame, script); } // static void SearchBoxExtension::DispatchFocusChange(blink::WebFrame* frame) { Dispatch(frame, kDispatchFocusChangedScript); } // static void SearchBoxExtension::DispatchHistorySyncCheckResult( blink::WebFrame* frame, bool sync_history) { blink::WebString script(base::UTF8ToUTF16(base::StringPrintf( kDispatchHistorySyncCheckResult, sync_history ? "true" : "false"))); Dispatch(frame, script); } // static void SearchBoxExtension::DispatchInputCancel(blink::WebFrame* frame) { Dispatch(frame, kDispatchInputCancelScript); } // static void SearchBoxExtension::DispatchInputStart(blink::WebFrame* frame) { Dispatch(frame, kDispatchInputStartScript); } // static void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebFrame* frame) { Dispatch(frame, kDispatchKeyCaptureChangeScript); } // static void SearchBoxExtension::DispatchMostVisitedChanged( blink::WebFrame* frame) { Dispatch(frame, kDispatchMostVisitedChangedScript); } // static void SearchBoxExtension::DispatchSubmit(blink::WebFrame* frame) { Dispatch(frame, kDispatchSubmitEventScript); } // static void SearchBoxExtension::DispatchSuggestionChange(blink::WebFrame* frame) { Dispatch(frame, kDispatchSuggestionChangeEventScript); } // static void SearchBoxExtension::DispatchThemeChange(blink::WebFrame* frame) { Dispatch(frame, kDispatchThemeChangeEventScript); } SearchBoxExtensionWrapper::SearchBoxExtensionWrapper( const base::StringPiece& code) : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) { } v8::Local SearchBoxExtensionWrapper::GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Local name) { if (name->Equals( v8::String::NewFromUtf8(isolate, "CheckIsUserSignedInToChromeAs"))) return v8::FunctionTemplate::New(isolate, CheckIsUserSignedInToChromeAs); if (name->Equals( v8::String::NewFromUtf8(isolate, "CheckIsUserSyncingHistory"))) return v8::FunctionTemplate::New(isolate, CheckIsUserSyncingHistory); if (name->Equals(v8::String::NewFromUtf8(isolate, "DeleteMostVisitedItem"))) return v8::FunctionTemplate::New(isolate, DeleteMostVisitedItem); if (name->Equals(v8::String::NewFromUtf8(isolate, "Focus"))) return v8::FunctionTemplate::New(isolate, Focus); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetAppLauncherEnabled"))) return v8::FunctionTemplate::New(isolate, GetAppLauncherEnabled); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetDispositionFromClick"))) return v8::FunctionTemplate::New(isolate, GetDispositionFromClick); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItems"))) return v8::FunctionTemplate::New(isolate, GetMostVisitedItems); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItemData"))) return v8::FunctionTemplate::New(isolate, GetMostVisitedItemData); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetQuery"))) return v8::FunctionTemplate::New(isolate, GetQuery); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetRightToLeft"))) return v8::FunctionTemplate::New(isolate, GetRightToLeft); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSearchRequestParams"))) return v8::FunctionTemplate::New(isolate, GetSearchRequestParams); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSuggestionToPrefetch"))) return v8::FunctionTemplate::New(isolate, GetSuggestionToPrefetch); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetThemeBackgroundInfo"))) return v8::FunctionTemplate::New(isolate, GetThemeBackgroundInfo); if (name->Equals(v8::String::NewFromUtf8(isolate, "IsFocused"))) return v8::FunctionTemplate::New(isolate, IsFocused); if (name->Equals(v8::String::NewFromUtf8(isolate, "IsInputInProgress"))) return v8::FunctionTemplate::New(isolate, IsInputInProgress); if (name->Equals(v8::String::NewFromUtf8(isolate, "IsKeyCaptureEnabled"))) return v8::FunctionTemplate::New(isolate, IsKeyCaptureEnabled); if (name->Equals(v8::String::NewFromUtf8(isolate, "LogEvent"))) return v8::FunctionTemplate::New(isolate, LogEvent); if (name->Equals( v8::String::NewFromUtf8(isolate, "LogMostVisitedImpression"))) { return v8::FunctionTemplate::New(isolate, LogMostVisitedImpression); } if (name->Equals( v8::String::NewFromUtf8(isolate, "LogMostVisitedNavigation"))) { return v8::FunctionTemplate::New(isolate, LogMostVisitedNavigation); } if (name->Equals(v8::String::NewFromUtf8(isolate, "NavigateContentWindow"))) return v8::FunctionTemplate::New(isolate, NavigateContentWindow); if (name->Equals(v8::String::NewFromUtf8(isolate, "Paste"))) return v8::FunctionTemplate::New(isolate, Paste); if (name->Equals( v8::String::NewFromUtf8(isolate, "StartCapturingKeyStrokes"))) return v8::FunctionTemplate::New(isolate, StartCapturingKeyStrokes); if (name->Equals(v8::String::NewFromUtf8(isolate, "StopCapturingKeyStrokes"))) return v8::FunctionTemplate::New(isolate, StopCapturingKeyStrokes); if (name->Equals( v8::String::NewFromUtf8(isolate, "UndoAllMostVisitedDeletions"))) return v8::FunctionTemplate::New(isolate, UndoAllMostVisitedDeletions); if (name->Equals(v8::String::NewFromUtf8(isolate, "UndoMostVisitedDeletion"))) return v8::FunctionTemplate::New(isolate, UndoMostVisitedDeletion); if (name->Equals( v8::String::NewFromUtf8(isolate, "GetDisplayInstantResults"))) return v8::FunctionTemplate::New(isolate, GetDisplayInstantResults); return v8::Local(); } // static content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { blink::WebLocalFrame* webframe = blink::WebLocalFrame::frameForCurrentContext(); if (!webframe) return NULL; blink::WebView* webview = webframe->view(); if (!webview) return NULL; // can happen during closing return content::RenderView::FromWebView(webview); } // static void SearchBoxExtensionWrapper::CheckIsUserSignedInToChromeAs( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; if (!args.Length() || args[0]->IsUndefined()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " CheckIsUserSignedInToChromeAs"; SearchBox::Get(render_view)->CheckIsUserSignedInToChromeAs( V8ValueToUTF16(args[0])); } // static void SearchBoxExtensionWrapper::CheckIsUserSyncingHistory( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " CheckIsUserSyncingHistory"; SearchBox::Get(render_view)->CheckIsUserSyncingHistory(); } // static void SearchBoxExtensionWrapper::DeleteMostVisitedItem( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; if (!args.Length()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " DeleteMostVisitedItem: " << args[0]->ToInteger()->Value(); SearchBox::Get(render_view)-> DeleteMostVisitedItem(args[0]->ToInteger()->Value()); } // static void SearchBoxExtensionWrapper::Focus( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " Focus"; SearchBox::Get(render_view)->Focus(); } // static void SearchBoxExtensionWrapper::GetAppLauncherEnabled( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; args.GetReturnValue().Set( SearchBox::Get(render_view)->app_launcher_enabled()); } // static void SearchBoxExtensionWrapper::GetDispositionFromClick( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; if (args.Length() != 5) { ThrowInvalidParameters(args); return; } bool middle_button = args[0]->BooleanValue(); bool alt_key = args[1]->BooleanValue(); bool ctrl_key = args[2]->BooleanValue(); bool meta_key = args[3]->BooleanValue(); bool shift_key = args[4]->BooleanValue(); WindowOpenDisposition disposition = ui::DispositionFromClick(middle_button, alt_key, ctrl_key, meta_key, shift_key); v8::Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(v8::Int32::New(isolate, disposition)); } // static void SearchBoxExtensionWrapper::GetMostVisitedItems( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " GetMostVisitedItems"; const SearchBox* search_box = SearchBox::Get(render_view); std::vector instant_mv_items; search_box->GetMostVisitedItems(&instant_mv_items); v8::Isolate* isolate = args.GetIsolate(); v8::Local v8_mv_items = v8::Array::New(isolate, instant_mv_items.size()); for (size_t i = 0; i < instant_mv_items.size(); ++i) { v8_mv_items->Set(i, GenerateMostVisitedItem(isolate, render_view->GetRoutingID(), instant_mv_items[i].first, instant_mv_items[i].second)); } args.GetReturnValue().Set(v8_mv_items); } // static void SearchBoxExtensionWrapper::GetMostVisitedItemData( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderViewWithCheckedOrigin( GURL(chrome::kChromeSearchMostVisitedUrl)); if (!render_view) return; // Need an rid argument. if (!args.Length() || !args[0]->IsNumber()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " GetMostVisitedItem"; InstantRestrictedID restricted_id = args[0]->IntegerValue(); InstantMostVisitedItem mv_item; if (!SearchBox::Get(render_view)->GetMostVisitedItemWithID( restricted_id, &mv_item)) { return; } v8::Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(GenerateMostVisitedItem( isolate, render_view->GetRoutingID(), restricted_id, mv_item)); } // static void SearchBoxExtensionWrapper::GetQuery( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; const base::string16& query = SearchBox::Get(render_view)->query(); DVLOG(1) << render_view << " GetQuery: '" << query << "'"; v8::Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(UTF16ToV8String(isolate, query)); } // static void SearchBoxExtensionWrapper::GetRightToLeft( const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(base::i18n::IsRTL()); } // static void SearchBoxExtensionWrapper::GetSearchRequestParams( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; const EmbeddedSearchRequestParams& params = SearchBox::Get(render_view)->GetEmbeddedSearchRequestParams(); v8::Isolate* isolate = args.GetIsolate(); v8::Local data = v8::Object::New(isolate); if (!params.search_query.empty()) { data->Set(v8::String::NewFromUtf8(isolate, kSearchQueryKey), UTF16ToV8String(isolate, params.search_query)); } if (!params.original_query.empty()) { data->Set(v8::String::NewFromUtf8(isolate, kOriginalQueryKey), UTF16ToV8String(isolate, params.original_query)); } if (!params.rlz_parameter_value.empty()) { data->Set(v8::String::NewFromUtf8(isolate, kRLZParameterKey), UTF16ToV8String(isolate, params.rlz_parameter_value)); } if (!params.input_encoding.empty()) { data->Set(v8::String::NewFromUtf8(isolate, kInputEncodingKey), UTF16ToV8String(isolate, params.input_encoding)); } if (!params.assisted_query_stats.empty()) { data->Set(v8::String::NewFromUtf8(isolate, kAssistedQueryStatsKey), UTF16ToV8String(isolate, params.assisted_query_stats)); } args.GetReturnValue().Set(data); } // static void SearchBoxExtensionWrapper::GetSuggestionToPrefetch( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; const InstantSuggestion& suggestion = SearchBox::Get(render_view)->suggestion(); v8::Isolate* isolate = args.GetIsolate(); v8::Local data = v8::Object::New(isolate); data->Set(v8::String::NewFromUtf8(isolate, "text"), UTF16ToV8String(isolate, suggestion.text)); data->Set(v8::String::NewFromUtf8(isolate, "metadata"), UTF8ToV8String(isolate, suggestion.metadata)); args.GetReturnValue().Set(data); } // static void SearchBoxExtensionWrapper::GetThemeBackgroundInfo( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " GetThemeBackgroundInfo"; const ThemeBackgroundInfo& theme_info = SearchBox::Get(render_view)->GetThemeBackgroundInfo(); v8::Isolate* isolate = args.GetIsolate(); v8::Local info = v8::Object::New(isolate); info->Set(v8::String::NewFromUtf8(isolate, "usingDefaultTheme"), v8::Boolean::New(isolate, theme_info.using_default_theme)); // The theme background color is in RGBA format "rgba(R,G,B,A)" where R, G and // B are between 0 and 255 inclusive, and A is a double between 0 and 1 // inclusive. // This is the CSS "background-color" format. // Value is always valid. // TODO(jfweitz): Remove this field after GWS is modified to use the new // backgroundColorRgba field. info->Set( v8::String::NewFromUtf8(isolate, "colorRgba"), UTF8ToV8String( isolate, // Convert the alpha using DoubleToString because StringPrintf will // use // locale specific formatters (e.g., use , instead of . in German). base::StringPrintf( kCSSBackgroundColorFormat, theme_info.background_color.r, theme_info.background_color.g, theme_info.background_color.b, base::DoubleToString(theme_info.background_color.a / 255.0) .c_str()))); // Theme color for background as an array with the RGBA components in order. // Value is always valid. info->Set(v8::String::NewFromUtf8(isolate, "backgroundColorRgba"), internal::RGBAColorToArray(isolate, theme_info.background_color)); // Theme color for text as an array with the RGBA components in order. // Value is always valid. info->Set(v8::String::NewFromUtf8(isolate, "textColorRgba"), internal::RGBAColorToArray(isolate, theme_info.text_color)); // Theme color for links as an array with the RGBA components in order. // Value is always valid. info->Set(v8::String::NewFromUtf8(isolate, "linkColorRgba"), internal::RGBAColorToArray(isolate, theme_info.link_color)); // Theme color for light text as an array with the RGBA components in order. // Value is always valid. info->Set(v8::String::NewFromUtf8(isolate, "textColorLightRgba"), internal::RGBAColorToArray(isolate, theme_info.text_color_light)); // Theme color for header as an array with the RGBA components in order. // Value is always valid. info->Set(v8::String::NewFromUtf8(isolate, "headerColorRgba"), internal::RGBAColorToArray(isolate, theme_info.header_color)); // Theme color for section border as an array with the RGBA components in // order. Value is always valid. info->Set( v8::String::NewFromUtf8(isolate, "sectionBorderColorRgba"), internal::RGBAColorToArray(isolate, theme_info.section_border_color)); // The theme alternate logo value indicates a white logo when TRUE and a // colorful one when FALSE. info->Set(v8::String::NewFromUtf8(isolate, "alternateLogo"), v8::Boolean::New(isolate, 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 (crx_file::id_util::IdIsValid(theme_info.theme_id)) { info->Set(v8::String::NewFromUtf8(isolate, "imageUrl"), UTF8ToV8String(isolate, 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; } info->Set(v8::String::NewFromUtf8(isolate, "imageHorizontalAlignment"), UTF8ToV8String(isolate, 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; } info->Set(v8::String::NewFromUtf8(isolate, "imageVerticalAlignment"), UTF8ToV8String(isolate, 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; } info->Set(v8::String::NewFromUtf8(isolate, "imageTiling"), UTF8ToV8String(isolate, tiling)); // The theme background image height is only valid if |imageUrl| is valid. info->Set(v8::String::NewFromUtf8(isolate, "imageHeight"), v8::Int32::New(isolate, theme_info.image_height)); // The attribution URL is only valid if the theme has attribution logo. if (theme_info.has_attribution) { info->Set( v8::String::NewFromUtf8(isolate, "attributionUrl"), UTF8ToV8String(isolate, base::StringPrintf(kThemeAttributionFormat, theme_info.theme_id.c_str(), theme_info.theme_id.c_str()))); } } args.GetReturnValue().Set(info); } // static void SearchBoxExtensionWrapper::IsFocused( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; bool is_focused = SearchBox::Get(render_view)->is_focused(); DVLOG(1) << render_view << " IsFocused: " << is_focused; args.GetReturnValue().Set(is_focused); } // static void SearchBoxExtensionWrapper::IsInputInProgress( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; bool is_input_in_progress = SearchBox::Get(render_view)->is_input_in_progress(); DVLOG(1) << render_view << " IsInputInProgress: " << is_input_in_progress; args.GetReturnValue().Set(is_input_in_progress); } // static void SearchBoxExtensionWrapper::IsKeyCaptureEnabled( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; args.GetReturnValue().Set(SearchBox::Get(render_view)-> is_key_capture_enabled()); } // static void SearchBoxExtensionWrapper::LogEvent( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderViewWithCheckedOrigin( GURL(chrome::kChromeSearchMostVisitedUrl)); if (!render_view) return; if (!args.Length() || !args[0]->IsNumber()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " LogEvent"; if (args[0]->Uint32Value() <= NTP_EVENT_TYPE_LAST) { NTPLoggingEventType event = static_cast(args[0]->Uint32Value()); SearchBox::Get(render_view)->LogEvent(event); } } // static void SearchBoxExtensionWrapper::LogMostVisitedImpression( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderViewWithCheckedOrigin( GURL(chrome::kChromeSearchMostVisitedUrl)); if (!render_view) return; if (args.Length() < 2 || !args[0]->IsNumber() || args[1]->IsUndefined()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " LogMostVisitedImpression"; SearchBox::Get(render_view)->LogMostVisitedImpression( args[0]->IntegerValue(), V8ValueToUTF16(args[1])); } // static void SearchBoxExtensionWrapper::LogMostVisitedNavigation( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderViewWithCheckedOrigin( GURL(chrome::kChromeSearchMostVisitedUrl)); if (!render_view) return; if (args.Length() < 2 || !args[0]->IsNumber() || args[1]->IsUndefined()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " LogMostVisitedNavigation"; SearchBox::Get(render_view)->LogMostVisitedNavigation( args[0]->IntegerValue(), V8ValueToUTF16(args[1])); } // static void SearchBoxExtensionWrapper::NavigateContentWindow( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; if (!args.Length()) { ThrowInvalidParameters(args); return; } GURL destination_url; bool is_most_visited_item_url = false; // Check if the url is a rid if (args[0]->IsNumber()) { InstantMostVisitedItem item; if (SearchBox::Get(render_view)->GetMostVisitedItemWithID( args[0]->IntegerValue(), &item)) { destination_url = item.url; is_most_visited_item_url = true; } } else { // Resolve the URL const base::string16& possibly_relative_url = V8ValueToUTF16(args[0]); GURL current_url = GetCurrentURL(render_view); destination_url = internal::ResolveURL(current_url, possibly_relative_url); } DVLOG(1) << render_view << " NavigateContentWindow: " << destination_url; // Navigate the main frame. Note that the security checks are enforced by the // browser process in InstantService::IsValidURLForNavigation(), but some // simple checks here are useful for avoiding unnecessary IPCs. if (destination_url.is_valid() && !destination_url.SchemeIs(url::kJavaScriptScheme)) { WindowOpenDisposition disposition = CURRENT_TAB; if (args[1]->IsNumber()) { disposition = (WindowOpenDisposition) args[1]->Uint32Value(); } SearchBox::Get(render_view)->NavigateToURL(destination_url, disposition, is_most_visited_item_url); } } // static void SearchBoxExtensionWrapper::Paste( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; base::string16 text; if (!args[0]->IsUndefined()) text = V8ValueToUTF16(args[0]); DVLOG(1) << render_view << " Paste: " << text; SearchBox::Get(render_view)->Paste(text); } // static void SearchBoxExtensionWrapper::StartCapturingKeyStrokes( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " StartCapturingKeyStrokes"; SearchBox::Get(render_view)->StartCapturingKeyStrokes(); } // static void SearchBoxExtensionWrapper::StopCapturingKeyStrokes( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " StopCapturingKeyStrokes"; SearchBox::Get(render_view)->StopCapturingKeyStrokes(); } // static void SearchBoxExtensionWrapper::UndoAllMostVisitedDeletions( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; DVLOG(1) << render_view << " UndoAllMostVisitedDeletions"; SearchBox::Get(render_view)->UndoAllMostVisitedDeletions(); } // static void SearchBoxExtensionWrapper::UndoMostVisitedDeletion( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) { return; } if (!args.Length()) { ThrowInvalidParameters(args); return; } DVLOG(1) << render_view << " UndoMostVisitedDeletion"; SearchBox::Get(render_view) ->UndoMostVisitedDeletion(args[0]->ToInteger()->Value()); } // static void SearchBoxExtensionWrapper::GetDisplayInstantResults( const v8::FunctionCallbackInfo& args) { content::RenderView* render_view = GetRenderView(); if (!render_view) return; bool display_instant_results = SearchBox::Get(render_view)->display_instant_results(); DVLOG(1) << render_view << " GetDisplayInstantResults" << display_instant_results; args.GetReturnValue().Set(display_instant_results); } } // namespace extensions_v8