Home | History | Annotate | Download | only in searchbox
      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