OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/searchbox/searchbox_extension.h" | 5 #include "chrome/renderer/searchbox/searchbox_extension.h" |
6 | 6 |
| 7 #include <ctype.h> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/string_number_conversions.h" |
| 11 #include "base/string_piece.h" |
| 12 #include "base/string_util.h" |
| 13 #include "base/utf_string_conversions.h" |
7 #include "chrome/renderer/searchbox/searchbox.h" | 14 #include "chrome/renderer/searchbox/searchbox.h" |
8 #include "content/public/renderer/render_view.h" | 15 #include "content/public/renderer/render_view.h" |
9 #include "grit/renderer_resources.h" | 16 #include "grit/renderer_resources.h" |
10 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | 17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" | 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" |
12 #include "ui/base/resource/resource_bundle.h" | 20 #include "ui/base/resource/resource_bundle.h" |
13 #include "v8/include/v8.h" | 21 #include "v8/include/v8.h" |
14 | 22 |
| 23 namespace { |
| 24 |
| 25 // Splits the string in |number| into two pieces, a leading number token (saved |
| 26 // in |number|) and the rest (saved in |suffix|). Either piece may become empty, |
| 27 // depending on whether the input had no digits or only digits. Neither argument |
| 28 // may be NULL. |
| 29 void SplitLeadingNumberToken(std::string* number, std::string* suffix) { |
| 30 size_t i = 0; |
| 31 while (i < number->size() && isdigit((*number)[i])) |
| 32 ++i; |
| 33 suffix->assign(*number, i, number->size() - i); |
| 34 number->resize(i); |
| 35 } |
| 36 |
| 37 // Converts a V8 value to a string16. |
| 38 string16 V8ValueToUTF16(v8::Handle<v8::Value> v) { |
| 39 v8::String::Value s(v); |
| 40 return string16(reinterpret_cast<const char16*>(*s), s.length()); |
| 41 } |
| 42 |
| 43 // Converts string16 to V8 String. |
| 44 v8::Handle<v8::String> UTF16ToV8String(const string16& s) { |
| 45 return v8::String::New(reinterpret_cast<const uint16_t*>(s.data()), s.size()); |
| 46 } |
| 47 |
| 48 } // namespace |
| 49 |
15 namespace extensions_v8 { | 50 namespace extensions_v8 { |
16 | 51 |
17 static const char kSearchBoxExtensionName[] = "v8/SearchBox"; | 52 static const char kSearchBoxExtensionName[] = "v8/SearchBox"; |
18 | 53 |
19 static const char kDispatchChangeEventScript[] = | 54 static const char kDispatchChangeEventScript[] = |
20 "if (window.chrome &&" | 55 "if (window.chrome &&" |
21 " window.chrome.searchBox &&" | 56 " window.chrome.searchBox &&" |
22 " window.chrome.searchBox.onchange &&" | 57 " window.chrome.searchBox.onchange &&" |
23 " typeof window.chrome.searchBox.onchange == 'function') {" | 58 " typeof window.chrome.searchBox.onchange == 'function') {" |
24 " window.chrome.searchBox.onchange();" | 59 " window.chrome.searchBox.onchange();" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 static const char kSupportsInstantScript[] = | 91 static const char kSupportsInstantScript[] = |
57 "if (window.chrome &&" | 92 "if (window.chrome &&" |
58 " window.chrome.searchBox &&" | 93 " window.chrome.searchBox &&" |
59 " window.chrome.searchBox.onsubmit &&" | 94 " window.chrome.searchBox.onsubmit &&" |
60 " typeof window.chrome.searchBox.onsubmit == 'function') {" | 95 " typeof window.chrome.searchBox.onsubmit == 'function') {" |
61 " true;" | 96 " true;" |
62 "} else {" | 97 "} else {" |
63 " false;" | 98 " false;" |
64 "}"; | 99 "}"; |
65 | 100 |
| 101 // Extended API. |
| 102 static const char kDispatchAutocompleteResultsEventScript[] = |
| 103 "if (window.chrome &&" |
| 104 " window.chrome.searchBox &&" |
| 105 " window.chrome.searchBox.onnativesuggestions &&" |
| 106 " typeof window.chrome.searchBox.onnativesuggestions == 'function') {" |
| 107 " window.chrome.searchBox.onnativesuggestions();" |
| 108 " true;" |
| 109 "}"; |
| 110 |
| 111 static const char kDispatchKeyPressEventScript[] = |
| 112 "if (window.chrome &&" |
| 113 " window.chrome.searchBox &&" |
| 114 " window.chrome.searchBox.onkeypress &&" |
| 115 " typeof window.chrome.searchBox.onkeypress == 'function') {" |
| 116 " window.chrome.searchBox.onkeypress(" |
| 117 " {keyCode:window.chrome.searchBox.keyCode});" |
| 118 " true;" |
| 119 "}"; |
| 120 |
66 // ---------------------------------------------------------------------------- | 121 // ---------------------------------------------------------------------------- |
67 | 122 |
68 class SearchBoxExtensionWrapper : public v8::Extension { | 123 class SearchBoxExtensionWrapper : public v8::Extension { |
69 public: | 124 public: |
70 explicit SearchBoxExtensionWrapper(const base::StringPiece& code); | 125 explicit SearchBoxExtensionWrapper(const base::StringPiece& code); |
71 | 126 |
72 // Allows v8's javascript code to call the native functions defined | 127 // Allows v8's javascript code to call the native functions defined |
73 // in this class for window.chrome. | 128 // in this class for window.chrome. |
74 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | 129 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
75 v8::Handle<v8::String> name); | 130 v8::Handle<v8::String> name); |
76 | 131 |
77 // Helper function to find the RenderView. May return NULL. | 132 // Helper function to find the RenderView. May return NULL. |
78 static content::RenderView* GetRenderView(); | 133 static content::RenderView* GetRenderView(); |
79 | 134 |
80 // Gets the value of the user's search query. | 135 // Gets the value of the user's search query. |
81 static v8::Handle<v8::Value> GetValue(const v8::Arguments& args); | 136 static v8::Handle<v8::Value> GetQuery(const v8::Arguments& args); |
82 | 137 |
83 // Gets whether the |value| should be considered final -- as opposed to a | 138 // Gets whether the |value| should be considered final -- as opposed to a |
84 // partial match. This may be set if the user clicks a suggestion, presses | 139 // partial match. This may be set if the user clicks a suggestion, presses |
85 // forward delete, or in other cases where Chrome overrides. | 140 // forward delete, or in other cases where Chrome overrides. |
86 static v8::Handle<v8::Value> GetVerbatim(const v8::Arguments& args); | 141 static v8::Handle<v8::Value> GetVerbatim(const v8::Arguments& args); |
87 | 142 |
88 // Gets the start of the selection in the search box. | 143 // Gets the start of the selection in the search box. |
89 static v8::Handle<v8::Value> GetSelectionStart(const v8::Arguments& args); | 144 static v8::Handle<v8::Value> GetSelectionStart(const v8::Arguments& args); |
90 | 145 |
91 // Gets the end of the selection in the search box. | 146 // Gets the end of the selection in the search box. |
92 static v8::Handle<v8::Value> GetSelectionEnd(const v8::Arguments& args); | 147 static v8::Handle<v8::Value> GetSelectionEnd(const v8::Arguments& args); |
93 | 148 |
94 // Gets the x coordinate (relative to |window|) of the left edge of the | 149 // Gets the x coordinate (relative to |window|) of the left edge of the |
95 // region of the search box that overlaps the window. | 150 // region of the search box that overlaps the window. |
96 static v8::Handle<v8::Value> GetX(const v8::Arguments& args); | 151 static v8::Handle<v8::Value> GetX(const v8::Arguments& args); |
97 | 152 |
98 // Gets the y coordinate (relative to |window|) of the right edge of the | 153 // Gets the y coordinate (relative to |window|) of the right edge of the |
99 // region of the search box that overlaps the window. | 154 // region of the search box that overlaps the window. |
100 static v8::Handle<v8::Value> GetY(const v8::Arguments& args); | 155 static v8::Handle<v8::Value> GetY(const v8::Arguments& args); |
101 | 156 |
102 // Gets the width of the region of the search box that overlaps the window. | 157 // Gets the width of the region of the search box that overlaps the window. |
103 static v8::Handle<v8::Value> GetWidth(const v8::Arguments& args); | 158 static v8::Handle<v8::Value> GetWidth(const v8::Arguments& args); |
104 | 159 |
105 // Gets the height of the region of the search box that overlaps the window. | 160 // Gets the height of the region of the search box that overlaps the window. |
106 static v8::Handle<v8::Value> GetHeight(const v8::Arguments& args); | 161 static v8::Handle<v8::Value> GetHeight(const v8::Arguments& args); |
107 | 162 |
| 163 // Gets the autocomplete results from search box. |
| 164 static v8::Handle<v8::Value> GetAutocompleteResults( |
| 165 const v8::Arguments& args); |
| 166 |
| 167 // Gets the last key code entered in search box. |
| 168 static v8::Handle<v8::Value> GetKeyCode(const v8::Arguments& args); |
| 169 |
108 // Sets ordered suggestions. Valid for current |value|. | 170 // Sets ordered suggestions. Valid for current |value|. |
109 static v8::Handle<v8::Value> SetSuggestions(const v8::Arguments& args); | 171 static v8::Handle<v8::Value> SetSuggestions(const v8::Arguments& args); |
110 | 172 |
| 173 // Sets the text to be autocompleted into the search box. |
| 174 static v8::Handle<v8::Value> SetQuerySuggestion(const v8::Arguments& args); |
| 175 |
| 176 // Like |SetQuerySuggestion| but uses a restricted ID to identify the text. |
| 177 static v8::Handle<v8::Value> SetQuerySuggestionFromAutocompleteResult( |
| 178 const v8::Arguments& args); |
| 179 |
| 180 // Sets the search box text, completely replacing what the user typed. |
| 181 static v8::Handle<v8::Value> SetQuery(const v8::Arguments& args); |
| 182 |
| 183 // Like |SetQuery| but uses a restricted ID to identify the text. |
| 184 static v8::Handle<v8::Value> SetQueryFromAutocompleteResult( |
| 185 const v8::Arguments& args); |
| 186 |
| 187 // Resize the preview to the given height. |
| 188 static v8::Handle<v8::Value> SetPreviewHeight(const v8::Arguments& args); |
| 189 |
111 private: | 190 private: |
112 DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); | 191 DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); |
113 }; | 192 }; |
114 | 193 |
115 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper( | 194 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper( |
116 const base::StringPiece& code) | 195 const base::StringPiece& code) |
117 : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) { | 196 : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) { |
118 } | 197 } |
119 | 198 |
120 v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( | 199 v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( |
121 v8::Handle<v8::String> name) { | 200 v8::Handle<v8::String> name) { |
122 if (name->Equals(v8::String::New("GetValue"))) { | 201 if (name->Equals(v8::String::New("GetQuery"))) { |
123 return v8::FunctionTemplate::New(GetValue); | 202 return v8::FunctionTemplate::New(GetQuery); |
124 } else if (name->Equals(v8::String::New("GetVerbatim"))) { | 203 } else if (name->Equals(v8::String::New("GetVerbatim"))) { |
125 return v8::FunctionTemplate::New(GetVerbatim); | 204 return v8::FunctionTemplate::New(GetVerbatim); |
126 } else if (name->Equals(v8::String::New("GetSelectionStart"))) { | 205 } else if (name->Equals(v8::String::New("GetSelectionStart"))) { |
127 return v8::FunctionTemplate::New(GetSelectionStart); | 206 return v8::FunctionTemplate::New(GetSelectionStart); |
128 } else if (name->Equals(v8::String::New("GetSelectionEnd"))) { | 207 } else if (name->Equals(v8::String::New("GetSelectionEnd"))) { |
129 return v8::FunctionTemplate::New(GetSelectionEnd); | 208 return v8::FunctionTemplate::New(GetSelectionEnd); |
130 } else if (name->Equals(v8::String::New("GetX"))) { | 209 } else if (name->Equals(v8::String::New("GetX"))) { |
131 return v8::FunctionTemplate::New(GetX); | 210 return v8::FunctionTemplate::New(GetX); |
132 } else if (name->Equals(v8::String::New("GetY"))) { | 211 } else if (name->Equals(v8::String::New("GetY"))) { |
133 return v8::FunctionTemplate::New(GetY); | 212 return v8::FunctionTemplate::New(GetY); |
134 } else if (name->Equals(v8::String::New("GetWidth"))) { | 213 } else if (name->Equals(v8::String::New("GetWidth"))) { |
135 return v8::FunctionTemplate::New(GetWidth); | 214 return v8::FunctionTemplate::New(GetWidth); |
136 } else if (name->Equals(v8::String::New("GetHeight"))) { | 215 } else if (name->Equals(v8::String::New("GetHeight"))) { |
137 return v8::FunctionTemplate::New(GetHeight); | 216 return v8::FunctionTemplate::New(GetHeight); |
| 217 } else if (name->Equals(v8::String::New("GetAutocompleteResults"))) { |
| 218 return v8::FunctionTemplate::New(GetAutocompleteResults); |
| 219 } else if (name->Equals(v8::String::New("GetKeyCode"))) { |
| 220 return v8::FunctionTemplate::New(GetKeyCode); |
138 } else if (name->Equals(v8::String::New("SetSuggestions"))) { | 221 } else if (name->Equals(v8::String::New("SetSuggestions"))) { |
139 return v8::FunctionTemplate::New(SetSuggestions); | 222 return v8::FunctionTemplate::New(SetSuggestions); |
| 223 } else if (name->Equals(v8::String::New("SetQuerySuggestion"))) { |
| 224 return v8::FunctionTemplate::New(SetQuerySuggestion); |
| 225 } else if (name->Equals(v8::String::New( |
| 226 "SetQuerySuggestionFromAutocompleteResult"))) { |
| 227 return v8::FunctionTemplate::New(SetQuerySuggestionFromAutocompleteResult); |
| 228 } else if (name->Equals(v8::String::New("SetQuery"))) { |
| 229 return v8::FunctionTemplate::New(SetQuery); |
| 230 } else if (name->Equals(v8::String::New("SetQueryFromAutocompleteResult"))) { |
| 231 return v8::FunctionTemplate::New(SetQueryFromAutocompleteResult); |
| 232 } else if (name->Equals(v8::String::New("SetPreviewHeight"))) { |
| 233 return v8::FunctionTemplate::New(SetPreviewHeight); |
140 } | 234 } |
141 return v8::Handle<v8::FunctionTemplate>(); | 235 return v8::Handle<v8::FunctionTemplate>(); |
142 } | 236 } |
143 | 237 |
144 // static | 238 // static |
145 content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { | 239 content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { |
146 WebKit::WebFrame* webframe = WebKit::WebFrame::frameForEnteredContext(); | 240 WebKit::WebFrame* webframe = WebKit::WebFrame::frameForEnteredContext(); |
147 DCHECK(webframe) << "There should be an active frame since we just got " | 241 DCHECK(webframe) << "There should be an active frame since we just got " |
148 "a native function called."; | 242 "a native function called."; |
149 if (!webframe) return NULL; | 243 if (!webframe) return NULL; |
150 | 244 |
151 WebKit::WebView* webview = webframe->view(); | 245 WebKit::WebView* webview = webframe->view(); |
152 if (!webview) return NULL; // can happen during closing | 246 if (!webview) return NULL; // can happen during closing |
153 | 247 |
154 return content::RenderView::FromWebView(webview); | 248 return content::RenderView::FromWebView(webview); |
155 } | 249 } |
156 | 250 |
157 // static | 251 // static |
158 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetValue( | 252 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetQuery( |
159 const v8::Arguments& args) { | 253 const v8::Arguments& args) { |
160 content::RenderView* render_view = GetRenderView(); | 254 content::RenderView* render_view = GetRenderView(); |
161 if (!render_view) return v8::Undefined(); | 255 if (!render_view) return v8::Undefined(); |
162 return v8::String::New( | 256 return UTF16ToV8String(SearchBox::Get(render_view)->query()); |
163 reinterpret_cast<const uint16_t*>( | |
164 SearchBox::Get(render_view)->value().data()), | |
165 SearchBox::Get(render_view)->value().length()); | |
166 } | 257 } |
167 | 258 |
168 // static | 259 // static |
169 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetVerbatim( | 260 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetVerbatim( |
170 const v8::Arguments& args) { | 261 const v8::Arguments& args) { |
171 content::RenderView* render_view = GetRenderView(); | 262 content::RenderView* render_view = GetRenderView(); |
172 if (!render_view) return v8::Undefined(); | 263 if (!render_view) return v8::Undefined(); |
173 return v8::Boolean::New(SearchBox::Get(render_view)->verbatim()); | 264 return v8::Boolean::New(SearchBox::Get(render_view)->verbatim()); |
174 } | 265 } |
175 | 266 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 | 306 |
216 // static | 307 // static |
217 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetHeight( | 308 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetHeight( |
218 const v8::Arguments& args) { | 309 const v8::Arguments& args) { |
219 content::RenderView* render_view = GetRenderView(); | 310 content::RenderView* render_view = GetRenderView(); |
220 if (!render_view) return v8::Undefined(); | 311 if (!render_view) return v8::Undefined(); |
221 return v8::Int32::New(SearchBox::Get(render_view)->GetRect().height()); | 312 return v8::Int32::New(SearchBox::Get(render_view)->GetRect().height()); |
222 } | 313 } |
223 | 314 |
224 // static | 315 // static |
| 316 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetAutocompleteResults( |
| 317 const v8::Arguments& args) { |
| 318 content::RenderView* render_view = GetRenderView(); |
| 319 if (!render_view) return v8::Undefined(); |
| 320 const std::vector<InstantAutocompleteResult>& results = |
| 321 SearchBox::Get(render_view)->autocomplete_results(); |
| 322 const int results_base = SearchBox::Get(render_view)->results_base(); |
| 323 v8::Handle<v8::Array> results_array = v8::Array::New(results.size()); |
| 324 for (size_t i = 0; i < results.size(); ++i) { |
| 325 v8::Handle<v8::Object> result = v8::Object::New(); |
| 326 result->Set(v8::String::New("provider"), |
| 327 UTF16ToV8String(results[i].provider)); |
| 328 result->Set(v8::String::New("contents"), |
| 329 UTF16ToV8String(results[i].contents)); |
| 330 result->Set(v8::String::New("destination_url"), |
| 331 v8::String::New(results[i].destination_url.spec().c_str())); |
| 332 result->Set(v8::String::New("rid"), v8::Uint32::New(results_base + i)); |
| 333 |
| 334 v8::Handle<v8::Object> ranking_data = v8::Object::New(); |
| 335 ranking_data->Set(v8::String::New("relevance"), |
| 336 v8::Int32::New(results[i].relevance)); |
| 337 result->Set(v8::String::New("rankingData"), ranking_data); |
| 338 |
| 339 results_array->Set(i, result); |
| 340 } |
| 341 |
| 342 return results_array; |
| 343 } |
| 344 |
| 345 // static |
| 346 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetKeyCode( |
| 347 const v8::Arguments& args) { |
| 348 content::RenderView* render_view = GetRenderView(); |
| 349 if (!render_view) return v8::Undefined(); |
| 350 return v8::Int32::New(SearchBox::Get(render_view)->key_code()); |
| 351 } |
| 352 |
| 353 // static |
225 v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestions( | 354 v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestions( |
226 const v8::Arguments& args) { | 355 const v8::Arguments& args) { |
227 std::vector<string16> suggestions; | 356 std::vector<InstantSuggestion> suggestions; |
228 InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; | |
229 | 357 |
230 if (args.Length() && args[0]->IsObject()) { | 358 if (args.Length() && args[0]->IsObject()) { |
231 v8::Local<v8::Object> suggestion_json = args[0]->ToObject(); | 359 v8::Handle<v8::Object> suggestion_json = args[0]->ToObject(); |
232 | 360 |
233 v8::Local<v8::Value> complete_value = | 361 InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
234 suggestion_json->Get(v8::String::New("complete_behavior")); | 362 InstantSuggestionType type = INSTANT_SUGGESTION_SEARCH; |
| 363 v8::Handle<v8::Value> complete_value = |
| 364 suggestion_json->Get(v8::String::New("complete_behavior")); |
235 if (complete_value->IsString()) { | 365 if (complete_value->IsString()) { |
236 if (complete_value->Equals(v8::String::New("now"))) { | 366 if (complete_value->Equals(v8::String::New("now"))) { |
237 behavior = INSTANT_COMPLETE_NOW; | 367 behavior = INSTANT_COMPLETE_NOW; |
238 } else if (complete_value->Equals(v8::String::New("never"))) { | 368 } else if (complete_value->Equals(v8::String::New("never"))) { |
239 behavior = INSTANT_COMPLETE_NEVER; | 369 behavior = INSTANT_COMPLETE_NEVER; |
240 } else if (complete_value->Equals(v8::String::New("delayed"))) { | 370 } else if (complete_value->Equals(v8::String::New("delayed"))) { |
241 behavior = INSTANT_COMPLETE_DELAYED; | 371 behavior = INSTANT_COMPLETE_DELAYED; |
| 372 } else if (complete_value->Equals(v8::String::New("replace"))) { |
| 373 behavior = INSTANT_COMPLETE_REPLACE; |
242 } else { | 374 } else { |
243 VLOG(1) << "Unsupported complete behavior '" | 375 VLOG(1) << "Unsupported complete behavior '" |
244 << *v8::String::Utf8Value(complete_value) << "'"; | 376 << *v8::String::Utf8Value(complete_value) << "'"; |
245 } | 377 } |
246 } | 378 } |
247 | 379 v8::Handle<v8::Value> suggestions_field = |
248 v8::Local<v8::Value> suggestions_field = | |
249 suggestion_json->Get(v8::String::New("suggestions")); | 380 suggestion_json->Get(v8::String::New("suggestions")); |
250 | |
251 if (suggestions_field->IsArray()) { | 381 if (suggestions_field->IsArray()) { |
252 v8::Local<v8::Array> suggestions_array = | 382 v8::Handle<v8::Array> suggestions_array = |
253 suggestions_field.As<v8::Array>(); | 383 suggestions_field.As<v8::Array>(); |
254 | |
255 size_t length = suggestions_array->Length(); | 384 size_t length = suggestions_array->Length(); |
256 for (size_t i = 0; i < length; i++) { | 385 for (size_t i = 0; i < length; i++) { |
257 v8::Local<v8::Value> suggestion_value = suggestions_array->Get(i); | 386 v8::Handle<v8::Value> suggestion_value = suggestions_array->Get(i); |
258 if (!suggestion_value->IsObject()) continue; | 387 if (!suggestion_value->IsObject()) continue; |
259 | 388 |
260 v8::Local<v8::Object> suggestion_object = suggestion_value->ToObject(); | 389 v8::Handle<v8::Object> suggestion_object = suggestion_value->ToObject(); |
261 v8::Local<v8::Value> suggestion_object_value = | 390 v8::Handle<v8::Value> suggestion_object_value = |
262 suggestion_object->Get(v8::String::New("value")); | 391 suggestion_object->Get(v8::String::New("value")); |
263 if (!suggestion_object_value->IsString()) continue; | 392 if (!suggestion_object_value->IsString()) continue; |
| 393 string16 text = V8ValueToUTF16(suggestion_object_value); |
264 | 394 |
265 string16 suggestion(reinterpret_cast<char16*>(*v8::String::Value( | 395 suggestions.push_back(InstantSuggestion(text, behavior, type)); |
266 suggestion_object_value->ToString()))); | |
267 suggestions.push_back(suggestion); | |
268 } | 396 } |
269 } | 397 } |
270 } | 398 } |
271 | 399 |
272 if (content::RenderView* render_view = GetRenderView()) | 400 if (content::RenderView* render_view = GetRenderView()) |
273 SearchBox::Get(render_view)->SetSuggestions(suggestions, behavior); | 401 SearchBox::Get(render_view)->SetSuggestions(suggestions); |
274 return v8::Undefined(); | 402 return v8::Undefined(); |
275 } | 403 } |
276 | 404 |
| 405 // static |
| 406 v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetQuerySuggestion( |
| 407 const v8::Arguments& args) { |
| 408 if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsString()) { |
| 409 string16 text = V8ValueToUTF16(args[0]); |
| 410 InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| 411 InstantSuggestionType type = INSTANT_SUGGESTION_URL; |
| 412 |
| 413 if (args.Length() >= 2 && args[1]->Uint32Value() == 2) { |
| 414 behavior = INSTANT_COMPLETE_NEVER; |
| 415 // TODO(sreeram): The page should really set the type explicitly. |
| 416 type = INSTANT_SUGGESTION_SEARCH; |
| 417 } |
| 418 |
| 419 if (content::RenderView* render_view = GetRenderView()) { |
| 420 std::vector<InstantSuggestion> suggestions; |
| 421 suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| 422 SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| 423 } |
| 424 } |
| 425 return v8::Undefined(); |
| 426 } |
| 427 |
| 428 // static |
| 429 v8::Handle<v8::Value> |
| 430 SearchBoxExtensionWrapper::SetQuerySuggestionFromAutocompleteResult( |
| 431 const v8::Arguments& args) { |
| 432 content::RenderView* render_view = GetRenderView(); |
| 433 if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsNumber() && |
| 434 render_view) { |
| 435 const int results_id = args[0]->Uint32Value(); |
| 436 const int results_base = SearchBox::Get(render_view)->results_base(); |
| 437 // Note that stale results_ids, less than the current results_base, will |
| 438 // wrap. |
| 439 const size_t index = results_id - results_base; |
| 440 const std::vector<InstantAutocompleteResult>& suggestions = |
| 441 SearchBox::Get(render_view)->autocomplete_results(); |
| 442 if (index < suggestions.size()) { |
| 443 string16 text = UTF8ToUTF16(suggestions[index].destination_url.spec()); |
| 444 InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| 445 InstantSuggestionType type = INSTANT_SUGGESTION_URL; |
| 446 |
| 447 if (args.Length() >= 2 && args[1]->Uint32Value() == 2) |
| 448 behavior = INSTANT_COMPLETE_NEVER; |
| 449 |
| 450 if (suggestions[index].is_search) { |
| 451 text = suggestions[index].contents; |
| 452 type = INSTANT_SUGGESTION_SEARCH; |
| 453 } |
| 454 |
| 455 std::vector<InstantSuggestion> suggestions; |
| 456 suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| 457 SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| 458 } else { |
| 459 VLOG(1) << "Invalid results_id " << results_id << "; " |
| 460 << "results_base is " << results_base << "."; |
| 461 } |
| 462 } |
| 463 return v8::Undefined(); |
| 464 } |
| 465 |
| 466 // static |
| 467 v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetQuery( |
| 468 const v8::Arguments& args) { |
| 469 // TODO(sreeram): Make the second argument (type) mandatory. |
| 470 if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsString()) { |
| 471 string16 text = V8ValueToUTF16(args[0]); |
| 472 InstantCompleteBehavior behavior = INSTANT_COMPLETE_REPLACE; |
| 473 InstantSuggestionType type = INSTANT_SUGGESTION_SEARCH; |
| 474 |
| 475 if (args.Length() >= 2 && args[1]->Uint32Value() == 1) |
| 476 type = INSTANT_SUGGESTION_URL; |
| 477 |
| 478 if (content::RenderView* render_view = GetRenderView()) { |
| 479 std::vector<InstantSuggestion> suggestions; |
| 480 suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| 481 SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| 482 } |
| 483 } |
| 484 return v8::Undefined(); |
| 485 } |
| 486 |
| 487 v8::Handle<v8::Value> |
| 488 SearchBoxExtensionWrapper::SetQueryFromAutocompleteResult( |
| 489 const v8::Arguments& args) { |
| 490 content::RenderView* render_view = GetRenderView(); |
| 491 if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsNumber() && |
| 492 render_view) { |
| 493 const int results_id = args[0]->Uint32Value(); |
| 494 const int results_base = SearchBox::Get(render_view)->results_base(); |
| 495 // Note that stale results_ids, less than the current results_base, will |
| 496 // wrap. |
| 497 const size_t index = results_id - results_base; |
| 498 const std::vector<InstantAutocompleteResult>& suggestions = |
| 499 SearchBox::Get(render_view)->autocomplete_results(); |
| 500 if (index < suggestions.size()) { |
| 501 string16 text = UTF8ToUTF16(suggestions[index].destination_url.spec()); |
| 502 InstantCompleteBehavior behavior = INSTANT_COMPLETE_REPLACE; |
| 503 InstantSuggestionType type = INSTANT_SUGGESTION_URL; |
| 504 |
| 505 if ((args.Length() >= 2 && args[1]->Uint32Value() == 0) || |
| 506 (args.Length() < 2 && suggestions[index].is_search)) { |
| 507 text = suggestions[index].contents; |
| 508 type = INSTANT_SUGGESTION_SEARCH; |
| 509 } |
| 510 |
| 511 std::vector<InstantSuggestion> suggestions; |
| 512 suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| 513 SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| 514 } else { |
| 515 VLOG(1) << "Invalid results_id " << results_id << "; " |
| 516 << "results_base is " << results_base << "."; |
| 517 } |
| 518 } |
| 519 return v8::Undefined(); |
| 520 } |
| 521 |
| 522 // static |
| 523 v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetPreviewHeight( |
| 524 const v8::Arguments& args) { |
| 525 if (args.Length() == 1) { |
| 526 int height = 0; |
| 527 InstantSizeUnits units = INSTANT_SIZE_PIXELS; |
| 528 if (args[0]->IsInt32()) { |
| 529 height = args[0]->Int32Value(); |
| 530 } else if (args[0]->IsString()) { |
| 531 std::string height_str = *v8::String::Utf8Value(args[0]); |
| 532 std::string units_str; |
| 533 SplitLeadingNumberToken(&height_str, &units_str); |
| 534 if (!base::StringToInt(height_str, &height)) |
| 535 return v8::Undefined(); |
| 536 if (units_str == "%") { |
| 537 units = INSTANT_SIZE_PERCENT; |
| 538 } else if (!units_str.empty() && units_str != "px") { |
| 539 return v8::Undefined(); |
| 540 } |
| 541 } else { |
| 542 return v8::Undefined(); |
| 543 } |
| 544 content::RenderView* render_view = GetRenderView(); |
| 545 if (render_view && height >= 0) |
| 546 SearchBox::Get(render_view)->SetInstantPreviewHeight(height, units); |
| 547 } |
| 548 return v8::Undefined(); |
| 549 } |
| 550 |
277 // static | 551 // static |
278 void Dispatch(WebKit::WebFrame* frame, WebKit::WebString script) { | 552 void Dispatch(WebKit::WebFrame* frame, WebKit::WebString script) { |
279 DCHECK(frame) << "Dispatch requires frame"; | 553 DCHECK(frame) << "Dispatch requires frame"; |
280 if (!frame) return; | 554 if (!frame) return; |
281 frame->executeScript(WebKit::WebScriptSource(script)); | 555 frame->executeScript(WebKit::WebScriptSource(script)); |
282 } | 556 } |
283 | 557 |
284 // static | 558 // static |
285 void SearchBoxExtension::DispatchChange(WebKit::WebFrame* frame) { | 559 void SearchBoxExtension::DispatchChange(WebKit::WebFrame* frame) { |
286 Dispatch(frame, kDispatchChangeEventScript); | 560 Dispatch(frame, kDispatchChangeEventScript); |
287 } | 561 } |
288 | 562 |
289 // static | 563 // static |
290 void SearchBoxExtension::DispatchSubmit(WebKit::WebFrame* frame) { | 564 void SearchBoxExtension::DispatchSubmit(WebKit::WebFrame* frame) { |
291 Dispatch(frame, kDispatchSubmitEventScript); | 565 Dispatch(frame, kDispatchSubmitEventScript); |
292 } | 566 } |
293 | 567 |
294 // static | 568 // static |
295 void SearchBoxExtension::DispatchCancel(WebKit::WebFrame* frame) { | 569 void SearchBoxExtension::DispatchCancel(WebKit::WebFrame* frame) { |
296 Dispatch(frame, kDispatchCancelEventScript); | 570 Dispatch(frame, kDispatchCancelEventScript); |
297 } | 571 } |
298 | 572 |
299 // static | 573 // static |
300 void SearchBoxExtension::DispatchResize(WebKit::WebFrame* frame) { | 574 void SearchBoxExtension::DispatchResize(WebKit::WebFrame* frame) { |
301 Dispatch(frame, kDispatchResizeEventScript); | 575 Dispatch(frame, kDispatchResizeEventScript); |
302 } | 576 } |
303 | 577 |
304 // static | 578 // static |
| 579 void SearchBoxExtension::DispatchAutocompleteResults(WebKit::WebFrame* frame) { |
| 580 Dispatch(frame, kDispatchAutocompleteResultsEventScript); |
| 581 } |
| 582 |
| 583 // static |
| 584 void SearchBoxExtension::DispatchKeyPress(WebKit::WebFrame* frame) { |
| 585 Dispatch(frame, kDispatchKeyPressEventScript); |
| 586 } |
| 587 |
| 588 // static |
305 bool SearchBoxExtension::PageSupportsInstant(WebKit::WebFrame* frame) { | 589 bool SearchBoxExtension::PageSupportsInstant(WebKit::WebFrame* frame) { |
306 DCHECK(frame) << "PageSupportsInstant requires frame"; | 590 DCHECK(frame) << "PageSupportsInstant requires frame"; |
307 if (!frame) return false; | 591 if (!frame) return false; |
308 | 592 |
309 v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue( | 593 v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue( |
310 WebKit::WebScriptSource(kSupportsInstantScript)); | 594 WebKit::WebScriptSource(kSupportsInstantScript)); |
311 bool supports_instant = !v.IsEmpty() && v->BooleanValue(); | 595 bool supports_instant = !v.IsEmpty() && v->BooleanValue(); |
312 | 596 |
313 // Send a resize message to tell the page that Chrome is actively using the | 597 // Send a resize message to tell the page that Chrome is actively using the |
314 // searchbox API with it. The page uses the message to transition from | 598 // searchbox API with it. The page uses the message to transition from |
315 // "homepage" mode to "search" mode. | 599 // "homepage" mode to "search" mode. |
316 if (supports_instant) | 600 if (supports_instant) |
317 DispatchResize(frame); | 601 DispatchResize(frame); |
318 | 602 |
319 return supports_instant; | 603 return supports_instant; |
320 } | 604 } |
321 | 605 |
322 // static | 606 // static |
323 v8::Extension* SearchBoxExtension::Get() { | 607 v8::Extension* SearchBoxExtension::Get() { |
324 const base::StringPiece code = | 608 const base::StringPiece code = |
325 ResourceBundle::GetSharedInstance().GetRawDataResource( | 609 ResourceBundle::GetSharedInstance().GetRawDataResource( |
326 IDR_SEARCHBOX_API, ui::SCALE_FACTOR_NONE); | 610 IDR_SEARCHBOX_API, ui::SCALE_FACTOR_NONE); |
327 return new SearchBoxExtensionWrapper(code); | 611 return new SearchBoxExtensionWrapper(code); |
328 } | 612 } |
329 | 613 |
330 } // namespace extensions_v8 | 614 } // namespace extensions_v8 |
OLD | NEW |