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. 4 5 #include "chrome/renderer/searchbox/searchbox_extension.h" 6 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/autocomplete_match_type.h" 14 #include "chrome/common/instant_types.h" 15 #include "chrome/common/ntp_logging_events.h" 16 #include "chrome/common/url_constants.h" 17 #include "chrome/renderer/searchbox/searchbox.h" 18 #include "content/public/renderer/render_view.h" 19 #include "extensions/common/extension.h" 20 #include "grit/renderer_resources.h" 21 #include "third_party/WebKit/public/platform/WebURLRequest.h" 22 #include "third_party/WebKit/public/web/WebDocument.h" 23 #include "third_party/WebKit/public/web/WebFrame.h" 24 #include "third_party/WebKit/public/web/WebScriptSource.h" 25 #include "third_party/WebKit/public/web/WebView.h" 26 #include "ui/base/resource/resource_bundle.h" 27 #include "ui/base/window_open_disposition.h" 28 #include "ui/events/keycodes/keyboard_codes.h" 29 #include "url/gurl.h" 30 #include "v8/include/v8.h" 31 32 namespace { 33 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)"; 37 38 const char kCSSBackgroundColorFormat[] = "rgba(%d,%d,%d,%s)"; 39 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"; 45 46 const char kCSSBackgroundRepeatNo[] = "no-repeat"; 47 const char kCSSBackgroundRepeatX[] = "repeat-x"; 48 const char kCSSBackgroundRepeatY[] = "repeat-y"; 49 const char kCSSBackgroundRepeat[] = "repeat"; 50 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)"; 54 55 const char kLTRHtmlTextDirection[] = "ltr"; 56 const char kRTLHtmlTextDirection[] = "rtl"; 57 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 char16*>(*s), s.length()); 62 } 63 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, 70 s.size()); 71 } 72 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()); 78 } 79 80 void Dispatch(blink::WebFrame* frame, const blink::WebString& script) { 81 if (!frame) return; 82 frame->executeScript(blink::WebScriptSource(script)); 83 } 84 85 v8::Handle<v8::String> GenerateThumbnailURL( 86 v8::Isolate* isolate, 87 int render_view_id, 88 InstantRestrictedID most_visited_item_id) { 89 return UTF8ToV8String( 90 isolate, 91 base::StringPrintf( 92 "chrome-search://thumb/%d/%d", render_view_id, most_visited_item_id)); 93 } 94 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, 101 int render_view_id, 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 114 // "ltr". 115 std::string direction; 116 if (base::i18n::StringContainsStrongRTLChars(mv_item.title)) 117 direction = kRTLHtmlTextDirection; 118 else 119 direction = kLTRHtmlTextDirection; 120 121 base::string16 title = mv_item.title; 122 if (title.empty()) 123 title = UTF8ToUTF16(mv_item.url.spec()); 124 125 v8::Handle<v8::Object> obj = v8::Object::New(); 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())); 140 return obj; 141 } 142 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::WebFrame* webframe = blink::WebFrame::frameForCurrentContext(); 149 if (!webframe) 150 return NULL; 151 blink::WebView* webview = webframe->view(); 152 if (!webview) 153 return NULL; // Can happen during closing. 154 content::RenderView* render_view = content::RenderView::FromWebView(webview); 155 if (!render_view) 156 return NULL; 157 158 GURL url(webframe->document().url()); 159 if (url.GetOrigin() != origin.GetOrigin()) 160 return NULL; 161 162 return render_view; 163 } 164 165 // Returns the current URL. 166 GURL GetCurrentURL(content::RenderView* render_view) { 167 blink::WebView* webview = render_view->GetWebView(); 168 return webview ? GURL(webview->mainFrame()->document().url()) : GURL(); 169 } 170 171 } // namespace 172 173 namespace internal { // for testing. 174 175 // Returns an array with the RGBA color components. 176 v8::Handle<v8::Value> RGBAColorToArray(v8::Isolate* isolate, 177 const RGBAColor& color) { 178 v8::Handle<v8::Array> color_array = v8::Array::New(isolate, 4); 179 color_array->Set(0, v8::Int32::New(isolate, color.r)); 180 color_array->Set(1, v8::Int32::New(isolate, color.g)); 181 color_array->Set(2, v8::Int32::New(isolate, color.b)); 182 color_array->Set(3, v8::Int32::New(isolate, color.a)); 183 return color_array; 184 } 185 186 // Resolves a possibly relative URL using the current URL. 187 GURL ResolveURL(const GURL& current_url, 188 const base::string16& possibly_relative_url) { 189 if (current_url.is_valid() && !possibly_relative_url.empty()) 190 return current_url.Resolve(possibly_relative_url); 191 return GURL(possibly_relative_url); 192 } 193 194 } // namespace internal 195 196 namespace extensions_v8 { 197 198 static const char kSearchBoxExtensionName[] = "v8/EmbeddedSearch"; 199 200 // We first send this script down to determine if the page supports instant. 201 static const char kSupportsInstantScript[] = 202 "if (window.chrome &&" 203 " window.chrome.embeddedSearch &&" 204 " window.chrome.embeddedSearch.searchBox &&" 205 " window.chrome.embeddedSearch.searchBox.onsubmit &&" 206 " typeof window.chrome.embeddedSearch.searchBox.onsubmit ==" 207 " 'function') {" 208 " true;" 209 "} else {" 210 " false;" 211 "}"; 212 213 static const char kDispatchChromeIdentityCheckResult[] = 214 "if (window.chrome &&" 215 " window.chrome.embeddedSearch &&" 216 " window.chrome.embeddedSearch.newTabPage &&" 217 " window.chrome.embeddedSearch.newTabPage.onsignedincheckdone &&" 218 " typeof window.chrome.embeddedSearch.newTabPage" 219 " .onsignedincheckdone === 'function') {" 220 " window.chrome.embeddedSearch.newTabPage.onsignedincheckdone(%s, %s);" 221 " true;" 222 "}"; 223 224 225 static const char kDispatchFocusChangedScript[] = 226 "if (window.chrome &&" 227 " window.chrome.embeddedSearch &&" 228 " window.chrome.embeddedSearch.searchBox &&" 229 " window.chrome.embeddedSearch.searchBox.onfocuschange &&" 230 " typeof window.chrome.embeddedSearch.searchBox.onfocuschange ==" 231 " 'function') {" 232 " window.chrome.embeddedSearch.searchBox.onfocuschange();" 233 " true;" 234 "}"; 235 236 static const char kDispatchInputCancelScript[] = 237 "if (window.chrome &&" 238 " window.chrome.embeddedSearch &&" 239 " window.chrome.embeddedSearch.newTabPage &&" 240 " window.chrome.embeddedSearch.newTabPage.oninputcancel &&" 241 " typeof window.chrome.embeddedSearch.newTabPage.oninputcancel ==" 242 " 'function') {" 243 " window.chrome.embeddedSearch.newTabPage.oninputcancel();" 244 " true;" 245 "}"; 246 247 static const char kDispatchInputStartScript[] = 248 "if (window.chrome &&" 249 " window.chrome.embeddedSearch &&" 250 " window.chrome.embeddedSearch.newTabPage &&" 251 " window.chrome.embeddedSearch.newTabPage.oninputstart &&" 252 " typeof window.chrome.embeddedSearch.newTabPage.oninputstart ==" 253 " 'function') {" 254 " window.chrome.embeddedSearch.newTabPage.oninputstart();" 255 " true;" 256 "}"; 257 258 static const char kDispatchKeyCaptureChangeScript[] = 259 "if (window.chrome &&" 260 " window.chrome.embeddedSearch &&" 261 " window.chrome.embeddedSearch.searchBox &&" 262 " window.chrome.embeddedSearch.searchBox.onkeycapturechange &&" 263 " typeof window.chrome.embeddedSearch.searchBox.onkeycapturechange ==" 264 " 'function') {" 265 " window.chrome.embeddedSearch.searchBox.onkeycapturechange();" 266 " true;" 267 "}"; 268 269 static const char kDispatchMarginChangeEventScript[] = 270 "if (window.chrome &&" 271 " window.chrome.embeddedSearch &&" 272 " window.chrome.embeddedSearch.searchBox &&" 273 " window.chrome.embeddedSearch.searchBox.onmarginchange &&" 274 " typeof window.chrome.embeddedSearch.searchBox.onmarginchange ==" 275 " 'function') {" 276 " window.chrome.embeddedSearch.searchBox.onmarginchange();" 277 " true;" 278 "}"; 279 280 static const char kDispatchMostVisitedChangedScript[] = 281 "if (window.chrome &&" 282 " window.chrome.embeddedSearch &&" 283 " window.chrome.embeddedSearch.newTabPage &&" 284 " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange &&" 285 " typeof window.chrome.embeddedSearch.newTabPage.onmostvisitedchange ==" 286 " 'function') {" 287 " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange();" 288 " true;" 289 "}"; 290 291 static const char kDispatchSubmitEventScript[] = 292 "if (window.chrome &&" 293 " window.chrome.embeddedSearch &&" 294 " window.chrome.embeddedSearch.searchBox &&" 295 " window.chrome.embeddedSearch.searchBox.onsubmit &&" 296 " typeof window.chrome.embeddedSearch.searchBox.onsubmit ==" 297 " 'function') {" 298 " window.chrome.embeddedSearch.searchBox.onsubmit();" 299 " true;" 300 "}"; 301 302 static const char kDispatchSuggestionChangeEventScript[] = 303 "if (window.chrome &&" 304 " window.chrome.embeddedSearch &&" 305 " window.chrome.embeddedSearch.searchBox &&" 306 " window.chrome.embeddedSearch.searchBox.onsuggestionchange &&" 307 " typeof window.chrome.embeddedSearch.searchBox.onsuggestionchange ==" 308 " 'function') {" 309 " window.chrome.embeddedSearch.searchBox.onsuggestionchange();" 310 " true;" 311 "}"; 312 313 static const char kDispatchThemeChangeEventScript[] = 314 "if (window.chrome &&" 315 " window.chrome.embeddedSearch &&" 316 " window.chrome.embeddedSearch.newTabPage &&" 317 " window.chrome.embeddedSearch.newTabPage.onthemechange &&" 318 " typeof window.chrome.embeddedSearch.newTabPage.onthemechange ==" 319 " 'function') {" 320 " window.chrome.embeddedSearch.newTabPage.onthemechange();" 321 " true;" 322 "}"; 323 324 static const char kDispatchToggleVoiceSearchScript[] = 325 "if (window.chrome &&" 326 " window.chrome.embeddedSearch &&" 327 " window.chrome.embeddedSearch.searchBox &&" 328 " window.chrome.embeddedSearch.searchBox.ontogglevoicesearch &&" 329 " typeof window.chrome.embeddedSearch.searchBox.ontogglevoicesearch ==" 330 " 'function') {" 331 " window.chrome.embeddedSearch.searchBox.ontogglevoicesearch();" 332 " true;" 333 "}"; 334 335 // ---------------------------------------------------------------------------- 336 337 class SearchBoxExtensionWrapper : public v8::Extension { 338 public: 339 explicit SearchBoxExtensionWrapper(const base::StringPiece& code); 340 341 // Allows v8's javascript code to call the native functions defined 342 // in this class for window.chrome. 343 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( 344 v8::Isolate*, 345 v8::Handle<v8::String> name) OVERRIDE; 346 347 // Helper function to find the RenderView. May return NULL. 348 static content::RenderView* GetRenderView(); 349 350 // Sends a Chrome identity check to the browser. 351 static void CheckIsUserSignedInToChromeAs( 352 const v8::FunctionCallbackInfo<v8::Value>& args); 353 354 // Deletes a Most Visited item. 355 static void DeleteMostVisitedItem( 356 const v8::FunctionCallbackInfo<v8::Value>& args); 357 358 // Focuses the omnibox. 359 static void Focus(const v8::FunctionCallbackInfo<v8::Value>& args); 360 361 // Gets whether or not the app launcher is enabled. 362 static void GetAppLauncherEnabled( 363 const v8::FunctionCallbackInfo<v8::Value>& args); 364 365 // Gets Most Visited Items. 366 static void GetMostVisitedItems( 367 const v8::FunctionCallbackInfo<v8::Value>& args); 368 369 // Gets the raw data for a most visited item including its raw URL. 370 // GetRenderViewWithCheckedOrigin() enforces that only code in the origin 371 // chrome-search://most-visited can call this function. 372 static void GetMostVisitedItemData( 373 const v8::FunctionCallbackInfo<v8::Value>& args); 374 375 // Gets the submitted value of the user's search query. 376 static void GetQuery(const v8::FunctionCallbackInfo<v8::Value>& args); 377 378 // Returns true if the Searchbox itself is oriented right-to-left. 379 static void GetRightToLeft(const v8::FunctionCallbackInfo<v8::Value>& args); 380 381 // Gets the start-edge margin to use with extended Instant. 382 static void GetStartMargin(const v8::FunctionCallbackInfo<v8::Value>& args); 383 384 // Gets the current top suggestion to prefetch search results. 385 static void GetSuggestionToPrefetch( 386 const v8::FunctionCallbackInfo<v8::Value>& args); 387 388 // Gets the background info of the theme currently adopted by browser. 389 // Call only when overlay is showing NTP page. 390 static void GetThemeBackgroundInfo( 391 const v8::FunctionCallbackInfo<v8::Value>& args); 392 393 // Gets whether the omnibox has focus or not. 394 static void IsFocused(const v8::FunctionCallbackInfo<v8::Value>& args); 395 396 // Gets whether user input is in progress. 397 static void IsInputInProgress( 398 const v8::FunctionCallbackInfo<v8::Value>& args); 399 400 // Gets whether the browser is capturing key strokes. 401 static void IsKeyCaptureEnabled( 402 const v8::FunctionCallbackInfo<v8::Value>& args); 403 404 // Logs information from the iframes/titles on the NTP. 405 static void LogEvent(const v8::FunctionCallbackInfo<v8::Value>& args); 406 407 // Logs an impression on one of the Most Visited tile on the NTP. 408 static void LogImpression(const v8::FunctionCallbackInfo<v8::Value>& args); 409 410 // Navigates the window to a URL represented by either a URL string or a 411 // restricted ID. 412 static void NavigateContentWindow( 413 const v8::FunctionCallbackInfo<v8::Value>& args); 414 415 // Pastes provided value or clipboard's content into the omnibox. 416 static void Paste(const v8::FunctionCallbackInfo<v8::Value>& args); 417 418 // Indicates whether the page supports voice search. 419 static void SetVoiceSearchSupported( 420 const v8::FunctionCallbackInfo<v8::Value>& args); 421 422 // Start capturing user key strokes. 423 static void StartCapturingKeyStrokes( 424 const v8::FunctionCallbackInfo<v8::Value>& args); 425 426 // Stop capturing user key strokes. 427 static void StopCapturingKeyStrokes( 428 const v8::FunctionCallbackInfo<v8::Value>& args); 429 430 // Undoes the deletion of all Most Visited itens. 431 static void UndoAllMostVisitedDeletions( 432 const v8::FunctionCallbackInfo<v8::Value>& args); 433 434 // Undoes the deletion of a Most Visited item. 435 static void UndoMostVisitedDeletion( 436 const v8::FunctionCallbackInfo<v8::Value>& args); 437 438 // Indicates whether the page supports Instant. 439 static void GetDisplayInstantResults( 440 const v8::FunctionCallbackInfo<v8::Value>& args); 441 442 private: 443 DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); 444 }; 445 446 // static 447 v8::Extension* SearchBoxExtension::Get() { 448 return new SearchBoxExtensionWrapper(ResourceBundle::GetSharedInstance(). 449 GetRawDataResource(IDR_SEARCHBOX_API)); 450 } 451 452 // static 453 bool SearchBoxExtension::PageSupportsInstant(blink::WebFrame* frame) { 454 if (!frame) return false; 455 v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); 456 v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue( 457 blink::WebScriptSource(kSupportsInstantScript)); 458 return !v.IsEmpty() && v->BooleanValue(); 459 } 460 461 // static 462 void SearchBoxExtension::DispatchChromeIdentityCheckResult( 463 blink::WebFrame* frame, 464 const base::string16& identity, 465 bool identity_match) { 466 std::string escaped_identity = base::GetQuotedJSONString(identity); 467 blink::WebString script(UTF8ToUTF16(base::StringPrintf( 468 kDispatchChromeIdentityCheckResult, 469 escaped_identity.c_str(), 470 identity_match ? "true" : "false"))); 471 Dispatch(frame, script); 472 } 473 474 // static 475 void SearchBoxExtension::DispatchFocusChange(blink::WebFrame* frame) { 476 Dispatch(frame, kDispatchFocusChangedScript); 477 } 478 479 // static 480 void SearchBoxExtension::DispatchInputCancel(blink::WebFrame* frame) { 481 Dispatch(frame, kDispatchInputCancelScript); 482 } 483 484 // static 485 void SearchBoxExtension::DispatchInputStart(blink::WebFrame* frame) { 486 Dispatch(frame, kDispatchInputStartScript); 487 } 488 489 // static 490 void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebFrame* frame) { 491 Dispatch(frame, kDispatchKeyCaptureChangeScript); 492 } 493 494 // static 495 void SearchBoxExtension::DispatchMarginChange(blink::WebFrame* frame) { 496 Dispatch(frame, kDispatchMarginChangeEventScript); 497 } 498 499 // static 500 void SearchBoxExtension::DispatchMostVisitedChanged( 501 blink::WebFrame* frame) { 502 Dispatch(frame, kDispatchMostVisitedChangedScript); 503 } 504 505 // static 506 void SearchBoxExtension::DispatchSubmit(blink::WebFrame* frame) { 507 Dispatch(frame, kDispatchSubmitEventScript); 508 } 509 510 // static 511 void SearchBoxExtension::DispatchSuggestionChange(blink::WebFrame* frame) { 512 Dispatch(frame, kDispatchSuggestionChangeEventScript); 513 } 514 515 // static 516 void SearchBoxExtension::DispatchThemeChange(blink::WebFrame* frame) { 517 Dispatch(frame, kDispatchThemeChangeEventScript); 518 } 519 520 // static 521 void SearchBoxExtension::DispatchToggleVoiceSearch( 522 blink::WebFrame* frame) { 523 Dispatch(frame, kDispatchToggleVoiceSearchScript); 524 } 525 526 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper( 527 const base::StringPiece& code) 528 : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) { 529 } 530 531 v8::Handle<v8::FunctionTemplate> 532 SearchBoxExtensionWrapper::GetNativeFunctionTemplate( 533 v8::Isolate* isolate, 534 v8::Handle<v8::String> name) { 535 if (name->Equals( 536 v8::String::NewFromUtf8(isolate, "CheckIsUserSignedInToChromeAs"))) 537 return v8::FunctionTemplate::New(isolate, CheckIsUserSignedInToChromeAs); 538 if (name->Equals(v8::String::NewFromUtf8(isolate, "DeleteMostVisitedItem"))) 539 return v8::FunctionTemplate::New(isolate, DeleteMostVisitedItem); 540 if (name->Equals(v8::String::NewFromUtf8(isolate, "Focus"))) 541 return v8::FunctionTemplate::New(isolate, Focus); 542 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetAppLauncherEnabled"))) 543 return v8::FunctionTemplate::New(isolate, GetAppLauncherEnabled); 544 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItems"))) 545 return v8::FunctionTemplate::New(isolate, GetMostVisitedItems); 546 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItemData"))) 547 return v8::FunctionTemplate::New(isolate, GetMostVisitedItemData); 548 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetQuery"))) 549 return v8::FunctionTemplate::New(isolate, GetQuery); 550 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetRightToLeft"))) 551 return v8::FunctionTemplate::New(isolate, GetRightToLeft); 552 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetStartMargin"))) 553 return v8::FunctionTemplate::New(isolate, GetStartMargin); 554 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSuggestionToPrefetch"))) 555 return v8::FunctionTemplate::New(isolate, GetSuggestionToPrefetch); 556 if (name->Equals(v8::String::NewFromUtf8(isolate, "GetThemeBackgroundInfo"))) 557 return v8::FunctionTemplate::New(isolate, GetThemeBackgroundInfo); 558 if (name->Equals(v8::String::NewFromUtf8(isolate, "IsFocused"))) 559 return v8::FunctionTemplate::New(isolate, IsFocused); 560 if (name->Equals(v8::String::NewFromUtf8(isolate, "IsInputInProgress"))) 561 return v8::FunctionTemplate::New(isolate, IsInputInProgress); 562 if (name->Equals(v8::String::NewFromUtf8(isolate, "IsKeyCaptureEnabled"))) 563 return v8::FunctionTemplate::New(isolate, IsKeyCaptureEnabled); 564 if (name->Equals(v8::String::NewFromUtf8(isolate, "LogEvent"))) 565 return v8::FunctionTemplate::New(isolate, LogEvent); 566 if (name->Equals(v8::String::NewFromUtf8(isolate, "LogImpression"))) 567 return v8::FunctionTemplate::New(isolate, LogImpression); 568 if (name->Equals(v8::String::NewFromUtf8(isolate, "NavigateContentWindow"))) 569 return v8::FunctionTemplate::New(isolate, NavigateContentWindow); 570 if (name->Equals(v8::String::NewFromUtf8(isolate, "Paste"))) 571 return v8::FunctionTemplate::New(isolate, Paste); 572 if (name->Equals(v8::String::NewFromUtf8(isolate, "SetVoiceSearchSupported"))) 573 return v8::FunctionTemplate::New(isolate, SetVoiceSearchSupported); 574 if (name->Equals( 575 v8::String::NewFromUtf8(isolate, "StartCapturingKeyStrokes"))) 576 return v8::FunctionTemplate::New(isolate, StartCapturingKeyStrokes); 577 if (name->Equals(v8::String::NewFromUtf8(isolate, "StopCapturingKeyStrokes"))) 578 return v8::FunctionTemplate::New(isolate, StopCapturingKeyStrokes); 579 if (name->Equals( 580 v8::String::NewFromUtf8(isolate, "UndoAllMostVisitedDeletions"))) 581 return v8::FunctionTemplate::New(isolate, UndoAllMostVisitedDeletions); 582 if (name->Equals(v8::String::NewFromUtf8(isolate, "UndoMostVisitedDeletion"))) 583 return v8::FunctionTemplate::New(isolate, UndoMostVisitedDeletion); 584 if (name->Equals( 585 v8::String::NewFromUtf8(isolate, "GetDisplayInstantResults"))) 586 return v8::FunctionTemplate::New(isolate, GetDisplayInstantResults); 587 return v8::Handle<v8::FunctionTemplate>(); 588 } 589 590 // static 591 content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { 592 blink::WebFrame* webframe = blink::WebFrame::frameForCurrentContext(); 593 if (!webframe) return NULL; 594 595 blink::WebView* webview = webframe->view(); 596 if (!webview) return NULL; // can happen during closing 597 598 return content::RenderView::FromWebView(webview); 599 } 600 601 // static 602 void SearchBoxExtensionWrapper::CheckIsUserSignedInToChromeAs( 603 const v8::FunctionCallbackInfo<v8::Value>& args) { 604 content::RenderView* render_view = GetRenderView(); 605 if (!render_view || args.Length() == 0 || args[0]->IsUndefined()) return; 606 607 DVLOG(1) << render_view << " CheckIsUserSignedInToChromeAs"; 608 609 SearchBox::Get(render_view)->CheckIsUserSignedInToChromeAs( 610 V8ValueToUTF16(args[0])); 611 } 612 613 // static 614 void SearchBoxExtensionWrapper::DeleteMostVisitedItem( 615 const v8::FunctionCallbackInfo<v8::Value>& args) { 616 content::RenderView* render_view = GetRenderView(); 617 if (!render_view || !args.Length()) return; 618 619 DVLOG(1) << render_view << " DeleteMostVisitedItem"; 620 SearchBox::Get(render_view)->DeleteMostVisitedItem(args[0]->IntegerValue()); 621 } 622 623 // static 624 void SearchBoxExtensionWrapper::Focus( 625 const v8::FunctionCallbackInfo<v8::Value>& args) { 626 content::RenderView* render_view = GetRenderView(); 627 if (!render_view) return; 628 629 DVLOG(1) << render_view << " Focus"; 630 SearchBox::Get(render_view)->Focus(); 631 } 632 633 // static 634 void SearchBoxExtensionWrapper::GetAppLauncherEnabled( 635 const v8::FunctionCallbackInfo<v8::Value>& args) { 636 content::RenderView* render_view = GetRenderView(); 637 if (!render_view) return; 638 639 args.GetReturnValue().Set( 640 SearchBox::Get(render_view)->app_launcher_enabled()); 641 } 642 643 // static 644 void SearchBoxExtensionWrapper::GetMostVisitedItems( 645 const v8::FunctionCallbackInfo<v8::Value>& args) { 646 content::RenderView* render_view = GetRenderView(); 647 if (!render_view) 648 return; 649 DVLOG(1) << render_view << " GetMostVisitedItems"; 650 651 const SearchBox* search_box = SearchBox::Get(render_view); 652 653 std::vector<InstantMostVisitedItemIDPair> instant_mv_items; 654 search_box->GetMostVisitedItems(&instant_mv_items); 655 v8::Isolate* isolate = args.GetIsolate(); 656 v8::Handle<v8::Array> v8_mv_items = 657 v8::Array::New(isolate, instant_mv_items.size()); 658 for (size_t i = 0; i < instant_mv_items.size(); ++i) { 659 v8_mv_items->Set(i, 660 GenerateMostVisitedItem(isolate, 661 render_view->GetRoutingID(), 662 instant_mv_items[i].first, 663 instant_mv_items[i].second)); 664 } 665 args.GetReturnValue().Set(v8_mv_items); 666 } 667 668 // static 669 void SearchBoxExtensionWrapper::GetMostVisitedItemData( 670 const v8::FunctionCallbackInfo<v8::Value>& args) { 671 content::RenderView* render_view = GetRenderViewWithCheckedOrigin( 672 GURL(chrome::kChromeSearchMostVisitedUrl)); 673 if (!render_view) return; 674 675 // Need an rid argument. 676 if (args.Length() < 1 || !args[0]->IsNumber()) 677 return; 678 679 DVLOG(1) << render_view << " GetMostVisitedItem"; 680 InstantRestrictedID restricted_id = args[0]->IntegerValue(); 681 InstantMostVisitedItem mv_item; 682 if (!SearchBox::Get(render_view)->GetMostVisitedItemWithID( 683 restricted_id, &mv_item)) { 684 return; 685 } 686 v8::Isolate* isolate = args.GetIsolate(); 687 args.GetReturnValue().Set(GenerateMostVisitedItem( 688 isolate, render_view->GetRoutingID(), restricted_id, mv_item)); 689 } 690 691 // static 692 void SearchBoxExtensionWrapper::GetQuery( 693 const v8::FunctionCallbackInfo<v8::Value>& args) { 694 content::RenderView* render_view = GetRenderView(); 695 if (!render_view) return; 696 const base::string16& query = SearchBox::Get(render_view)->query(); 697 DVLOG(1) << render_view << " GetQuery: '" << query << "'"; 698 v8::Isolate* isolate = args.GetIsolate(); 699 args.GetReturnValue().Set(UTF16ToV8String(isolate, query)); 700 } 701 702 // static 703 void SearchBoxExtensionWrapper::GetRightToLeft( 704 const v8::FunctionCallbackInfo<v8::Value>& args) { 705 args.GetReturnValue().Set(base::i18n::IsRTL()); 706 } 707 708 // static 709 void SearchBoxExtensionWrapper::GetStartMargin( 710 const v8::FunctionCallbackInfo<v8::Value>& args) { 711 content::RenderView* render_view = GetRenderView(); 712 if (!render_view) return; 713 args.GetReturnValue().Set(static_cast<int32_t>( 714 SearchBox::Get(render_view)->start_margin())); 715 } 716 717 // static 718 void SearchBoxExtensionWrapper::GetSuggestionToPrefetch( 719 const v8::FunctionCallbackInfo<v8::Value>& args) { 720 content::RenderView* render_view = GetRenderView(); 721 if (!render_view) return; 722 723 const InstantSuggestion& suggestion = 724 SearchBox::Get(render_view)->suggestion(); 725 v8::Isolate* isolate = args.GetIsolate(); 726 v8::Handle<v8::Object> data = v8::Object::New(); 727 data->Set(v8::String::NewFromUtf8(isolate, "text"), 728 UTF16ToV8String(isolate, suggestion.text)); 729 data->Set(v8::String::NewFromUtf8(isolate, "metadata"), 730 UTF8ToV8String(isolate, suggestion.metadata)); 731 args.GetReturnValue().Set(data); 732 } 733 734 // static 735 void SearchBoxExtensionWrapper::GetThemeBackgroundInfo( 736 const v8::FunctionCallbackInfo<v8::Value>& args) { 737 content::RenderView* render_view = GetRenderView(); 738 if (!render_view) return; 739 740 DVLOG(1) << render_view << " GetThemeBackgroundInfo"; 741 const ThemeBackgroundInfo& theme_info = 742 SearchBox::Get(render_view)->GetThemeBackgroundInfo(); 743 v8::Isolate* isolate = args.GetIsolate(); 744 v8::Handle<v8::Object> info = v8::Object::New(); 745 746 info->Set(v8::String::NewFromUtf8(isolate, "usingDefaultTheme"), 747 v8::Boolean::New(isolate, theme_info.using_default_theme)); 748 749 // The theme background color is in RGBA format "rgba(R,G,B,A)" where R, G and 750 // B are between 0 and 255 inclusive, and A is a double between 0 and 1 751 // inclusive. 752 // This is the CSS "background-color" format. 753 // Value is always valid. 754 // TODO(jfweitz): Remove this field after GWS is modified to use the new 755 // backgroundColorRgba field. 756 info->Set( 757 v8::String::NewFromUtf8(isolate, "colorRgba"), 758 UTF8ToV8String( 759 isolate, 760 // Convert the alpha using DoubleToString because StringPrintf will 761 // use 762 // locale specific formatters (e.g., use , instead of . in German). 763 base::StringPrintf( 764 kCSSBackgroundColorFormat, 765 theme_info.background_color.r, 766 theme_info.background_color.g, 767 theme_info.background_color.b, 768 base::DoubleToString(theme_info.background_color.a / 255.0) 769 .c_str()))); 770 771 // Theme color for background as an array with the RGBA components in order. 772 // Value is always valid. 773 info->Set(v8::String::NewFromUtf8(isolate, "backgroundColorRgba"), 774 internal::RGBAColorToArray(isolate, theme_info.background_color)); 775 776 // Theme color for text as an array with the RGBA components in order. 777 // Value is always valid. 778 info->Set(v8::String::NewFromUtf8(isolate, "textColorRgba"), 779 internal::RGBAColorToArray(isolate, theme_info.text_color)); 780 781 // Theme color for links as an array with the RGBA components in order. 782 // Value is always valid. 783 info->Set(v8::String::NewFromUtf8(isolate, "linkColorRgba"), 784 internal::RGBAColorToArray(isolate, theme_info.link_color)); 785 786 // Theme color for light text as an array with the RGBA components in order. 787 // Value is always valid. 788 info->Set(v8::String::NewFromUtf8(isolate, "textColorLightRgba"), 789 internal::RGBAColorToArray(isolate, theme_info.text_color_light)); 790 791 // Theme color for header as an array with the RGBA components in order. 792 // Value is always valid. 793 info->Set(v8::String::NewFromUtf8(isolate, "headerColorRgba"), 794 internal::RGBAColorToArray(isolate, theme_info.header_color)); 795 796 // Theme color for section border as an array with the RGBA components in 797 // order. Value is always valid. 798 info->Set( 799 v8::String::NewFromUtf8(isolate, "sectionBorderColorRgba"), 800 internal::RGBAColorToArray(isolate, theme_info.section_border_color)); 801 802 // The theme alternate logo value indicates a white logo when TRUE and a 803 // colorful one when FALSE. 804 info->Set(v8::String::NewFromUtf8(isolate, "alternateLogo"), 805 v8::Boolean::New(isolate, theme_info.logo_alternate)); 806 807 // The theme background image url is of format kCSSBackgroundImageFormat 808 // where both instances of "%s" are replaced with the id that identifies the 809 // theme. 810 // This is the CSS "background-image" format. 811 // Value is only valid if there's a custom theme background image. 812 if (extensions::Extension::IdIsValid(theme_info.theme_id)) { 813 info->Set(v8::String::NewFromUtf8(isolate, "imageUrl"), 814 UTF8ToV8String(isolate, 815 base::StringPrintf(kCSSBackgroundImageFormat, 816 theme_info.theme_id.c_str(), 817 theme_info.theme_id.c_str()))); 818 819 // The theme background image horizontal alignment is one of "left", 820 // "right", "center". 821 // This is the horizontal component of the CSS "background-position" format. 822 // Value is only valid if |imageUrl| is not empty. 823 std::string alignment = kCSSBackgroundPositionCenter; 824 if (theme_info.image_horizontal_alignment == 825 THEME_BKGRND_IMAGE_ALIGN_LEFT) { 826 alignment = kCSSBackgroundPositionLeft; 827 } else if (theme_info.image_horizontal_alignment == 828 THEME_BKGRND_IMAGE_ALIGN_RIGHT) { 829 alignment = kCSSBackgroundPositionRight; 830 } 831 info->Set(v8::String::NewFromUtf8(isolate, "imageHorizontalAlignment"), 832 UTF8ToV8String(isolate, alignment)); 833 834 // The theme background image vertical alignment is one of "top", "bottom", 835 // "center". 836 // This is the vertical component of the CSS "background-position" format. 837 // Value is only valid if |image_url| is not empty. 838 if (theme_info.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) { 839 alignment = kCSSBackgroundPositionTop; 840 } else if (theme_info.image_vertical_alignment == 841 THEME_BKGRND_IMAGE_ALIGN_BOTTOM) { 842 alignment = kCSSBackgroundPositionBottom; 843 } else { 844 alignment = kCSSBackgroundPositionCenter; 845 } 846 info->Set(v8::String::NewFromUtf8(isolate, "imageVerticalAlignment"), 847 UTF8ToV8String(isolate, alignment)); 848 849 // The tiling of the theme background image is one of "no-repeat", 850 // "repeat-x", "repeat-y", "repeat". 851 // This is the CSS "background-repeat" format. 852 // Value is only valid if |image_url| is not empty. 853 std::string tiling = kCSSBackgroundRepeatNo; 854 switch (theme_info.image_tiling) { 855 case THEME_BKGRND_IMAGE_NO_REPEAT: 856 tiling = kCSSBackgroundRepeatNo; 857 break; 858 case THEME_BKGRND_IMAGE_REPEAT_X: 859 tiling = kCSSBackgroundRepeatX; 860 break; 861 case THEME_BKGRND_IMAGE_REPEAT_Y: 862 tiling = kCSSBackgroundRepeatY; 863 break; 864 case THEME_BKGRND_IMAGE_REPEAT: 865 tiling = kCSSBackgroundRepeat; 866 break; 867 } 868 info->Set(v8::String::NewFromUtf8(isolate, "imageTiling"), 869 UTF8ToV8String(isolate, tiling)); 870 871 // The theme background image height is only valid if |imageUrl| is valid. 872 info->Set(v8::String::NewFromUtf8(isolate, "imageHeight"), 873 v8::Int32::New(isolate, theme_info.image_height)); 874 875 // The attribution URL is only valid if the theme has attribution logo. 876 if (theme_info.has_attribution) { 877 info->Set( 878 v8::String::NewFromUtf8(isolate, "attributionUrl"), 879 UTF8ToV8String(isolate, 880 base::StringPrintf(kThemeAttributionFormat, 881 theme_info.theme_id.c_str(), 882 theme_info.theme_id.c_str()))); 883 } 884 } 885 886 args.GetReturnValue().Set(info); 887 } 888 889 // static 890 void SearchBoxExtensionWrapper::IsFocused( 891 const v8::FunctionCallbackInfo<v8::Value>& args) { 892 content::RenderView* render_view = GetRenderView(); 893 if (!render_view) return; 894 895 bool is_focused = SearchBox::Get(render_view)->is_focused(); 896 DVLOG(1) << render_view << " IsFocused: " << is_focused; 897 args.GetReturnValue().Set(is_focused); 898 } 899 900 // static 901 void SearchBoxExtensionWrapper::IsInputInProgress( 902 const v8::FunctionCallbackInfo<v8::Value>& args) { 903 content::RenderView* render_view = GetRenderView(); 904 if (!render_view) return; 905 906 bool is_input_in_progress = 907 SearchBox::Get(render_view)->is_input_in_progress(); 908 DVLOG(1) << render_view << " IsInputInProgress: " << is_input_in_progress; 909 args.GetReturnValue().Set(is_input_in_progress); 910 } 911 912 // static 913 void SearchBoxExtensionWrapper::IsKeyCaptureEnabled( 914 const v8::FunctionCallbackInfo<v8::Value>& args) { 915 content::RenderView* render_view = GetRenderView(); 916 if (!render_view) return; 917 918 args.GetReturnValue().Set(SearchBox::Get(render_view)-> 919 is_key_capture_enabled()); 920 } 921 922 // static 923 void SearchBoxExtensionWrapper::LogEvent( 924 const v8::FunctionCallbackInfo<v8::Value>& args) { 925 content::RenderView* render_view = GetRenderViewWithCheckedOrigin( 926 GURL(chrome::kChromeSearchMostVisitedUrl)); 927 if (!render_view) return; 928 929 if (args.Length() < 1 || !args[0]->IsNumber()) 930 return; 931 932 DVLOG(1) << render_view << " LogEvent"; 933 934 if (args[0]->Uint32Value() < NTP_NUM_EVENT_TYPES) { 935 NTPLoggingEventType event = 936 static_cast<NTPLoggingEventType>(args[0]->Uint32Value()); 937 SearchBox::Get(render_view)->LogEvent(event); 938 } 939 } 940 941 // static 942 void SearchBoxExtensionWrapper::LogImpression( 943 const v8::FunctionCallbackInfo<v8::Value>& args) { 944 content::RenderView* render_view = GetRenderViewWithCheckedOrigin( 945 GURL(chrome::kChromeSearchMostVisitedUrl)); 946 if (!render_view) return; 947 948 if (args.Length() < 2 || !args[0]->IsNumber() || args[1]->IsUndefined()) 949 return; 950 951 DVLOG(1) << render_view << " LogImpression"; 952 953 SearchBox::Get(render_view)->LogImpression(args[0]->IntegerValue(), 954 V8ValueToUTF16(args[1])); 955 } 956 957 // static 958 void SearchBoxExtensionWrapper::NavigateContentWindow( 959 const v8::FunctionCallbackInfo<v8::Value>& args) { 960 content::RenderView* render_view = GetRenderView(); 961 if (!render_view || !args.Length()) return; 962 963 GURL destination_url; 964 bool is_most_visited_item_url = false; 965 // Check if the url is a rid 966 if (args[0]->IsNumber()) { 967 InstantMostVisitedItem item; 968 if (SearchBox::Get(render_view)->GetMostVisitedItemWithID( 969 args[0]->IntegerValue(), &item)) { 970 destination_url = item.url; 971 is_most_visited_item_url = true; 972 } 973 } else { 974 // Resolve the URL 975 const base::string16& possibly_relative_url = V8ValueToUTF16(args[0]); 976 GURL current_url = GetCurrentURL(render_view); 977 destination_url = internal::ResolveURL(current_url, possibly_relative_url); 978 } 979 980 DVLOG(1) << render_view << " NavigateContentWindow: " << destination_url; 981 982 // Navigate the main frame. 983 if (destination_url.is_valid()) { 984 WindowOpenDisposition disposition = CURRENT_TAB; 985 if (args[1]->Uint32Value() == 2) 986 disposition = NEW_BACKGROUND_TAB; 987 SearchBox::Get(render_view)->NavigateToURL(destination_url, disposition, 988 is_most_visited_item_url); 989 } 990 } 991 992 // static 993 void SearchBoxExtensionWrapper::Paste( 994 const v8::FunctionCallbackInfo<v8::Value>& args) { 995 content::RenderView* render_view = GetRenderView(); 996 if (!render_view) return; 997 998 base::string16 text; 999 if (!args[0]->IsUndefined()) 1000 text = V8ValueToUTF16(args[0]); 1001 1002 DVLOG(1) << render_view << " Paste: " << text; 1003 SearchBox::Get(render_view)->Paste(text); 1004 } 1005 1006 // static 1007 void SearchBoxExtensionWrapper::StartCapturingKeyStrokes( 1008 const v8::FunctionCallbackInfo<v8::Value>& args) { 1009 content::RenderView* render_view = GetRenderView(); 1010 if (!render_view) return; 1011 1012 DVLOG(1) << render_view << " StartCapturingKeyStrokes"; 1013 SearchBox::Get(render_view)->StartCapturingKeyStrokes(); 1014 } 1015 1016 // static 1017 void SearchBoxExtensionWrapper::StopCapturingKeyStrokes( 1018 const v8::FunctionCallbackInfo<v8::Value>& args) { 1019 content::RenderView* render_view = GetRenderView(); 1020 if (!render_view) return; 1021 1022 DVLOG(1) << render_view << " StopCapturingKeyStrokes"; 1023 SearchBox::Get(render_view)->StopCapturingKeyStrokes(); 1024 } 1025 1026 // static 1027 void SearchBoxExtensionWrapper::SetVoiceSearchSupported( 1028 const v8::FunctionCallbackInfo<v8::Value>& args) { 1029 content::RenderView* render_view = GetRenderView(); 1030 if (!render_view || args.Length() < 1) return; 1031 1032 DVLOG(1) << render_view << " SetVoiceSearchSupported"; 1033 SearchBox::Get(render_view)->SetVoiceSearchSupported(args[0]->BooleanValue()); 1034 } 1035 1036 // static 1037 void SearchBoxExtensionWrapper::UndoAllMostVisitedDeletions( 1038 const v8::FunctionCallbackInfo<v8::Value>& args) { 1039 content::RenderView* render_view = GetRenderView(); 1040 if (!render_view) return; 1041 1042 DVLOG(1) << render_view << " UndoAllMostVisitedDeletions"; 1043 SearchBox::Get(render_view)->UndoAllMostVisitedDeletions(); 1044 } 1045 1046 // static 1047 void SearchBoxExtensionWrapper::UndoMostVisitedDeletion( 1048 const v8::FunctionCallbackInfo<v8::Value>& args) { 1049 content::RenderView* render_view = GetRenderView(); 1050 if (!render_view || !args.Length()) return; 1051 1052 DVLOG(1) << render_view << " UndoMostVisitedDeletion"; 1053 SearchBox::Get(render_view)->UndoMostVisitedDeletion(args[0]->IntegerValue()); 1054 } 1055 1056 // static 1057 void SearchBoxExtensionWrapper::GetDisplayInstantResults( 1058 const v8::FunctionCallbackInfo<v8::Value>& args) { 1059 content::RenderView* render_view = GetRenderView(); 1060 if (!render_view) return; 1061 1062 bool display_instant_results = 1063 SearchBox::Get(render_view)->display_instant_results(); 1064 DVLOG(1) << render_view << " GetDisplayInstantResults" << 1065 display_instant_results; 1066 args.GetReturnValue().Set(display_instant_results); 1067 } 1068 1069 } // namespace extensions_v8 1070