OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/renderer/searchbox_extension.h" | |
6 | |
7 #include "chrome/renderer/searchbox.h" | |
8 #include "content/public/renderer/render_view.h" | |
9 #include "grit/renderer_resources.h" | |
10 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" | |
12 #include "ui/base/resource/resource_bundle.h" | |
13 #include "v8/include/v8.h" | |
14 | |
15 namespace extensions_v8 { | |
16 | |
17 static const char kSearchBoxExtensionName[] = "v8/SearchBox"; | |
18 | |
19 static const char kDispatchChangeEventScript[] = | |
20 "if (window.chrome &&" | |
21 " window.chrome.searchBox &&" | |
22 " window.chrome.searchBox.onchange &&" | |
23 " typeof window.chrome.searchBox.onchange == 'function') {" | |
24 " window.chrome.searchBox.onchange();" | |
25 " true;" | |
26 "}"; | |
27 | |
28 static const char kDispatchSubmitEventScript[] = | |
29 "if (window.chrome &&" | |
30 " window.chrome.searchBox &&" | |
31 " window.chrome.searchBox.onsubmit &&" | |
32 " typeof window.chrome.searchBox.onsubmit == 'function') {" | |
33 " window.chrome.searchBox.onsubmit();" | |
34 " true;" | |
35 "}"; | |
36 | |
37 static const char kDispatchCancelEventScript[] = | |
38 "if (window.chrome &&" | |
39 " window.chrome.searchBox &&" | |
40 " window.chrome.searchBox.oncancel &&" | |
41 " typeof window.chrome.searchBox.oncancel == 'function') {" | |
42 " window.chrome.searchBox.oncancel();" | |
43 " true;" | |
44 "}"; | |
45 | |
46 static const char kDispatchResizeEventScript[] = | |
47 "if (window.chrome &&" | |
48 " window.chrome.searchBox &&" | |
49 " window.chrome.searchBox.onresize &&" | |
50 " typeof window.chrome.searchBox.onresize == 'function') {" | |
51 " window.chrome.searchBox.onresize();" | |
52 " true;" | |
53 "}"; | |
54 | |
55 // We first send this script down to determine if the page supports instant. | |
56 static const char kSupportsInstantScript[] = | |
57 "if (window.chrome &&" | |
58 " window.chrome.searchBox &&" | |
59 " window.chrome.searchBox.onsubmit &&" | |
60 " typeof window.chrome.searchBox.onsubmit == 'function') {" | |
61 " true;" | |
62 "} else {" | |
63 " false;" | |
64 "}"; | |
65 | |
66 // ---------------------------------------------------------------------------- | |
67 | |
68 class SearchBoxExtensionWrapper : public v8::Extension { | |
69 public: | |
70 explicit SearchBoxExtensionWrapper(const base::StringPiece& code); | |
71 | |
72 // Allows v8's javascript code to call the native functions defined | |
73 // in this class for window.chrome. | |
74 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | |
75 v8::Handle<v8::String> name); | |
76 | |
77 // Helper function to find the RenderView. May return NULL. | |
78 static content::RenderView* GetRenderView(); | |
79 | |
80 // Gets the value of the user's search query. | |
81 static v8::Handle<v8::Value> GetValue(const v8::Arguments& args); | |
82 | |
83 // 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 | |
85 // forward delete, or in other cases where Chrome overrides. | |
86 static v8::Handle<v8::Value> GetVerbatim(const v8::Arguments& args); | |
87 | |
88 // Gets the start of the selection in the search box. | |
89 static v8::Handle<v8::Value> GetSelectionStart(const v8::Arguments& args); | |
90 | |
91 // Gets the end of the selection in the search box. | |
92 static v8::Handle<v8::Value> GetSelectionEnd(const v8::Arguments& args); | |
93 | |
94 // Gets the x coordinate (relative to |window|) of the left edge of the | |
95 // region of the search box that overlaps the window. | |
96 static v8::Handle<v8::Value> GetX(const v8::Arguments& args); | |
97 | |
98 // Gets the y coordinate (relative to |window|) of the right edge of the | |
99 // region of the search box that overlaps the window. | |
100 static v8::Handle<v8::Value> GetY(const v8::Arguments& args); | |
101 | |
102 // 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); | |
104 | |
105 // 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); | |
107 | |
108 // Sets ordered suggestions. Valid for current |value|. | |
109 static v8::Handle<v8::Value> SetSuggestions(const v8::Arguments& args); | |
110 | |
111 private: | |
112 DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); | |
113 }; | |
114 | |
115 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper( | |
116 const base::StringPiece& code) | |
117 : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) { | |
118 } | |
119 | |
120 v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( | |
121 v8::Handle<v8::String> name) { | |
122 if (name->Equals(v8::String::New("GetValue"))) { | |
123 return v8::FunctionTemplate::New(GetValue); | |
124 } else if (name->Equals(v8::String::New("GetVerbatim"))) { | |
125 return v8::FunctionTemplate::New(GetVerbatim); | |
126 } else if (name->Equals(v8::String::New("GetSelectionStart"))) { | |
127 return v8::FunctionTemplate::New(GetSelectionStart); | |
128 } else if (name->Equals(v8::String::New("GetSelectionEnd"))) { | |
129 return v8::FunctionTemplate::New(GetSelectionEnd); | |
130 } else if (name->Equals(v8::String::New("GetX"))) { | |
131 return v8::FunctionTemplate::New(GetX); | |
132 } else if (name->Equals(v8::String::New("GetY"))) { | |
133 return v8::FunctionTemplate::New(GetY); | |
134 } else if (name->Equals(v8::String::New("GetWidth"))) { | |
135 return v8::FunctionTemplate::New(GetWidth); | |
136 } else if (name->Equals(v8::String::New("GetHeight"))) { | |
137 return v8::FunctionTemplate::New(GetHeight); | |
138 } else if (name->Equals(v8::String::New("SetSuggestions"))) { | |
139 return v8::FunctionTemplate::New(SetSuggestions); | |
140 } | |
141 return v8::Handle<v8::FunctionTemplate>(); | |
142 } | |
143 | |
144 // static | |
145 content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { | |
146 WebKit::WebFrame* webframe = WebKit::WebFrame::frameForEnteredContext(); | |
147 DCHECK(webframe) << "There should be an active frame since we just got " | |
148 "a native function called."; | |
149 if (!webframe) return NULL; | |
150 | |
151 WebKit::WebView* webview = webframe->view(); | |
152 if (!webview) return NULL; // can happen during closing | |
153 | |
154 return content::RenderView::FromWebView(webview); | |
155 } | |
156 | |
157 // static | |
158 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetValue( | |
159 const v8::Arguments& args) { | |
160 content::RenderView* render_view = GetRenderView(); | |
161 if (!render_view) return v8::Undefined(); | |
162 return v8::String::New( | |
163 reinterpret_cast<const uint16_t*>( | |
164 SearchBox::Get(render_view)->value().data()), | |
165 SearchBox::Get(render_view)->value().length()); | |
166 } | |
167 | |
168 // static | |
169 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetVerbatim( | |
170 const v8::Arguments& args) { | |
171 content::RenderView* render_view = GetRenderView(); | |
172 if (!render_view) return v8::Undefined(); | |
173 return v8::Boolean::New(SearchBox::Get(render_view)->verbatim()); | |
174 } | |
175 | |
176 // static | |
177 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetSelectionStart( | |
178 const v8::Arguments& args) { | |
179 content::RenderView* render_view = GetRenderView(); | |
180 if (!render_view) return v8::Undefined(); | |
181 return v8::Uint32::New(SearchBox::Get(render_view)->selection_start()); | |
182 } | |
183 | |
184 // static | |
185 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetSelectionEnd( | |
186 const v8::Arguments& args) { | |
187 content::RenderView* render_view = GetRenderView(); | |
188 if (!render_view) return v8::Undefined(); | |
189 return v8::Uint32::New(SearchBox::Get(render_view)->selection_end()); | |
190 } | |
191 | |
192 // static | |
193 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetX( | |
194 const v8::Arguments& args) { | |
195 content::RenderView* render_view = GetRenderView(); | |
196 if (!render_view) return v8::Undefined(); | |
197 return v8::Int32::New(SearchBox::Get(render_view)->GetRect().x()); | |
198 } | |
199 | |
200 // static | |
201 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetY( | |
202 const v8::Arguments& args) { | |
203 content::RenderView* render_view = GetRenderView(); | |
204 if (!render_view) return v8::Undefined(); | |
205 return v8::Int32::New(SearchBox::Get(render_view)->GetRect().y()); | |
206 } | |
207 | |
208 // static | |
209 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetWidth( | |
210 const v8::Arguments& args) { | |
211 content::RenderView* render_view = GetRenderView(); | |
212 if (!render_view) return v8::Undefined(); | |
213 return v8::Int32::New(SearchBox::Get(render_view)->GetRect().width()); | |
214 } | |
215 | |
216 // static | |
217 v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetHeight( | |
218 const v8::Arguments& args) { | |
219 content::RenderView* render_view = GetRenderView(); | |
220 if (!render_view) return v8::Undefined(); | |
221 return v8::Int32::New(SearchBox::Get(render_view)->GetRect().height()); | |
222 } | |
223 | |
224 // static | |
225 v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestions( | |
226 const v8::Arguments& args) { | |
227 std::vector<string16> suggestions; | |
228 InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; | |
229 | |
230 if (args.Length() && args[0]->IsObject()) { | |
231 v8::Local<v8::Object> suggestion_json = args[0]->ToObject(); | |
232 | |
233 v8::Local<v8::Value> complete_value = | |
234 suggestion_json->Get(v8::String::New("complete_behavior")); | |
235 if (complete_value->IsString()) { | |
236 if (complete_value->Equals(v8::String::New("now"))) { | |
237 behavior = INSTANT_COMPLETE_NOW; | |
238 } else if (complete_value->Equals(v8::String::New("never"))) { | |
239 behavior = INSTANT_COMPLETE_NEVER; | |
240 } else if (complete_value->Equals(v8::String::New("delayed"))) { | |
241 behavior = INSTANT_COMPLETE_DELAYED; | |
242 } else { | |
243 VLOG(1) << "Unsupported complete behavior '" | |
244 << *v8::String::Utf8Value(complete_value) << "'"; | |
245 } | |
246 } | |
247 | |
248 v8::Local<v8::Value> suggestions_field = | |
249 suggestion_json->Get(v8::String::New("suggestions")); | |
250 | |
251 if (suggestions_field->IsArray()) { | |
252 v8::Local<v8::Array> suggestions_array = | |
253 suggestions_field.As<v8::Array>(); | |
254 | |
255 size_t length = suggestions_array->Length(); | |
256 for (size_t i = 0; i < length; i++) { | |
257 v8::Local<v8::Value> suggestion_value = suggestions_array->Get(i); | |
258 if (!suggestion_value->IsObject()) continue; | |
259 | |
260 v8::Local<v8::Object> suggestion_object = suggestion_value->ToObject(); | |
261 v8::Local<v8::Value> suggestion_object_value = | |
262 suggestion_object->Get(v8::String::New("value")); | |
263 if (!suggestion_object_value->IsString()) continue; | |
264 | |
265 string16 suggestion(reinterpret_cast<char16*>(*v8::String::Value( | |
266 suggestion_object_value->ToString()))); | |
267 suggestions.push_back(suggestion); | |
268 } | |
269 } | |
270 } | |
271 | |
272 if (content::RenderView* render_view = GetRenderView()) | |
273 SearchBox::Get(render_view)->SetSuggestions(suggestions, behavior); | |
274 return v8::Undefined(); | |
275 } | |
276 | |
277 // static | |
278 void Dispatch(WebKit::WebFrame* frame, WebKit::WebString script) { | |
279 DCHECK(frame) << "Dispatch requires frame"; | |
280 if (!frame) return; | |
281 frame->executeScript(WebKit::WebScriptSource(script)); | |
282 } | |
283 | |
284 // static | |
285 void SearchBoxExtension::DispatchChange(WebKit::WebFrame* frame) { | |
286 Dispatch(frame, kDispatchChangeEventScript); | |
287 } | |
288 | |
289 // static | |
290 void SearchBoxExtension::DispatchSubmit(WebKit::WebFrame* frame) { | |
291 Dispatch(frame, kDispatchSubmitEventScript); | |
292 } | |
293 | |
294 // static | |
295 void SearchBoxExtension::DispatchCancel(WebKit::WebFrame* frame) { | |
296 Dispatch(frame, kDispatchCancelEventScript); | |
297 } | |
298 | |
299 // static | |
300 void SearchBoxExtension::DispatchResize(WebKit::WebFrame* frame) { | |
301 Dispatch(frame, kDispatchResizeEventScript); | |
302 } | |
303 | |
304 // static | |
305 bool SearchBoxExtension::PageSupportsInstant(WebKit::WebFrame* frame) { | |
306 DCHECK(frame) << "PageSupportsInstant requires frame"; | |
307 if (!frame) return false; | |
308 | |
309 v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue( | |
310 WebKit::WebScriptSource(kSupportsInstantScript)); | |
311 bool supports_instant = !v.IsEmpty() && v->BooleanValue(); | |
312 | |
313 // 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 | |
315 // "homepage" mode to "search" mode. | |
316 if (supports_instant) | |
317 DispatchResize(frame); | |
318 | |
319 return supports_instant; | |
320 } | |
321 | |
322 // static | |
323 v8::Extension* SearchBoxExtension::Get() { | |
324 const base::StringPiece code = | |
325 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
326 IDR_SEARCHBOX_API, ui::SCALE_FACTOR_NONE); | |
327 return new SearchBoxExtensionWrapper(code); | |
328 } | |
329 | |
330 } // namespace extensions_v8 | |
OLD | NEW |