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/strings/string_number_conversions.h" 9 #include "base/strings/string_util.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/common/autocomplete_match_type.h" 13 #include "chrome/common/extensions/extension.h" 14 #include "chrome/common/instant_types.h" 15 #include "chrome/common/url_constants.h" 16 #include "chrome/renderer/searchbox/searchbox.h" 17 #include "content/public/renderer/render_view.h" 18 #include "grit/renderer_resources.h" 19 #include "third_party/WebKit/public/platform/WebURLRequest.h" 20 #include "third_party/WebKit/public/web/WebDocument.h" 21 #include "third_party/WebKit/public/web/WebFrame.h" 22 #include "third_party/WebKit/public/web/WebScriptSource.h" 23 #include "third_party/WebKit/public/web/WebView.h" 24 #include "ui/base/keycodes/keyboard_codes.h" 25 #include "ui/base/resource/resource_bundle.h" 26 #include "ui/base/window_open_disposition.h" 27 #include "url/gurl.h" 28 #include "v8/include/v8.h" 29 30 namespace { 31 32 const char kCSSBackgroundImageFormat[] = "-webkit-image-set(" 33 "url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND?%s) 1x, " 34 "url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND@2x?%s) 2x)"; 35 36 const char kCSSBackgroundColorFormat[] = "rgba(%d,%d,%d,%s)"; 37 38 const char kCSSBackgroundPositionCenter[] = "center"; 39 const char kCSSBackgroundPositionLeft[] = "left"; 40 const char kCSSBackgroundPositionTop[] = "top"; 41 const char kCSSBackgroundPositionRight[] = "right"; 42 const char kCSSBackgroundPositionBottom[] = "bottom"; 43 44 const char kCSSBackgroundRepeatNo[] = "no-repeat"; 45 const char kCSSBackgroundRepeatX[] = "repeat-x"; 46 const char kCSSBackgroundRepeatY[] = "repeat-y"; 47 const char kCSSBackgroundRepeat[] = "repeat"; 48 49 const char kThemeAttributionFormat[] = "-webkit-image-set(" 50 "url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION?%s) 1x, " 51 "url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION@2x?%s) 2x)"; 52 53 const char kLTRHtmlTextDirection[] = "ltr"; 54 const char kRTLHtmlTextDirection[] = "rtl"; 55 56 // Converts a V8 value to a string16. 57 string16 V8ValueToUTF16(v8::Handle<v8::Value> v) { 58 v8::String::Value s(v); 59 return string16(reinterpret_cast<const char16*>(*s), s.length()); 60 } 61 62 // Converts string16 to V8 String. 63 v8::Handle<v8::String> UTF16ToV8String(const string16& s) { 64 return v8::String::New(reinterpret_cast<const uint16_t*>(s.data()), s.size()); 65 } 66 67 // Converts std::string to V8 String. 68 v8::Handle<v8::String> UTF8ToV8String(const std::string& s) { 69 return v8::String::New(s.data(), s.size()); 70 } 71 72 void Dispatch(WebKit::WebFrame* frame, const WebKit::WebString& script) { 73 if (!frame) return; 74 frame->executeScript(WebKit::WebScriptSource(script)); 75 } 76 77 v8::Handle<v8::String> GenerateThumbnailURL( 78 int render_view_id, 79 InstantRestrictedID most_visited_item_id) { 80 return UTF8ToV8String(base::StringPrintf("chrome-search://thumb/%d/%d", 81 render_view_id, 82 most_visited_item_id)); 83 } 84 85 // Populates a Javascript MostVisitedItem object from |mv_item|. 86 // NOTE: Includes "url", "title" and "domain" which are private data, so should 87 // not be returned to the Instant page. These should be erased before returning 88 // the object. See GetMostVisitedItemsWrapper() in searchbox_api.js. 89 v8::Handle<v8::Object> GenerateMostVisitedItem( 90 int render_view_id, 91 InstantRestrictedID restricted_id, 92 const InstantMostVisitedItem &mv_item) { 93 // We set the "dir" attribute of the title, so that in RTL locales, a LTR 94 // title is rendered left-to-right and truncated from the right. For 95 // example, the title of http://msdn.microsoft.com/en-us/default.aspx is 96 // "MSDN: Microsoft developer network". In RTL locales, in the New Tab 97 // page, if the "dir" of this title is not specified, it takes Chrome UI's 98 // directionality. So the title will be truncated as "soft developer 99 // network". Setting the "dir" attribute as "ltr" renders the truncated 100 // title as "MSDN: Microsoft D...". As another example, the title of 101 // http://yahoo.com is "Yahoo!". In RTL locales, in the New Tab page, the 102 // title will be rendered as "!Yahoo" if its "dir" attribute is not set to 103 // "ltr". 104 std::string direction; 105 if (base::i18n::StringContainsStrongRTLChars(mv_item.title)) 106 direction = kRTLHtmlTextDirection; 107 else 108 direction = kLTRHtmlTextDirection; 109 110 string16 title = mv_item.title; 111 if (title.empty()) 112 title = UTF8ToUTF16(mv_item.url.spec()); 113 114 v8::Handle<v8::Object> obj = v8::Object::New(); 115 obj->Set(v8::String::New("renderViewId"), v8::Int32::New(render_view_id)); 116 obj->Set(v8::String::New("rid"), v8::Int32::New(restricted_id)); 117 obj->Set(v8::String::New("thumbnailUrl"), 118 GenerateThumbnailURL(render_view_id, restricted_id)); 119 obj->Set(v8::String::New("title"), UTF16ToV8String(title)); 120 obj->Set(v8::String::New("domain"), UTF8ToV8String(mv_item.url.host())); 121 obj->Set(v8::String::New("direction"), UTF8ToV8String(direction)); 122 obj->Set(v8::String::New("url"), UTF8ToV8String(mv_item.url.spec())); 123 return obj; 124 } 125 126 // Returns the render view for the current JS context if it matches |origin|, 127 // otherwise returns NULL. Used to restrict methods that access suggestions and 128 // most visited data to pages with origin chrome-search://most-visited and 129 // chrome-search://suggestions. 130 content::RenderView* GetRenderViewWithCheckedOrigin(const GURL& origin) { 131 WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); 132 if (!webframe) 133 return NULL; 134 WebKit::WebView* webview = webframe->view(); 135 if (!webview) 136 return NULL; // Can happen during closing. 137 content::RenderView* render_view = content::RenderView::FromWebView(webview); 138 if (!render_view) 139 return NULL; 140 141 GURL url(webframe->document().url()); 142 if (url.GetOrigin() != origin.GetOrigin()) 143 return NULL; 144 145 return render_view; 146 } 147 148 // Returns the current URL. 149 GURL GetCurrentURL(content::RenderView* render_view) { 150 WebKit::WebView* webview = render_view->GetWebView(); 151 return webview ? GURL(webview->mainFrame()->document().url()) : GURL(); 152 } 153 154 } // namespace 155 156 namespace internal { // for testing. 157 158 // Returns an array with the RGBA color components. 159 v8::Handle<v8::Value> RGBAColorToArray(const RGBAColor& color) { 160 v8::Handle<v8::Array> color_array = v8::Array::New(4); 161 color_array->Set(0, v8::Int32::New(color.r)); 162 color_array->Set(1, v8::Int32::New(color.g)); 163 color_array->Set(2, v8::Int32::New(color.b)); 164 color_array->Set(3, v8::Int32::New(color.a)); 165 return color_array; 166 } 167 168 // Resolves a possibly relative URL using the current URL. 169 GURL ResolveURL(const GURL& current_url, 170 const string16& possibly_relative_url) { 171 if (current_url.is_valid() && !possibly_relative_url.empty()) 172 return current_url.Resolve(possibly_relative_url); 173 return GURL(possibly_relative_url); 174 } 175 176 } // namespace internal 177 178 namespace extensions_v8 { 179 180 static const char kSearchBoxExtensionName[] = "v8/EmbeddedSearch"; 181 182 // We first send this script down to determine if the page supports instant. 183 static const char kSupportsInstantScript[] = 184 "if (window.chrome &&" 185 " window.chrome.embeddedSearch &&" 186 " window.chrome.embeddedSearch.searchBox &&" 187 " window.chrome.embeddedSearch.searchBox.onsubmit &&" 188 " typeof window.chrome.embeddedSearch.searchBox.onsubmit ==" 189 " 'function') {" 190 " true;" 191 "} else {" 192 " false;" 193 "}"; 194 195 static const char kDispatchFocusChangedScript[] = 196 "if (window.chrome &&" 197 " window.chrome.embeddedSearch &&" 198 " window.chrome.embeddedSearch.searchBox &&" 199 " window.chrome.embeddedSearch.searchBox.onfocuschange &&" 200 " typeof window.chrome.embeddedSearch.searchBox.onfocuschange ==" 201 " 'function') {" 202 " window.chrome.embeddedSearch.searchBox.onfocuschange();" 203 " true;" 204 "}"; 205 206 static const char kDispatchInputCancelScript[] = 207 "if (window.chrome &&" 208 " window.chrome.embeddedSearch &&" 209 " window.chrome.embeddedSearch.newTabPage &&" 210 " window.chrome.embeddedSearch.newTabPage.oninputcancel &&" 211 " typeof window.chrome.embeddedSearch.newTabPage.oninputcancel ==" 212 " 'function') {" 213 " window.chrome.embeddedSearch.newTabPage.oninputcancel();" 214 " true;" 215 "}"; 216 217 static const char kDispatchInputStartScript[] = 218 "if (window.chrome &&" 219 " window.chrome.embeddedSearch &&" 220 " window.chrome.embeddedSearch.newTabPage &&" 221 " window.chrome.embeddedSearch.newTabPage.oninputstart &&" 222 " typeof window.chrome.embeddedSearch.newTabPage.oninputstart ==" 223 " 'function') {" 224 " window.chrome.embeddedSearch.newTabPage.oninputstart();" 225 " true;" 226 "}"; 227 228 static const char kDispatchKeyCaptureChangeScript[] = 229 "if (window.chrome &&" 230 " window.chrome.embeddedSearch &&" 231 " window.chrome.embeddedSearch.searchBox &&" 232 " window.chrome.embeddedSearch.searchBox.onkeycapturechange &&" 233 " typeof window.chrome.embeddedSearch.searchBox.onkeycapturechange ==" 234 " 'function') {" 235 " window.chrome.embeddedSearch.searchBox.onkeycapturechange();" 236 " true;" 237 "}"; 238 239 static const char kDispatchMarginChangeEventScript[] = 240 "if (window.chrome &&" 241 " window.chrome.embeddedSearch &&" 242 " window.chrome.embeddedSearch.searchBox &&" 243 " window.chrome.embeddedSearch.searchBox.onmarginchange &&" 244 " typeof window.chrome.embeddedSearch.searchBox.onmarginchange ==" 245 " 'function') {" 246 " window.chrome.embeddedSearch.searchBox.onmarginchange();" 247 " true;" 248 "}"; 249 250 static const char kDispatchMostVisitedChangedScript[] = 251 "if (window.chrome &&" 252 " window.chrome.embeddedSearch &&" 253 " window.chrome.embeddedSearch.newTabPage &&" 254 " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange &&" 255 " typeof window.chrome.embeddedSearch.newTabPage.onmostvisitedchange ==" 256 " 'function') {" 257 " window.chrome.embeddedSearch.newTabPage.onmostvisitedchange();" 258 " true;" 259 "}"; 260 261 static const char kDispatchSubmitEventScript[] = 262 "if (window.chrome &&" 263 " window.chrome.embeddedSearch &&" 264 " window.chrome.embeddedSearch.searchBox &&" 265 " window.chrome.embeddedSearch.searchBox.onsubmit &&" 266 " typeof window.chrome.embeddedSearch.searchBox.onsubmit ==" 267 " 'function') {" 268 " window.chrome.embeddedSearch.searchBox.onsubmit();" 269 " true;" 270 "}"; 271 272 static const char kDispatchThemeChangeEventScript[] = 273 "if (window.chrome &&" 274 " window.chrome.embeddedSearch &&" 275 " window.chrome.embeddedSearch.newTabPage &&" 276 " window.chrome.embeddedSearch.newTabPage.onthemechange &&" 277 " typeof window.chrome.embeddedSearch.newTabPage.onthemechange ==" 278 " 'function') {" 279 " window.chrome.embeddedSearch.newTabPage.onthemechange();" 280 " true;" 281 "}"; 282 283 static const char kDispatchToggleVoiceSearchScript[] = 284 "if (window.chrome &&" 285 " window.chrome.embeddedSearch &&" 286 " window.chrome.embeddedSearch.searchBox &&" 287 " window.chrome.embeddedSearch.searchBox.ontogglevoicesearch &&" 288 " typeof window.chrome.embeddedSearch.searchBox.ontogglevoicesearch ==" 289 " 'function') {" 290 " window.chrome.embeddedSearch.searchBox.ontogglevoicesearch();" 291 " true;" 292 "}"; 293 294 // ---------------------------------------------------------------------------- 295 296 class SearchBoxExtensionWrapper : public v8::Extension { 297 public: 298 explicit SearchBoxExtensionWrapper(const base::StringPiece& code); 299 300 // Allows v8's javascript code to call the native functions defined 301 // in this class for window.chrome. 302 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 303 v8::Handle<v8::String> name) OVERRIDE; 304 305 // Helper function to find the RenderView. May return NULL. 306 static content::RenderView* GetRenderView(); 307 308 // Deletes a Most Visited item. 309 static void DeleteMostVisitedItem( 310 const v8::FunctionCallbackInfo<v8::Value>& args); 311 312 // Gets whether or not the app launcher is enabled. 313 static void GetAppLauncherEnabled( 314 const v8::FunctionCallbackInfo<v8::Value>& args); 315 316 // Gets the font family of the text in the omnibox. 317 static void GetFont(const v8::FunctionCallbackInfo<v8::Value>& args); 318 319 // Gets the font size of the text in the omnibox. 320 static void GetFontSize(const v8::FunctionCallbackInfo<v8::Value>& args); 321 322 // Gets Most Visited Items. 323 static void GetMostVisitedItems( 324 const v8::FunctionCallbackInfo<v8::Value>& args); 325 326 // Gets the raw data for a most visited item including its raw URL. 327 // GetRenderViewWithCheckedOrigin() enforces that only code in the origin 328 // chrome-search://most-visited can call this function. 329 static void GetMostVisitedItemData( 330 const v8::FunctionCallbackInfo<v8::Value>& args); 331 332 // Gets the submitted value of the user's search query. 333 static void GetQuery(const v8::FunctionCallbackInfo<v8::Value>& args); 334 335 // Returns true if the Searchbox itself is oriented right-to-left. 336 static void GetRightToLeft(const v8::FunctionCallbackInfo<v8::Value>& args); 337 338 // Gets the start-edge margin to use with extended Instant. 339 static void GetStartMargin(const v8::FunctionCallbackInfo<v8::Value>& args); 340 341 // Gets the background info of the theme currently adopted by browser. 342 // Call only when overlay is showing NTP page. 343 static void GetThemeBackgroundInfo( 344 const v8::FunctionCallbackInfo<v8::Value>& args); 345 346 // Gets whether the omnibox has focus or not. 347 static void IsFocused(const v8::FunctionCallbackInfo<v8::Value>& args); 348 349 // Gets whether user input is in progress. 350 static void IsInputInProgress( 351 const v8::FunctionCallbackInfo<v8::Value>& args); 352 353 // Gets whether the browser is capturing key strokes. 354 static void IsKeyCaptureEnabled( 355 const v8::FunctionCallbackInfo<v8::Value>& args); 356 357 // Logs information from the iframes/titles on the NTP. 358 static void LogEvent(const v8::FunctionCallbackInfo<v8::Value>& args); 359 360 // Navigates the window to a URL represented by either a URL string or a 361 // restricted ID. 362 static void NavigateContentWindow( 363 const v8::FunctionCallbackInfo<v8::Value>& args); 364 365 // Pastes provided value or clipboard's content into the omnibox. 366 static void Paste(const v8::FunctionCallbackInfo<v8::Value>& args); 367 368 // Indicates whether the page supports voice search. 369 static void SetVoiceSearchSupported( 370 const v8::FunctionCallbackInfo<v8::Value>& args); 371 372 // Start capturing user key strokes. 373 static void StartCapturingKeyStrokes( 374 const v8::FunctionCallbackInfo<v8::Value>& args); 375 376 // Stop capturing user key strokes. 377 static void StopCapturingKeyStrokes( 378 const v8::FunctionCallbackInfo<v8::Value>& args); 379 380 // Undoes the deletion of all Most Visited itens. 381 static void UndoAllMostVisitedDeletions( 382 const v8::FunctionCallbackInfo<v8::Value>& args); 383 384 // Undoes the deletion of a Most Visited item. 385 static void UndoMostVisitedDeletion( 386 const v8::FunctionCallbackInfo<v8::Value>& args); 387 388 private: 389 DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); 390 }; 391 392 // static 393 v8::Extension* SearchBoxExtension::Get() { 394 return new SearchBoxExtensionWrapper(ResourceBundle::GetSharedInstance(). 395 GetRawDataResource(IDR_SEARCHBOX_API)); 396 } 397 398 // static 399 bool SearchBoxExtension::PageSupportsInstant(WebKit::WebFrame* frame) { 400 if (!frame) return false; 401 v8::HandleScope handle_scope; 402 v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue( 403 WebKit::WebScriptSource(kSupportsInstantScript)); 404 return !v.IsEmpty() && v->BooleanValue(); 405 } 406 407 // static 408 void SearchBoxExtension::DispatchFocusChange(WebKit::WebFrame* frame) { 409 Dispatch(frame, kDispatchFocusChangedScript); 410 } 411 412 // static 413 void SearchBoxExtension::DispatchInputCancel(WebKit::WebFrame* frame) { 414 Dispatch(frame, kDispatchInputCancelScript); 415 } 416 417 // static 418 void SearchBoxExtension::DispatchInputStart(WebKit::WebFrame* frame) { 419 Dispatch(frame, kDispatchInputStartScript); 420 } 421 422 // static 423 void SearchBoxExtension::DispatchKeyCaptureChange(WebKit::WebFrame* frame) { 424 Dispatch(frame, kDispatchKeyCaptureChangeScript); 425 } 426 427 // static 428 void SearchBoxExtension::DispatchMarginChange(WebKit::WebFrame* frame) { 429 Dispatch(frame, kDispatchMarginChangeEventScript); 430 } 431 432 // static 433 void SearchBoxExtension::DispatchMostVisitedChanged( 434 WebKit::WebFrame* frame) { 435 Dispatch(frame, kDispatchMostVisitedChangedScript); 436 } 437 438 // static 439 void SearchBoxExtension::DispatchSubmit(WebKit::WebFrame* frame) { 440 Dispatch(frame, kDispatchSubmitEventScript); 441 } 442 443 // static 444 void SearchBoxExtension::DispatchThemeChange(WebKit::WebFrame* frame) { 445 Dispatch(frame, kDispatchThemeChangeEventScript); 446 } 447 448 // static 449 void SearchBoxExtension::DispatchToggleVoiceSearch( 450 WebKit::WebFrame* frame) { 451 Dispatch(frame, kDispatchToggleVoiceSearchScript); 452 } 453 454 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper( 455 const base::StringPiece& code) 456 : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) { 457 } 458 459 v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( 460 v8::Handle<v8::String> name) { 461 if (name->Equals(v8::String::New("DeleteMostVisitedItem"))) 462 return v8::FunctionTemplate::New(DeleteMostVisitedItem); 463 if (name->Equals(v8::String::New("GetAppLauncherEnabled"))) 464 return v8::FunctionTemplate::New(GetAppLauncherEnabled); 465 if (name->Equals(v8::String::New("GetFont"))) 466 return v8::FunctionTemplate::New(GetFont); 467 if (name->Equals(v8::String::New("GetFontSize"))) 468 return v8::FunctionTemplate::New(GetFontSize); 469 if (name->Equals(v8::String::New("GetMostVisitedItems"))) 470 return v8::FunctionTemplate::New(GetMostVisitedItems); 471 if (name->Equals(v8::String::New("GetMostVisitedItemData"))) 472 return v8::FunctionTemplate::New(GetMostVisitedItemData); 473 if (name->Equals(v8::String::New("GetQuery"))) 474 return v8::FunctionTemplate::New(GetQuery); 475 if (name->Equals(v8::String::New("GetRightToLeft"))) 476 return v8::FunctionTemplate::New(GetRightToLeft); 477 if (name->Equals(v8::String::New("GetStartMargin"))) 478 return v8::FunctionTemplate::New(GetStartMargin); 479 if (name->Equals(v8::String::New("GetThemeBackgroundInfo"))) 480 return v8::FunctionTemplate::New(GetThemeBackgroundInfo); 481 if (name->Equals(v8::String::New("IsFocused"))) 482 return v8::FunctionTemplate::New(IsFocused); 483 if (name->Equals(v8::String::New("IsInputInProgress"))) 484 return v8::FunctionTemplate::New(IsInputInProgress); 485 if (name->Equals(v8::String::New("IsKeyCaptureEnabled"))) 486 return v8::FunctionTemplate::New(IsKeyCaptureEnabled); 487 if (name->Equals(v8::String::New("LogEvent"))) 488 return v8::FunctionTemplate::New(LogEvent); 489 if (name->Equals(v8::String::New("NavigateContentWindow"))) 490 return v8::FunctionTemplate::New(NavigateContentWindow); 491 if (name->Equals(v8::String::New("Paste"))) 492 return v8::FunctionTemplate::New(Paste); 493 if (name->Equals(v8::String::New("SetVoiceSearchSupported"))) 494 return v8::FunctionTemplate::New(SetVoiceSearchSupported); 495 if (name->Equals(v8::String::New("StartCapturingKeyStrokes"))) 496 return v8::FunctionTemplate::New(StartCapturingKeyStrokes); 497 if (name->Equals(v8::String::New("StopCapturingKeyStrokes"))) 498 return v8::FunctionTemplate::New(StopCapturingKeyStrokes); 499 if (name->Equals(v8::String::New("UndoAllMostVisitedDeletions"))) 500 return v8::FunctionTemplate::New(UndoAllMostVisitedDeletions); 501 if (name->Equals(v8::String::New("UndoMostVisitedDeletion"))) 502 return v8::FunctionTemplate::New(UndoMostVisitedDeletion); 503 return v8::Handle<v8::FunctionTemplate>(); 504 } 505 506 // static 507 content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { 508 WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext(); 509 if (!webframe) return NULL; 510 511 WebKit::WebView* webview = webframe->view(); 512 if (!webview) return NULL; // can happen during closing 513 514 return content::RenderView::FromWebView(webview); 515 } 516 517 // static 518 void SearchBoxExtensionWrapper::DeleteMostVisitedItem( 519 const v8::FunctionCallbackInfo<v8::Value>& args) { 520 content::RenderView* render_view = GetRenderView(); 521 if (!render_view || !args.Length()) return; 522 523 DVLOG(1) << render_view << " DeleteMostVisitedItem"; 524 SearchBox::Get(render_view)->DeleteMostVisitedItem(args[0]->IntegerValue()); 525 } 526 527 // static 528 void SearchBoxExtensionWrapper::GetAppLauncherEnabled( 529 const v8::FunctionCallbackInfo<v8::Value>& args) { 530 content::RenderView* render_view = GetRenderView(); 531 if (!render_view) return; 532 533 args.GetReturnValue().Set( 534 SearchBox::Get(render_view)->app_launcher_enabled()); 535 } 536 537 // static 538 void SearchBoxExtensionWrapper::GetFont( 539 const v8::FunctionCallbackInfo<v8::Value>& args) { 540 content::RenderView* render_view = GetRenderView(); 541 if (!render_view) return; 542 543 args.GetReturnValue().Set( 544 UTF16ToV8String(SearchBox::Get(render_view)->omnibox_font())); 545 } 546 547 // static 548 void SearchBoxExtensionWrapper::GetFontSize( 549 const v8::FunctionCallbackInfo<v8::Value>& args) { 550 content::RenderView* render_view = GetRenderView(); 551 if (!render_view) return; 552 553 args.GetReturnValue().Set(static_cast<uint32_t>( 554 SearchBox::Get(render_view)->omnibox_font_size())); 555 } 556 557 // static 558 void SearchBoxExtensionWrapper::GetMostVisitedItems( 559 const v8::FunctionCallbackInfo<v8::Value>& args) { 560 content::RenderView* render_view = GetRenderView(); 561 if (!render_view) 562 return; 563 DVLOG(1) << render_view << " GetMostVisitedItems"; 564 565 const SearchBox* search_box = SearchBox::Get(render_view); 566 567 std::vector<InstantMostVisitedItemIDPair> instant_mv_items; 568 search_box->GetMostVisitedItems(&instant_mv_items); 569 v8::Handle<v8::Array> v8_mv_items = v8::Array::New(instant_mv_items.size()); 570 for (size_t i = 0; i < instant_mv_items.size(); ++i) { 571 v8_mv_items->Set(i, GenerateMostVisitedItem(render_view->GetRoutingID(), 572 instant_mv_items[i].first, 573 instant_mv_items[i].second)); 574 } 575 args.GetReturnValue().Set(v8_mv_items); 576 } 577 578 // static 579 void SearchBoxExtensionWrapper::GetMostVisitedItemData( 580 const v8::FunctionCallbackInfo<v8::Value>& args) { 581 content::RenderView* render_view = GetRenderViewWithCheckedOrigin( 582 GURL(chrome::kChromeSearchMostVisitedUrl)); 583 if (!render_view) return; 584 585 // Need an rid argument. 586 if (args.Length() < 1 || !args[0]->IsNumber()) 587 return; 588 589 DVLOG(1) << render_view << " GetMostVisitedItem"; 590 InstantRestrictedID restricted_id = args[0]->IntegerValue(); 591 InstantMostVisitedItem mv_item; 592 if (!SearchBox::Get(render_view)->GetMostVisitedItemWithID( 593 restricted_id, &mv_item)) { 594 return; 595 } 596 args.GetReturnValue().Set( 597 GenerateMostVisitedItem(render_view->GetRoutingID(), restricted_id, 598 mv_item)); 599 } 600 601 // static 602 void SearchBoxExtensionWrapper::GetQuery( 603 const v8::FunctionCallbackInfo<v8::Value>& args) { 604 content::RenderView* render_view = GetRenderView(); 605 if (!render_view) return; 606 const string16& query = SearchBox::Get(render_view)->query(); 607 DVLOG(1) << render_view << " GetQuery: '" << query << "'"; 608 args.GetReturnValue().Set(UTF16ToV8String(query)); 609 } 610 611 // static 612 void SearchBoxExtensionWrapper::GetRightToLeft( 613 const v8::FunctionCallbackInfo<v8::Value>& args) { 614 args.GetReturnValue().Set(base::i18n::IsRTL()); 615 } 616 617 // static 618 void SearchBoxExtensionWrapper::GetStartMargin( 619 const v8::FunctionCallbackInfo<v8::Value>& args) { 620 content::RenderView* render_view = GetRenderView(); 621 if (!render_view) return; 622 args.GetReturnValue().Set(static_cast<int32_t>( 623 SearchBox::Get(render_view)->start_margin())); 624 } 625 626 // static 627 void SearchBoxExtensionWrapper::GetThemeBackgroundInfo( 628 const v8::FunctionCallbackInfo<v8::Value>& args) { 629 content::RenderView* render_view = GetRenderView(); 630 if (!render_view) return; 631 632 DVLOG(1) << render_view << " GetThemeBackgroundInfo"; 633 const ThemeBackgroundInfo& theme_info = 634 SearchBox::Get(render_view)->GetThemeBackgroundInfo(); 635 v8::Handle<v8::Object> info = v8::Object::New(); 636 637 info->Set(v8::String::New("usingDefaultTheme"), 638 v8::Boolean::New(theme_info.using_default_theme)); 639 640 // The theme background color is in RGBA format "rgba(R,G,B,A)" where R, G and 641 // B are between 0 and 255 inclusive, and A is a double between 0 and 1 642 // inclusive. 643 // This is the CSS "background-color" format. 644 // Value is always valid. 645 // TODO(jfweitz): Remove this field after GWS is modified to use the new 646 // backgroundColorRgba field. 647 info->Set(v8::String::New("colorRgba"), UTF8ToV8String( 648 // Convert the alpha using DoubleToString because StringPrintf will use 649 // locale specific formatters (e.g., use , instead of . in German). 650 base::StringPrintf( 651 kCSSBackgroundColorFormat, 652 theme_info.background_color.r, 653 theme_info.background_color.g, 654 theme_info.background_color.b, 655 base::DoubleToString( 656 theme_info.background_color.a / 255.0).c_str()))); 657 658 // Theme color for background as an array with the RGBA components in order. 659 // Value is always valid. 660 info->Set(v8::String::New("backgroundColorRgba"), 661 internal::RGBAColorToArray(theme_info.background_color)); 662 663 // Theme color for text as an array with the RGBA components in order. 664 // Value is always valid. 665 info->Set(v8::String::New("textColorRgba"), 666 internal::RGBAColorToArray(theme_info.text_color)); 667 668 // Theme color for links as an array with the RGBA components in order. 669 // Value is always valid. 670 info->Set(v8::String::New("linkColorRgba"), 671 internal::RGBAColorToArray(theme_info.link_color)); 672 673 // Theme color for light text as an array with the RGBA components in order. 674 // Value is always valid. 675 info->Set(v8::String::New("textColorLightRgba"), 676 internal::RGBAColorToArray(theme_info.text_color_light)); 677 678 // Theme color for header as an array with the RGBA components in order. 679 // Value is always valid. 680 info->Set(v8::String::New("headerColorRgba"), 681 internal::RGBAColorToArray(theme_info.header_color)); 682 683 // Theme color for section border as an array with the RGBA components in 684 // order. Value is always valid. 685 info->Set(v8::String::New("sectionBorderColorRgba"), 686 internal::RGBAColorToArray(theme_info.section_border_color)); 687 688 // The theme alternate logo value indicates a white logo when TRUE and a 689 // colorful one when FALSE. 690 info->Set(v8::String::New("alternateLogo"), 691 v8::Boolean::New(theme_info.logo_alternate)); 692 693 // The theme background image url is of format kCSSBackgroundImageFormat 694 // where both instances of "%s" are replaced with the id that identifies the 695 // theme. 696 // This is the CSS "background-image" format. 697 // Value is only valid if there's a custom theme background image. 698 if (extensions::Extension::IdIsValid(theme_info.theme_id)) { 699 info->Set(v8::String::New("imageUrl"), UTF8ToV8String( 700 base::StringPrintf(kCSSBackgroundImageFormat, 701 theme_info.theme_id.c_str(), 702 theme_info.theme_id.c_str()))); 703 704 // The theme background image horizontal alignment is one of "left", 705 // "right", "center". 706 // This is the horizontal component of the CSS "background-position" format. 707 // Value is only valid if |imageUrl| is not empty. 708 std::string alignment = kCSSBackgroundPositionCenter; 709 if (theme_info.image_horizontal_alignment == 710 THEME_BKGRND_IMAGE_ALIGN_LEFT) { 711 alignment = kCSSBackgroundPositionLeft; 712 } else if (theme_info.image_horizontal_alignment == 713 THEME_BKGRND_IMAGE_ALIGN_RIGHT) { 714 alignment = kCSSBackgroundPositionRight; 715 } 716 info->Set(v8::String::New("imageHorizontalAlignment"), 717 UTF8ToV8String(alignment)); 718 719 // The theme background image vertical alignment is one of "top", "bottom", 720 // "center". 721 // This is the vertical component of the CSS "background-position" format. 722 // Value is only valid if |image_url| is not empty. 723 if (theme_info.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) { 724 alignment = kCSSBackgroundPositionTop; 725 } else if (theme_info.image_vertical_alignment == 726 THEME_BKGRND_IMAGE_ALIGN_BOTTOM) { 727 alignment = kCSSBackgroundPositionBottom; 728 } else { 729 alignment = kCSSBackgroundPositionCenter; 730 } 731 info->Set(v8::String::New("imageVerticalAlignment"), 732 UTF8ToV8String(alignment)); 733 734 // The tiling of the theme background image is one of "no-repeat", 735 // "repeat-x", "repeat-y", "repeat". 736 // This is the CSS "background-repeat" format. 737 // Value is only valid if |image_url| is not empty. 738 std::string tiling = kCSSBackgroundRepeatNo; 739 switch (theme_info.image_tiling) { 740 case THEME_BKGRND_IMAGE_NO_REPEAT: 741 tiling = kCSSBackgroundRepeatNo; 742 break; 743 case THEME_BKGRND_IMAGE_REPEAT_X: 744 tiling = kCSSBackgroundRepeatX; 745 break; 746 case THEME_BKGRND_IMAGE_REPEAT_Y: 747 tiling = kCSSBackgroundRepeatY; 748 break; 749 case THEME_BKGRND_IMAGE_REPEAT: 750 tiling = kCSSBackgroundRepeat; 751 break; 752 } 753 info->Set(v8::String::New("imageTiling"), UTF8ToV8String(tiling)); 754 755 // The theme background image height is only valid if |imageUrl| is valid. 756 info->Set(v8::String::New("imageHeight"), 757 v8::Int32::New(theme_info.image_height)); 758 759 // The attribution URL is only valid if the theme has attribution logo. 760 if (theme_info.has_attribution) { 761 info->Set(v8::String::New("attributionUrl"), UTF8ToV8String( 762 base::StringPrintf(kThemeAttributionFormat, 763 theme_info.theme_id.c_str(), 764 theme_info.theme_id.c_str()))); 765 } 766 } 767 768 args.GetReturnValue().Set(info); 769 } 770 771 // static 772 void SearchBoxExtensionWrapper::IsFocused( 773 const v8::FunctionCallbackInfo<v8::Value>& args) { 774 content::RenderView* render_view = GetRenderView(); 775 if (!render_view) return; 776 777 bool is_focused = SearchBox::Get(render_view)->is_focused(); 778 DVLOG(1) << render_view << " IsFocused: " << is_focused; 779 args.GetReturnValue().Set(is_focused); 780 } 781 782 // static 783 void SearchBoxExtensionWrapper::IsInputInProgress( 784 const v8::FunctionCallbackInfo<v8::Value>& args) { 785 content::RenderView* render_view = GetRenderView(); 786 if (!render_view) return; 787 788 bool is_input_in_progress = 789 SearchBox::Get(render_view)->is_input_in_progress(); 790 DVLOG(1) << render_view << " IsInputInProgress: " << is_input_in_progress; 791 args.GetReturnValue().Set(is_input_in_progress); 792 } 793 794 // static 795 void SearchBoxExtensionWrapper::IsKeyCaptureEnabled( 796 const v8::FunctionCallbackInfo<v8::Value>& args) { 797 content::RenderView* render_view = GetRenderView(); 798 if (!render_view) return; 799 800 args.GetReturnValue().Set(SearchBox::Get(render_view)-> 801 is_key_capture_enabled()); 802 } 803 804 // static 805 void SearchBoxExtensionWrapper::LogEvent( 806 const v8::FunctionCallbackInfo<v8::Value>& args) { 807 content::RenderView* render_view = GetRenderViewWithCheckedOrigin( 808 GURL(chrome::kChromeSearchMostVisitedUrl)); 809 if (!render_view) return; 810 811 if (args.Length() < 1 || !args[0]->IsString()) 812 return; 813 814 DVLOG(1) << render_view << " LogEvent"; 815 816 std::string histogram_name = *v8::String::Utf8Value(args[0]->ToString()); 817 818 if (histogram_name == "NewTabPage.NumberOfMouseOvers") 819 SearchBox::Get(render_view)->CountMouseover(); 820 else 821 DVLOG(1) << render_view << " Unsupported histogram name"; 822 } 823 824 // static 825 void SearchBoxExtensionWrapper::NavigateContentWindow( 826 const v8::FunctionCallbackInfo<v8::Value>& args) { 827 content::RenderView* render_view = GetRenderView(); 828 if (!render_view || !args.Length()) return; 829 830 GURL destination_url; 831 content::PageTransition transition = content::PAGE_TRANSITION_AUTO_BOOKMARK; 832 833 // Check if the url is a rid 834 if (args[0]->IsNumber()) { 835 InstantMostVisitedItem item; 836 if (SearchBox::Get(render_view)->GetMostVisitedItemWithID( 837 args[0]->IntegerValue(), &item)) { 838 destination_url = item.url; 839 } 840 } else { 841 // Resolve the URL 842 const string16& possibly_relative_url = V8ValueToUTF16(args[0]); 843 GURL current_url = GetCurrentURL(render_view); 844 destination_url = internal::ResolveURL(current_url, possibly_relative_url); 845 } 846 847 DVLOG(1) << render_view << " NavigateContentWindow: " << destination_url; 848 849 // Navigate the main frame. 850 if (destination_url.is_valid()) { 851 WindowOpenDisposition disposition = CURRENT_TAB; 852 if (args[1]->Uint32Value() == 2) 853 disposition = NEW_BACKGROUND_TAB; 854 SearchBox::Get(render_view)->NavigateToURL( 855 destination_url, transition, disposition, false); 856 } 857 } 858 859 // static 860 void SearchBoxExtensionWrapper::Paste( 861 const v8::FunctionCallbackInfo<v8::Value>& args) { 862 content::RenderView* render_view = GetRenderView(); 863 if (!render_view) return; 864 865 string16 text; 866 if (!args[0]->IsUndefined()) 867 text = V8ValueToUTF16(args[0]); 868 869 DVLOG(1) << render_view << " Paste: " << text; 870 SearchBox::Get(render_view)->Paste(text); 871 } 872 873 // static 874 void SearchBoxExtensionWrapper::StartCapturingKeyStrokes( 875 const v8::FunctionCallbackInfo<v8::Value>& args) { 876 content::RenderView* render_view = GetRenderView(); 877 if (!render_view) return; 878 879 DVLOG(1) << render_view << " StartCapturingKeyStrokes"; 880 SearchBox::Get(render_view)->StartCapturingKeyStrokes(); 881 } 882 883 // static 884 void SearchBoxExtensionWrapper::StopCapturingKeyStrokes( 885 const v8::FunctionCallbackInfo<v8::Value>& args) { 886 content::RenderView* render_view = GetRenderView(); 887 if (!render_view) return; 888 889 DVLOG(1) << render_view << " StopCapturingKeyStrokes"; 890 SearchBox::Get(render_view)->StopCapturingKeyStrokes(); 891 } 892 893 // static 894 void SearchBoxExtensionWrapper::SetVoiceSearchSupported( 895 const v8::FunctionCallbackInfo<v8::Value>& args) { 896 content::RenderView* render_view = GetRenderView(); 897 if (!render_view || args.Length() < 1) return; 898 899 DVLOG(1) << render_view << " SetVoiceSearchSupported"; 900 SearchBox::Get(render_view)->SetVoiceSearchSupported(args[0]->BooleanValue()); 901 } 902 903 // static 904 void SearchBoxExtensionWrapper::UndoAllMostVisitedDeletions( 905 const v8::FunctionCallbackInfo<v8::Value>& args) { 906 content::RenderView* render_view = GetRenderView(); 907 if (!render_view) return; 908 909 DVLOG(1) << render_view << " UndoAllMostVisitedDeletions"; 910 SearchBox::Get(render_view)->UndoAllMostVisitedDeletions(); 911 } 912 913 // static 914 void SearchBoxExtensionWrapper::UndoMostVisitedDeletion( 915 const v8::FunctionCallbackInfo<v8::Value>& args) { 916 content::RenderView* render_view = GetRenderView(); 917 if (!render_view || !args.Length()) return; 918 919 DVLOG(1) << render_view << " UndoMostVisitedDeletion"; 920 SearchBox::Get(render_view)->UndoMostVisitedDeletion(args[0]->IntegerValue()); 921 } 922 923 } // namespace extensions_v8 924