1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/searchbox/searchbox_extension.h"
7 #include "base/i18n/rtl.h"
8 #include "base/json/string_escape.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/common/instant_types.h"
14 #include "chrome/common/ntp_logging_events.h"
15 #include "chrome/common/url_constants.h"
16 #include "chrome/grit/renderer_resources.h"
17 #include "chrome/renderer/searchbox/searchbox.h"
18 #include "components/crx_file/id_util.h"
19 #include "content/public/renderer/render_view.h"
20 #include "third_party/WebKit/public/platform/WebURLRequest.h"
21 #include "third_party/WebKit/public/web/WebDocument.h"
22 #include "third_party/WebKit/public/web/WebLocalFrame.h"
23 #include "third_party/WebKit/public/web/WebScriptSource.h"
24 #include "third_party/WebKit/public/web/WebView.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/base/window_open_disposition.h"
27 #include "ui/events/keycodes/keyboard_codes.h"
29 #include "url/url_constants.h"
30 #include "v8/include/v8.h"
34 const char kCSSBackgroundImageFormat[] = "-webkit-image-set("
35 "url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND?%s) 1x, "
36 "url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND@2x?%s) 2x)";
38 const char kCSSBackgroundColorFormat[] = "rgba(%d,%d,%d,%s)";
40 const char kCSSBackgroundPositionCenter[] = "center";
41 const char kCSSBackgroundPositionLeft[] = "left";
42 const char kCSSBackgroundPositionTop[] = "top";
43 const char kCSSBackgroundPositionRight[] = "right";
44 const char kCSSBackgroundPositionBottom[] = "bottom";
46 const char kCSSBackgroundRepeatNo[] = "no-repeat";
47 const char kCSSBackgroundRepeatX[] = "repeat-x";
48 const char kCSSBackgroundRepeatY[] = "repeat-y";
49 const char kCSSBackgroundRepeat[] = "repeat";
51 const char kThemeAttributionFormat[] = "-webkit-image-set("
52 "url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION?%s) 1x, "
53 "url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION@2x?%s) 2x)";
55 const char kLTRHtmlTextDirection[] = "ltr";
56 const char kRTLHtmlTextDirection[] = "rtl";
58 // Converts a V8 value to a string16.
59 base::string16 V8ValueToUTF16(v8::Handle<v8::Value> v) {
60 v8::String::Value s(v);
61 return base::string16(reinterpret_cast<const base::char16*>(*s), s.length());
64 // Converts string16 to V8 String.
65 v8::Handle<v8::String> UTF16ToV8String(v8::Isolate* isolate,
66 const base::string16& s) {
67 return v8::String::NewFromTwoByte(isolate,
68 reinterpret_cast<const uint16_t*>(s.data()),
69 v8::String::kNormalString,
73 // Converts std::string to V8 String.
74 v8::Handle<v8::String> UTF8ToV8String(v8::Isolate* isolate,
75 const std::string& s) {
76 return v8::String::NewFromUtf8(
77 isolate, s.data(), v8::String::kNormalString, s.size());
80 void Dispatch(blink::WebFrame* frame, const blink::WebString& script) {
82 frame->executeScript(blink::WebScriptSource(script));
85 v8::Handle<v8::String> GenerateThumbnailURL(
88 InstantRestrictedID most_visited_item_id) {
89 return UTF8ToV8String(
92 "chrome-search://thumb/%d/%d", render_view_id, most_visited_item_id));
95 // Populates a Javascript MostVisitedItem object from |mv_item|.
96 // NOTE: Includes "url", "title" and "domain" which are private data, so should
97 // not be returned to the Instant page. These should be erased before returning
98 // the object. See GetMostVisitedItemsWrapper() in searchbox_api.js.
99 v8::Handle<v8::Object> GenerateMostVisitedItem(
100 v8::Isolate* isolate,
102 InstantRestrictedID restricted_id,
103 const InstantMostVisitedItem& mv_item) {
104 // We set the "dir" attribute of the title, so that in RTL locales, a LTR
105 // title is rendered left-to-right and truncated from the right. For
106 // example, the title of http://msdn.microsoft.com/en-us/default.aspx is
107 // "MSDN: Microsoft developer network". In RTL locales, in the New Tab
108 // page, if the "dir" of this title is not specified, it takes Chrome UI's
109 // directionality. So the title will be truncated as "soft developer
110 // network". Setting the "dir" attribute as "ltr" renders the truncated
111 // title as "MSDN: Microsoft D...". As another example, the title of
112 // http://yahoo.com is "Yahoo!". In RTL locales, in the New Tab page, the
113 // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
115 std::string direction;
116 if (base::i18n::StringContainsStrongRTLChars(mv_item.title))
117 direction = kRTLHtmlTextDirection;
119 direction = kLTRHtmlTextDirection;
121 base::string16 title = mv_item.title;
123 title = base::UTF8ToUTF16(mv_item.url.spec());
125 v8::Handle<v8::Object> obj = v8::Object::New(isolate);
126 obj->Set(v8::String::NewFromUtf8(isolate, "renderViewId"),
127 v8::Int32::New(isolate, render_view_id));
128 obj->Set(v8::String::NewFromUtf8(isolate, "rid"),
129 v8::Int32::New(isolate, restricted_id));
130 obj->Set(v8::String::NewFromUtf8(isolate, "thumbnailUrl"),
131 GenerateThumbnailURL(isolate, render_view_id, restricted_id));
132 obj->Set(v8::String::NewFromUtf8(isolate, "title"),
133 UTF16ToV8String(isolate, title));
134 obj->Set(v8::String::NewFromUtf8(isolate, "domain"),
135 UTF8ToV8String(isolate, mv_item.url.host()));
136 obj->Set(v8::String::NewFromUtf8(isolate, "direction"),
137 UTF8ToV8String(isolate, direction));
138 obj->Set(v8::String::NewFromUtf8(isolate, "url"),
139 UTF8ToV8String(isolate, mv_item.url.spec()));
143 // Returns the render view for the current JS context if it matches |origin|,
144 // otherwise returns NULL. Used to restrict methods that access suggestions and
145 // most visited data to pages with origin chrome-search://most-visited and
146 // chrome-search://suggestions.
147 content::RenderView* GetRenderViewWithCheckedOrigin(const GURL& origin) {
148 blink::WebLocalFrame* webframe =
149 blink::WebLocalFrame::frameForCurrentContext();
152 blink::WebView* webview = webframe->view();
154 return NULL; // Can happen during closing.
155 content::RenderView* render_view = content::RenderView::FromWebView(webview);
159 GURL url(webframe->document().url());
160 if (url.GetOrigin() != origin.GetOrigin())
166 // Returns the current URL.
167 GURL GetCurrentURL(content::RenderView* render_view) {
168 blink::WebView* webview = render_view->GetWebView();
169 return webview ? GURL(webview->mainFrame()->document().url()) : GURL();
174 namespace internal { // for testing.
176 // Returns an array with the RGBA color components.
177 v8::Handle<v8::Value> RGBAColorToArray(v8::Isolate* isolate,
178 const RGBAColor& color) {
179 v8::Handle<v8::Array> color_array = v8::Array::New(isolate, 4);
180 color_array->Set(0, v8::Int32::New(isolate, color.r));
181 color_array->Set(1, v8::Int32::New(isolate, color.g));
182 color_array->Set(2, v8::Int32::New(isolate, color.b));
183 color_array->Set(3, v8::Int32::New(isolate, color.a));
187 // Resolves a possibly relative URL using the current URL.
188 GURL ResolveURL(const GURL& current_url,
189 const base::string16& possibly_relative_url) {
190 if (current_url.is_valid() && !possibly_relative_url.empty())
191 return current_url.Resolve(possibly_relative_url);
192 return GURL(possibly_relative_url);
195 } // namespace internal
197 namespace extensions_v8 {
199 static const char kSearchBoxExtensionName[] = "v8/EmbeddedSearch";
201 // We first send this script down to determine if the page supports instant.
202 static const char kSupportsInstantScript[] =
203 "if (window.chrome &&"
204 " window.chrome.embeddedSearch &&"
205 " window.chrome.embeddedSearch.searchBox &&"
206 " window.chrome.embeddedSearch.searchBox.onsubmit &&"
207 " typeof window.chrome.embeddedSearch.searchBox.onsubmit =="
214 static const char kDispatchChromeIdentityCheckResult[] =
215 "if (window.chrome &&"
216 " window.chrome.embeddedSearch &&"
217 " window.chrome.embeddedSearch.newTabPage &&"
218 " window.chrome.embeddedSearch.newTabPage.onsignedincheckdone &&"
219 " typeof window.chrome.embeddedSearch.newTabPage"
220 " .onsignedincheckdone === 'function') {"
221 " window.chrome.embeddedSearch.newTabPage.onsignedincheckdone(%s, %s);"
226 static const char kDispatchFocusChangedScript[] =
227 "if (window.chrome &&"
228 " window.chrome.embeddedSearch &&"
229 " window.chrome.embeddedSearch.searchBox &&"
230 " window.chrome.embeddedSearch.searchBox.onfocuschange &&"
231 " typeof window.chrome.embeddedSearch.searchBox.onfocuschange =="
233 " window.chrome.embeddedSearch.searchBox.onfocuschange();"
237 static const char kDispatchInputCancelScript[] =
238 "if (window.chrome &&"
239 " window.chrome.embeddedSearch &&"
240 " window.chrome.embeddedSearch.newTabPage &&"
241 " window.chrome.embeddedSearch.newTabPage.oninputcancel &&"
242 " typeof window.chrome.embeddedSearch.newTabPage.oninputcancel =="
244 " window.chrome.embeddedSearch.newTabPage.oninputcancel();"
248 static const char kDispatchInputStartScript[] =
249 "if (window.chrome &&"
250 " window.chrome.embeddedSearch &&"
251 " window.chrome.embeddedSearch.newTabPage &&"
252 " window.chrome.embeddedSearch.newTabPage.oninputstart &&"
253 " typeof window.chrome.embeddedSearch.newTabPage.oninputstart =="
255 " window.chrome.embeddedSearch.newTabPage.oninputstart();"
259 static const char kDispatchKeyCaptureChangeScript[] =
260 "if (window.chrome &&"
261 " window.chrome.embeddedSearch &&"
262 " window.chrome.embeddedSearch.searchBox &&"
263 " window.chrome.embeddedSearch.searchBox.onkeycapturechange &&"
264 " typeof window.chrome.embeddedSearch.searchBox.onkeycapturechange =="
266 " window.chrome.embeddedSearch.searchBox.onkeycapturechange();"
270 static const char kDispatchMarginChangeEventScript[] =
271 "if (window.chrome &&"
272 " window.chrome.embeddedSearch &&"
273 " window.chrome.embeddedSearch.searchBox &&"
274 " window.chrome.embeddedSearch.searchBox.onmarginchange &&"
275 " typeof window.chrome.embeddedSearch.searchBox.onmarginchange =="
277 " window.chrome.embeddedSearch.searchBox.onmarginchange();"
281 static const char kDispatchMostVisitedChangedScript[] =
282 "if (window.chrome &&"
283 " window.chrome.embeddedSearch &&"
284 " window.chrome.embeddedSearch.newTabPage &&"
285 " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange &&"
286 " typeof window.chrome.embeddedSearch.newTabPage.onmostvisitedchange =="
288 " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange();"
292 static const char kDispatchSubmitEventScript[] =
293 "if (window.chrome &&"
294 " window.chrome.embeddedSearch &&"
295 " window.chrome.embeddedSearch.searchBox &&"
296 " window.chrome.embeddedSearch.searchBox.onsubmit &&"
297 " typeof window.chrome.embeddedSearch.searchBox.onsubmit =="
299 " window.chrome.embeddedSearch.searchBox.onsubmit();"
303 static const char kDispatchSuggestionChangeEventScript[] =
304 "if (window.chrome &&"
305 " window.chrome.embeddedSearch &&"
306 " window.chrome.embeddedSearch.searchBox &&"
307 " window.chrome.embeddedSearch.searchBox.onsuggestionchange &&"
308 " typeof window.chrome.embeddedSearch.searchBox.onsuggestionchange =="
310 " window.chrome.embeddedSearch.searchBox.onsuggestionchange();"
314 static const char kDispatchThemeChangeEventScript[] =
315 "if (window.chrome &&"
316 " window.chrome.embeddedSearch &&"
317 " window.chrome.embeddedSearch.newTabPage &&"
318 " window.chrome.embeddedSearch.newTabPage.onthemechange &&"
319 " typeof window.chrome.embeddedSearch.newTabPage.onthemechange =="
321 " window.chrome.embeddedSearch.newTabPage.onthemechange();"
325 static const char kDispatchToggleVoiceSearchScript[] =
326 "if (window.chrome &&"
327 " window.chrome.embeddedSearch &&"
328 " window.chrome.embeddedSearch.searchBox &&"
329 " window.chrome.embeddedSearch.searchBox.ontogglevoicesearch &&"
330 " typeof window.chrome.embeddedSearch.searchBox.ontogglevoicesearch =="
332 " window.chrome.embeddedSearch.searchBox.ontogglevoicesearch();"
336 // ----------------------------------------------------------------------------
338 class SearchBoxExtensionWrapper : public v8::Extension {
340 explicit SearchBoxExtensionWrapper(const base::StringPiece& code);
342 // Allows v8's javascript code to call the native functions defined
343 // in this class for window.chrome.
344 v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
346 v8::Handle<v8::String> name) override;
348 // Helper function to find the RenderView. May return NULL.
349 static content::RenderView* GetRenderView();
351 // Sends a Chrome identity check to the browser.
352 static void CheckIsUserSignedInToChromeAs(
353 const v8::FunctionCallbackInfo<v8::Value>& args);
355 // Deletes a Most Visited item.
356 static void DeleteMostVisitedItem(
357 const v8::FunctionCallbackInfo<v8::Value>& args);
359 // Focuses the omnibox.
360 static void Focus(const v8::FunctionCallbackInfo<v8::Value>& args);
362 // Gets whether or not the app launcher is enabled.
363 static void GetAppLauncherEnabled(
364 const v8::FunctionCallbackInfo<v8::Value>& args);
366 // Gets the desired navigation behavior from a click event.
367 static void GetDispositionFromClick(
368 const v8::FunctionCallbackInfo<v8::Value>& args);
370 // Gets Most Visited Items.
371 static void GetMostVisitedItems(
372 const v8::FunctionCallbackInfo<v8::Value>& args);
374 // Gets the raw data for a most visited item including its raw URL.
375 // GetRenderViewWithCheckedOrigin() enforces that only code in the origin
376 // chrome-search://most-visited can call this function.
377 static void GetMostVisitedItemData(
378 const v8::FunctionCallbackInfo<v8::Value>& args);
380 // Gets the submitted value of the user's search query.
381 static void GetQuery(const v8::FunctionCallbackInfo<v8::Value>& args);
383 // Returns true if the Searchbox itself is oriented right-to-left.
384 static void GetRightToLeft(const v8::FunctionCallbackInfo<v8::Value>& args);
386 // Gets the Embedded Search request params. Used for logging purposes.
387 static void GetSearchRequestParams(
388 const v8::FunctionCallbackInfo<v8::Value>& args);
390 // Gets the start-edge margin to use with extended Instant.
391 static void GetStartMargin(const v8::FunctionCallbackInfo<v8::Value>& args);
393 // Gets the current top suggestion to prefetch search results.
394 static void GetSuggestionToPrefetch(
395 const v8::FunctionCallbackInfo<v8::Value>& args);
397 // Gets the background info of the theme currently adopted by browser.
398 // Call only when overlay is showing NTP page.
399 static void GetThemeBackgroundInfo(
400 const v8::FunctionCallbackInfo<v8::Value>& args);
402 // Gets whether the omnibox has focus or not.
403 static void IsFocused(const v8::FunctionCallbackInfo<v8::Value>& args);
405 // Gets whether user input is in progress.
406 static void IsInputInProgress(
407 const v8::FunctionCallbackInfo<v8::Value>& args);
409 // Gets whether the browser is capturing key strokes.
410 static void IsKeyCaptureEnabled(
411 const v8::FunctionCallbackInfo<v8::Value>& args);
413 // Logs information from the iframes/titles on the NTP.
414 static void LogEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
416 // Logs an impression on one of the Most Visited tile on the NTP.
417 static void LogMostVisitedImpression(
418 const v8::FunctionCallbackInfo<v8::Value>& args);
420 // Logs a navigation on one of the Most Visited tile on the NTP.
421 static void LogMostVisitedNavigation(
422 const v8::FunctionCallbackInfo<v8::Value>& args);
424 // Navigates the window to a URL represented by either a URL string or a
426 static void NavigateContentWindow(
427 const v8::FunctionCallbackInfo<v8::Value>& args);
429 // Pastes provided value or clipboard's content into the omnibox.
430 static void Paste(const v8::FunctionCallbackInfo<v8::Value>& args);
432 // Indicates whether the page supports voice search.
433 static void SetVoiceSearchSupported(
434 const v8::FunctionCallbackInfo<v8::Value>& args);
436 // Start capturing user key strokes.
437 static void StartCapturingKeyStrokes(
438 const v8::FunctionCallbackInfo<v8::Value>& args);
440 // Stop capturing user key strokes.
441 static void StopCapturingKeyStrokes(
442 const v8::FunctionCallbackInfo<v8::Value>& args);
444 // Undoes the deletion of all Most Visited itens.
445 static void UndoAllMostVisitedDeletions(
446 const v8::FunctionCallbackInfo<v8::Value>& args);
448 // Undoes the deletion of a Most Visited item.
449 static void UndoMostVisitedDeletion(
450 const v8::FunctionCallbackInfo<v8::Value>& args);
452 // Indicates whether the page supports Instant.
453 static void GetDisplayInstantResults(
454 const v8::FunctionCallbackInfo<v8::Value>& args);
457 DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper);
461 v8::Extension* SearchBoxExtension::Get() {
462 return new SearchBoxExtensionWrapper(ResourceBundle::GetSharedInstance().
463 GetRawDataResource(IDR_SEARCHBOX_API));
467 bool SearchBoxExtension::PageSupportsInstant(blink::WebFrame* frame) {
468 if (!frame) return false;
469 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
470 v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue(
471 blink::WebScriptSource(kSupportsInstantScript));
472 return !v.IsEmpty() && v->BooleanValue();
476 void SearchBoxExtension::DispatchChromeIdentityCheckResult(
477 blink::WebFrame* frame,
478 const base::string16& identity,
479 bool identity_match) {
480 std::string escaped_identity = base::GetQuotedJSONString(identity);
481 blink::WebString script(base::UTF8ToUTF16(base::StringPrintf(
482 kDispatchChromeIdentityCheckResult,
483 escaped_identity.c_str(),
484 identity_match ? "true" : "false")));
485 Dispatch(frame, script);
489 void SearchBoxExtension::DispatchFocusChange(blink::WebFrame* frame) {
490 Dispatch(frame, kDispatchFocusChangedScript);
494 void SearchBoxExtension::DispatchInputCancel(blink::WebFrame* frame) {
495 Dispatch(frame, kDispatchInputCancelScript);
499 void SearchBoxExtension::DispatchInputStart(blink::WebFrame* frame) {
500 Dispatch(frame, kDispatchInputStartScript);
504 void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebFrame* frame) {
505 Dispatch(frame, kDispatchKeyCaptureChangeScript);
509 void SearchBoxExtension::DispatchMarginChange(blink::WebFrame* frame) {
510 Dispatch(frame, kDispatchMarginChangeEventScript);
514 void SearchBoxExtension::DispatchMostVisitedChanged(
515 blink::WebFrame* frame) {
516 Dispatch(frame, kDispatchMostVisitedChangedScript);
520 void SearchBoxExtension::DispatchSubmit(blink::WebFrame* frame) {
521 Dispatch(frame, kDispatchSubmitEventScript);
525 void SearchBoxExtension::DispatchSuggestionChange(blink::WebFrame* frame) {
526 Dispatch(frame, kDispatchSuggestionChangeEventScript);
530 void SearchBoxExtension::DispatchThemeChange(blink::WebFrame* frame) {
531 Dispatch(frame, kDispatchThemeChangeEventScript);
535 void SearchBoxExtension::DispatchToggleVoiceSearch(
536 blink::WebFrame* frame) {
537 Dispatch(frame, kDispatchToggleVoiceSearchScript);
540 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper(
541 const base::StringPiece& code)
542 : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) {
545 v8::Handle<v8::FunctionTemplate>
546 SearchBoxExtensionWrapper::GetNativeFunctionTemplate(
547 v8::Isolate* isolate,
548 v8::Handle<v8::String> name) {
550 v8::String::NewFromUtf8(isolate, "CheckIsUserSignedInToChromeAs")))
551 return v8::FunctionTemplate::New(isolate, CheckIsUserSignedInToChromeAs);
552 if (name->Equals(v8::String::NewFromUtf8(isolate, "DeleteMostVisitedItem")))
553 return v8::FunctionTemplate::New(isolate, DeleteMostVisitedItem);
554 if (name->Equals(v8::String::NewFromUtf8(isolate, "Focus")))
555 return v8::FunctionTemplate::New(isolate, Focus);
556 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetAppLauncherEnabled")))
557 return v8::FunctionTemplate::New(isolate, GetAppLauncherEnabled);
558 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetDispositionFromClick")))
559 return v8::FunctionTemplate::New(isolate, GetDispositionFromClick);
560 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItems")))
561 return v8::FunctionTemplate::New(isolate, GetMostVisitedItems);
562 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItemData")))
563 return v8::FunctionTemplate::New(isolate, GetMostVisitedItemData);
564 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetQuery")))
565 return v8::FunctionTemplate::New(isolate, GetQuery);
566 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetRightToLeft")))
567 return v8::FunctionTemplate::New(isolate, GetRightToLeft);
568 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSearchRequestParams")))
569 return v8::FunctionTemplate::New(isolate, GetSearchRequestParams);
570 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetStartMargin")))
571 return v8::FunctionTemplate::New(isolate, GetStartMargin);
572 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSuggestionToPrefetch")))
573 return v8::FunctionTemplate::New(isolate, GetSuggestionToPrefetch);
574 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetThemeBackgroundInfo")))
575 return v8::FunctionTemplate::New(isolate, GetThemeBackgroundInfo);
576 if (name->Equals(v8::String::NewFromUtf8(isolate, "IsFocused")))
577 return v8::FunctionTemplate::New(isolate, IsFocused);
578 if (name->Equals(v8::String::NewFromUtf8(isolate, "IsInputInProgress")))
579 return v8::FunctionTemplate::New(isolate, IsInputInProgress);
580 if (name->Equals(v8::String::NewFromUtf8(isolate, "IsKeyCaptureEnabled")))
581 return v8::FunctionTemplate::New(isolate, IsKeyCaptureEnabled);
582 if (name->Equals(v8::String::NewFromUtf8(isolate, "LogEvent")))
583 return v8::FunctionTemplate::New(isolate, LogEvent);
585 v8::String::NewFromUtf8(isolate, "LogMostVisitedImpression"))) {
586 return v8::FunctionTemplate::New(isolate, LogMostVisitedImpression);
589 v8::String::NewFromUtf8(isolate, "LogMostVisitedNavigation"))) {
590 return v8::FunctionTemplate::New(isolate, LogMostVisitedNavigation);
592 if (name->Equals(v8::String::NewFromUtf8(isolate, "NavigateContentWindow")))
593 return v8::FunctionTemplate::New(isolate, NavigateContentWindow);
594 if (name->Equals(v8::String::NewFromUtf8(isolate, "Paste")))
595 return v8::FunctionTemplate::New(isolate, Paste);
596 if (name->Equals(v8::String::NewFromUtf8(isolate, "SetVoiceSearchSupported")))
597 return v8::FunctionTemplate::New(isolate, SetVoiceSearchSupported);
599 v8::String::NewFromUtf8(isolate, "StartCapturingKeyStrokes")))
600 return v8::FunctionTemplate::New(isolate, StartCapturingKeyStrokes);
601 if (name->Equals(v8::String::NewFromUtf8(isolate, "StopCapturingKeyStrokes")))
602 return v8::FunctionTemplate::New(isolate, StopCapturingKeyStrokes);
604 v8::String::NewFromUtf8(isolate, "UndoAllMostVisitedDeletions")))
605 return v8::FunctionTemplate::New(isolate, UndoAllMostVisitedDeletions);
606 if (name->Equals(v8::String::NewFromUtf8(isolate, "UndoMostVisitedDeletion")))
607 return v8::FunctionTemplate::New(isolate, UndoMostVisitedDeletion);
609 v8::String::NewFromUtf8(isolate, "GetDisplayInstantResults")))
610 return v8::FunctionTemplate::New(isolate, GetDisplayInstantResults);
611 return v8::Handle<v8::FunctionTemplate>();
615 content::RenderView* SearchBoxExtensionWrapper::GetRenderView() {
616 blink::WebLocalFrame* webframe =
617 blink::WebLocalFrame::frameForCurrentContext();
618 if (!webframe) return NULL;
620 blink::WebView* webview = webframe->view();
621 if (!webview) return NULL; // can happen during closing
623 return content::RenderView::FromWebView(webview);
627 void SearchBoxExtensionWrapper::CheckIsUserSignedInToChromeAs(
628 const v8::FunctionCallbackInfo<v8::Value>& args) {
629 content::RenderView* render_view = GetRenderView();
630 if (!render_view || args.Length() == 0 || args[0]->IsUndefined()) return;
632 DVLOG(1) << render_view << " CheckIsUserSignedInToChromeAs";
634 SearchBox::Get(render_view)->CheckIsUserSignedInToChromeAs(
635 V8ValueToUTF16(args[0]));
639 void SearchBoxExtensionWrapper::DeleteMostVisitedItem(
640 const v8::FunctionCallbackInfo<v8::Value>& args) {
641 content::RenderView* render_view = GetRenderView();
642 if (!render_view || !args.Length()) return;
644 DVLOG(1) << render_view << " DeleteMostVisitedItem";
645 SearchBox::Get(render_view)->DeleteMostVisitedItem(args[0]->IntegerValue());
649 void SearchBoxExtensionWrapper::Focus(
650 const v8::FunctionCallbackInfo<v8::Value>& args) {
651 content::RenderView* render_view = GetRenderView();
652 if (!render_view) return;
654 DVLOG(1) << render_view << " Focus";
655 SearchBox::Get(render_view)->Focus();
659 void SearchBoxExtensionWrapper::GetAppLauncherEnabled(
660 const v8::FunctionCallbackInfo<v8::Value>& args) {
661 content::RenderView* render_view = GetRenderView();
662 if (!render_view) return;
664 args.GetReturnValue().Set(
665 SearchBox::Get(render_view)->app_launcher_enabled());
669 void SearchBoxExtensionWrapper::GetDispositionFromClick(
670 const v8::FunctionCallbackInfo<v8::Value>& args) {
671 content::RenderView* render_view = GetRenderView();
672 if (!render_view || args.Length() != 5) return;
674 bool middle_button = args[0]->BooleanValue();
675 bool alt_key = args[1]->BooleanValue();
676 bool ctrl_key = args[2]->BooleanValue();
677 bool meta_key = args[3]->BooleanValue();
678 bool shift_key = args[4]->BooleanValue();
680 WindowOpenDisposition disposition = ui::DispositionFromClick(middle_button,
685 v8::Isolate* isolate = args.GetIsolate();
686 args.GetReturnValue().Set(v8::Int32::New(isolate, disposition));
690 void SearchBoxExtensionWrapper::GetMostVisitedItems(
691 const v8::FunctionCallbackInfo<v8::Value>& args) {
692 content::RenderView* render_view = GetRenderView();
695 DVLOG(1) << render_view << " GetMostVisitedItems";
697 const SearchBox* search_box = SearchBox::Get(render_view);
699 std::vector<InstantMostVisitedItemIDPair> instant_mv_items;
700 search_box->GetMostVisitedItems(&instant_mv_items);
701 v8::Isolate* isolate = args.GetIsolate();
702 v8::Handle<v8::Array> v8_mv_items =
703 v8::Array::New(isolate, instant_mv_items.size());
704 for (size_t i = 0; i < instant_mv_items.size(); ++i) {
706 GenerateMostVisitedItem(isolate,
707 render_view->GetRoutingID(),
708 instant_mv_items[i].first,
709 instant_mv_items[i].second));
711 args.GetReturnValue().Set(v8_mv_items);
715 void SearchBoxExtensionWrapper::GetMostVisitedItemData(
716 const v8::FunctionCallbackInfo<v8::Value>& args) {
717 content::RenderView* render_view = GetRenderViewWithCheckedOrigin(
718 GURL(chrome::kChromeSearchMostVisitedUrl));
719 if (!render_view) return;
721 // Need an rid argument.
722 if (args.Length() < 1 || !args[0]->IsNumber())
725 DVLOG(1) << render_view << " GetMostVisitedItem";
726 InstantRestrictedID restricted_id = args[0]->IntegerValue();
727 InstantMostVisitedItem mv_item;
728 if (!SearchBox::Get(render_view)->GetMostVisitedItemWithID(
729 restricted_id, &mv_item)) {
732 v8::Isolate* isolate = args.GetIsolate();
733 args.GetReturnValue().Set(GenerateMostVisitedItem(
734 isolate, render_view->GetRoutingID(), restricted_id, mv_item));
738 void SearchBoxExtensionWrapper::GetQuery(
739 const v8::FunctionCallbackInfo<v8::Value>& args) {
740 content::RenderView* render_view = GetRenderView();
741 if (!render_view) return;
742 const base::string16& query = SearchBox::Get(render_view)->query();
743 DVLOG(1) << render_view << " GetQuery: '" << query << "'";
744 v8::Isolate* isolate = args.GetIsolate();
745 args.GetReturnValue().Set(UTF16ToV8String(isolate, query));
749 void SearchBoxExtensionWrapper::GetRightToLeft(
750 const v8::FunctionCallbackInfo<v8::Value>& args) {
751 args.GetReturnValue().Set(base::i18n::IsRTL());
755 void SearchBoxExtensionWrapper::GetSearchRequestParams(
756 const v8::FunctionCallbackInfo<v8::Value>& args) {
757 content::RenderView* render_view = GetRenderView();
758 if (!render_view) return;
760 const EmbeddedSearchRequestParams& params =
761 SearchBox::Get(render_view)->GetEmbeddedSearchRequestParams();
762 v8::Isolate* isolate = args.GetIsolate();
763 v8::Handle<v8::Object> data = v8::Object::New(isolate);
764 if (!params.search_query.empty()) {
765 data->Set(v8::String::NewFromUtf8(isolate, kSearchQueryKey),
766 UTF16ToV8String(isolate, params.search_query));
768 if (!params.original_query.empty()) {
769 data->Set(v8::String::NewFromUtf8(isolate, kOriginalQueryKey),
770 UTF16ToV8String(isolate, params.original_query));
772 if (!params.rlz_parameter_value.empty()) {
773 data->Set(v8::String::NewFromUtf8(isolate, kRLZParameterKey),
774 UTF16ToV8String(isolate, params.rlz_parameter_value));
776 if (!params.input_encoding.empty()) {
777 data->Set(v8::String::NewFromUtf8(isolate, kInputEncodingKey),
778 UTF16ToV8String(isolate, params.input_encoding));
780 if (!params.assisted_query_stats.empty()) {
781 data->Set(v8::String::NewFromUtf8(isolate, kAssistedQueryStatsKey),
782 UTF16ToV8String(isolate, params.assisted_query_stats));
784 args.GetReturnValue().Set(data);
788 void SearchBoxExtensionWrapper::GetStartMargin(
789 const v8::FunctionCallbackInfo<v8::Value>& args) {
790 content::RenderView* render_view = GetRenderView();
791 if (!render_view) return;
792 args.GetReturnValue().Set(static_cast<int32_t>(
793 SearchBox::Get(render_view)->start_margin()));
797 void SearchBoxExtensionWrapper::GetSuggestionToPrefetch(
798 const v8::FunctionCallbackInfo<v8::Value>& args) {
799 content::RenderView* render_view = GetRenderView();
800 if (!render_view) return;
802 const InstantSuggestion& suggestion =
803 SearchBox::Get(render_view)->suggestion();
804 v8::Isolate* isolate = args.GetIsolate();
805 v8::Handle<v8::Object> data = v8::Object::New(isolate);
806 data->Set(v8::String::NewFromUtf8(isolate, "text"),
807 UTF16ToV8String(isolate, suggestion.text));
808 data->Set(v8::String::NewFromUtf8(isolate, "metadata"),
809 UTF8ToV8String(isolate, suggestion.metadata));
810 args.GetReturnValue().Set(data);
814 void SearchBoxExtensionWrapper::GetThemeBackgroundInfo(
815 const v8::FunctionCallbackInfo<v8::Value>& args) {
816 content::RenderView* render_view = GetRenderView();
817 if (!render_view) return;
819 DVLOG(1) << render_view << " GetThemeBackgroundInfo";
820 const ThemeBackgroundInfo& theme_info =
821 SearchBox::Get(render_view)->GetThemeBackgroundInfo();
822 v8::Isolate* isolate = args.GetIsolate();
823 v8::Handle<v8::Object> info = v8::Object::New(isolate);
825 info->Set(v8::String::NewFromUtf8(isolate, "usingDefaultTheme"),
826 v8::Boolean::New(isolate, theme_info.using_default_theme));
828 // The theme background color is in RGBA format "rgba(R,G,B,A)" where R, G and
829 // B are between 0 and 255 inclusive, and A is a double between 0 and 1
831 // This is the CSS "background-color" format.
832 // Value is always valid.
833 // TODO(jfweitz): Remove this field after GWS is modified to use the new
834 // backgroundColorRgba field.
836 v8::String::NewFromUtf8(isolate, "colorRgba"),
839 // Convert the alpha using DoubleToString because StringPrintf will
841 // locale specific formatters (e.g., use , instead of . in German).
843 kCSSBackgroundColorFormat,
844 theme_info.background_color.r,
845 theme_info.background_color.g,
846 theme_info.background_color.b,
847 base::DoubleToString(theme_info.background_color.a / 255.0)
850 // Theme color for background as an array with the RGBA components in order.
851 // Value is always valid.
852 info->Set(v8::String::NewFromUtf8(isolate, "backgroundColorRgba"),
853 internal::RGBAColorToArray(isolate, theme_info.background_color));
855 // Theme color for text as an array with the RGBA components in order.
856 // Value is always valid.
857 info->Set(v8::String::NewFromUtf8(isolate, "textColorRgba"),
858 internal::RGBAColorToArray(isolate, theme_info.text_color));
860 // Theme color for links as an array with the RGBA components in order.
861 // Value is always valid.
862 info->Set(v8::String::NewFromUtf8(isolate, "linkColorRgba"),
863 internal::RGBAColorToArray(isolate, theme_info.link_color));
865 // Theme color for light text as an array with the RGBA components in order.
866 // Value is always valid.
867 info->Set(v8::String::NewFromUtf8(isolate, "textColorLightRgba"),
868 internal::RGBAColorToArray(isolate, theme_info.text_color_light));
870 // Theme color for header as an array with the RGBA components in order.
871 // Value is always valid.
872 info->Set(v8::String::NewFromUtf8(isolate, "headerColorRgba"),
873 internal::RGBAColorToArray(isolate, theme_info.header_color));
875 // Theme color for section border as an array with the RGBA components in
876 // order. Value is always valid.
878 v8::String::NewFromUtf8(isolate, "sectionBorderColorRgba"),
879 internal::RGBAColorToArray(isolate, theme_info.section_border_color));
881 // The theme alternate logo value indicates a white logo when TRUE and a
882 // colorful one when FALSE.
883 info->Set(v8::String::NewFromUtf8(isolate, "alternateLogo"),
884 v8::Boolean::New(isolate, theme_info.logo_alternate));
886 // The theme background image url is of format kCSSBackgroundImageFormat
887 // where both instances of "%s" are replaced with the id that identifies the
889 // This is the CSS "background-image" format.
890 // Value is only valid if there's a custom theme background image.
891 if (crx_file::id_util::IdIsValid(theme_info.theme_id)) {
892 info->Set(v8::String::NewFromUtf8(isolate, "imageUrl"),
893 UTF8ToV8String(isolate,
894 base::StringPrintf(kCSSBackgroundImageFormat,
895 theme_info.theme_id.c_str(),
896 theme_info.theme_id.c_str())));
898 // The theme background image horizontal alignment is one of "left",
899 // "right", "center".
900 // This is the horizontal component of the CSS "background-position" format.
901 // Value is only valid if |imageUrl| is not empty.
902 std::string alignment = kCSSBackgroundPositionCenter;
903 if (theme_info.image_horizontal_alignment ==
904 THEME_BKGRND_IMAGE_ALIGN_LEFT) {
905 alignment = kCSSBackgroundPositionLeft;
906 } else if (theme_info.image_horizontal_alignment ==
907 THEME_BKGRND_IMAGE_ALIGN_RIGHT) {
908 alignment = kCSSBackgroundPositionRight;
910 info->Set(v8::String::NewFromUtf8(isolate, "imageHorizontalAlignment"),
911 UTF8ToV8String(isolate, alignment));
913 // The theme background image vertical alignment is one of "top", "bottom",
915 // This is the vertical component of the CSS "background-position" format.
916 // Value is only valid if |image_url| is not empty.
917 if (theme_info.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) {
918 alignment = kCSSBackgroundPositionTop;
919 } else if (theme_info.image_vertical_alignment ==
920 THEME_BKGRND_IMAGE_ALIGN_BOTTOM) {
921 alignment = kCSSBackgroundPositionBottom;
923 alignment = kCSSBackgroundPositionCenter;
925 info->Set(v8::String::NewFromUtf8(isolate, "imageVerticalAlignment"),
926 UTF8ToV8String(isolate, alignment));
928 // The tiling of the theme background image is one of "no-repeat",
929 // "repeat-x", "repeat-y", "repeat".
930 // This is the CSS "background-repeat" format.
931 // Value is only valid if |image_url| is not empty.
932 std::string tiling = kCSSBackgroundRepeatNo;
933 switch (theme_info.image_tiling) {
934 case THEME_BKGRND_IMAGE_NO_REPEAT:
935 tiling = kCSSBackgroundRepeatNo;
937 case THEME_BKGRND_IMAGE_REPEAT_X:
938 tiling = kCSSBackgroundRepeatX;
940 case THEME_BKGRND_IMAGE_REPEAT_Y:
941 tiling = kCSSBackgroundRepeatY;
943 case THEME_BKGRND_IMAGE_REPEAT:
944 tiling = kCSSBackgroundRepeat;
947 info->Set(v8::String::NewFromUtf8(isolate, "imageTiling"),
948 UTF8ToV8String(isolate, tiling));
950 // The theme background image height is only valid if |imageUrl| is valid.
951 info->Set(v8::String::NewFromUtf8(isolate, "imageHeight"),
952 v8::Int32::New(isolate, theme_info.image_height));
954 // The attribution URL is only valid if the theme has attribution logo.
955 if (theme_info.has_attribution) {
957 v8::String::NewFromUtf8(isolate, "attributionUrl"),
958 UTF8ToV8String(isolate,
959 base::StringPrintf(kThemeAttributionFormat,
960 theme_info.theme_id.c_str(),
961 theme_info.theme_id.c_str())));
965 args.GetReturnValue().Set(info);
969 void SearchBoxExtensionWrapper::IsFocused(
970 const v8::FunctionCallbackInfo<v8::Value>& args) {
971 content::RenderView* render_view = GetRenderView();
972 if (!render_view) return;
974 bool is_focused = SearchBox::Get(render_view)->is_focused();
975 DVLOG(1) << render_view << " IsFocused: " << is_focused;
976 args.GetReturnValue().Set(is_focused);
980 void SearchBoxExtensionWrapper::IsInputInProgress(
981 const v8::FunctionCallbackInfo<v8::Value>& args) {
982 content::RenderView* render_view = GetRenderView();
983 if (!render_view) return;
985 bool is_input_in_progress =
986 SearchBox::Get(render_view)->is_input_in_progress();
987 DVLOG(1) << render_view << " IsInputInProgress: " << is_input_in_progress;
988 args.GetReturnValue().Set(is_input_in_progress);
992 void SearchBoxExtensionWrapper::IsKeyCaptureEnabled(
993 const v8::FunctionCallbackInfo<v8::Value>& args) {
994 content::RenderView* render_view = GetRenderView();
995 if (!render_view) return;
997 args.GetReturnValue().Set(SearchBox::Get(render_view)->
998 is_key_capture_enabled());
1002 void SearchBoxExtensionWrapper::LogEvent(
1003 const v8::FunctionCallbackInfo<v8::Value>& args) {
1004 content::RenderView* render_view = GetRenderViewWithCheckedOrigin(
1005 GURL(chrome::kChromeSearchMostVisitedUrl));
1006 if (!render_view) return;
1008 if (args.Length() < 1 || !args[0]->IsNumber())
1011 DVLOG(1) << render_view << " LogEvent";
1013 if (args[0]->Uint32Value() <= NTP_EVENT_TYPE_LAST) {
1014 NTPLoggingEventType event =
1015 static_cast<NTPLoggingEventType>(args[0]->Uint32Value());
1016 SearchBox::Get(render_view)->LogEvent(event);
1021 void SearchBoxExtensionWrapper::LogMostVisitedImpression(
1022 const v8::FunctionCallbackInfo<v8::Value>& args) {
1023 content::RenderView* render_view = GetRenderViewWithCheckedOrigin(
1024 GURL(chrome::kChromeSearchMostVisitedUrl));
1025 if (!render_view) return;
1027 if (args.Length() < 2 || !args[0]->IsNumber() || args[1]->IsUndefined())
1030 DVLOG(1) << render_view << " LogMostVisitedImpression";
1032 SearchBox::Get(render_view)->LogMostVisitedImpression(
1033 args[0]->IntegerValue(), V8ValueToUTF16(args[1]));
1037 void SearchBoxExtensionWrapper::LogMostVisitedNavigation(
1038 const v8::FunctionCallbackInfo<v8::Value>& args) {
1039 content::RenderView* render_view = GetRenderViewWithCheckedOrigin(
1040 GURL(chrome::kChromeSearchMostVisitedUrl));
1041 if (!render_view) return;
1043 if (args.Length() < 2 || !args[0]->IsNumber() || args[1]->IsUndefined())
1046 DVLOG(1) << render_view << " LogMostVisitedNavigation";
1048 SearchBox::Get(render_view)->LogMostVisitedNavigation(
1049 args[0]->IntegerValue(), V8ValueToUTF16(args[1]));
1053 void SearchBoxExtensionWrapper::NavigateContentWindow(
1054 const v8::FunctionCallbackInfo<v8::Value>& args) {
1055 content::RenderView* render_view = GetRenderView();
1056 if (!render_view || !args.Length()) return;
1058 GURL destination_url;
1059 bool is_most_visited_item_url = false;
1060 // Check if the url is a rid
1061 if (args[0]->IsNumber()) {
1062 InstantMostVisitedItem item;
1063 if (SearchBox::Get(render_view)->GetMostVisitedItemWithID(
1064 args[0]->IntegerValue(), &item)) {
1065 destination_url = item.url;
1066 is_most_visited_item_url = true;
1070 const base::string16& possibly_relative_url = V8ValueToUTF16(args[0]);
1071 GURL current_url = GetCurrentURL(render_view);
1072 destination_url = internal::ResolveURL(current_url, possibly_relative_url);
1075 DVLOG(1) << render_view << " NavigateContentWindow: " << destination_url;
1077 // Navigate the main frame.
1078 if (destination_url.is_valid() &&
1079 !destination_url.SchemeIs(url::kJavaScriptScheme)) {
1080 WindowOpenDisposition disposition = CURRENT_TAB;
1081 if (args[1]->IsNumber()) {
1082 disposition = (WindowOpenDisposition) args[1]->Uint32Value();
1084 SearchBox::Get(render_view)->NavigateToURL(destination_url, disposition,
1085 is_most_visited_item_url);
1090 void SearchBoxExtensionWrapper::Paste(
1091 const v8::FunctionCallbackInfo<v8::Value>& args) {
1092 content::RenderView* render_view = GetRenderView();
1093 if (!render_view) return;
1095 base::string16 text;
1096 if (!args[0]->IsUndefined())
1097 text = V8ValueToUTF16(args[0]);
1099 DVLOG(1) << render_view << " Paste: " << text;
1100 SearchBox::Get(render_view)->Paste(text);
1104 void SearchBoxExtensionWrapper::StartCapturingKeyStrokes(
1105 const v8::FunctionCallbackInfo<v8::Value>& args) {
1106 content::RenderView* render_view = GetRenderView();
1107 if (!render_view) return;
1109 DVLOG(1) << render_view << " StartCapturingKeyStrokes";
1110 SearchBox::Get(render_view)->StartCapturingKeyStrokes();
1114 void SearchBoxExtensionWrapper::StopCapturingKeyStrokes(
1115 const v8::FunctionCallbackInfo<v8::Value>& args) {
1116 content::RenderView* render_view = GetRenderView();
1117 if (!render_view) return;
1119 DVLOG(1) << render_view << " StopCapturingKeyStrokes";
1120 SearchBox::Get(render_view)->StopCapturingKeyStrokes();
1124 void SearchBoxExtensionWrapper::SetVoiceSearchSupported(
1125 const v8::FunctionCallbackInfo<v8::Value>& args) {
1126 content::RenderView* render_view = GetRenderView();
1127 if (!render_view || args.Length() < 1) return;
1129 DVLOG(1) << render_view << " SetVoiceSearchSupported";
1130 SearchBox::Get(render_view)->SetVoiceSearchSupported(args[0]->BooleanValue());
1134 void SearchBoxExtensionWrapper::UndoAllMostVisitedDeletions(
1135 const v8::FunctionCallbackInfo<v8::Value>& args) {
1136 content::RenderView* render_view = GetRenderView();
1137 if (!render_view) return;
1139 DVLOG(1) << render_view << " UndoAllMostVisitedDeletions";
1140 SearchBox::Get(render_view)->UndoAllMostVisitedDeletions();
1144 void SearchBoxExtensionWrapper::UndoMostVisitedDeletion(
1145 const v8::FunctionCallbackInfo<v8::Value>& args) {
1146 content::RenderView* render_view = GetRenderView();
1147 if (!render_view || !args.Length()) return;
1149 DVLOG(1) << render_view << " UndoMostVisitedDeletion";
1150 SearchBox::Get(render_view)->UndoMostVisitedDeletion(args[0]->IntegerValue());
1154 void SearchBoxExtensionWrapper::GetDisplayInstantResults(
1155 const v8::FunctionCallbackInfo<v8::Value>& args) {
1156 content::RenderView* render_view = GetRenderView();
1157 if (!render_view) return;
1159 bool display_instant_results =
1160 SearchBox::Get(render_view)->display_instant_results();
1161 DVLOG(1) << render_view << " GetDisplayInstantResults" <<
1162 display_instant_results;
1163 args.GetReturnValue().Set(display_instant_results);
1166 } // namespace extensions_v8