Chromium Code Reviews| Index: chrome/renderer/searchbox_extension.cc |
| diff --git a/chrome/renderer/searchbox_extension.cc b/chrome/renderer/searchbox_extension.cc |
| index 29bddcfecf0669d5b96f5d5210f016fbc3dda72c..d15db22e21242ee8eecf7d3a40f3e4530ff7d9fc 100644 |
| --- a/chrome/renderer/searchbox_extension.cc |
| +++ b/chrome/renderer/searchbox_extension.cc |
| @@ -8,15 +8,19 @@ |
| #include <vector> |
| #include "base/basictypes.h" |
| -#include "base/command_line.h" |
| +#include "base/string_number_conversions.h" |
| +#include "base/string_piece.h" |
| #include "base/string_split.h" |
| #include "base/stringprintf.h" |
| +#include "chrome/common/chrome_switches.h" |
| #include "chrome/renderer/searchbox.h" |
| #include "content/public/renderer/render_view.h" |
| #include "grit/renderer_resources.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" |
| #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "v8/include/v8.h" |
| @@ -25,6 +29,22 @@ using WebKit::WebScriptSource; |
| using WebKit::WebString; |
| using WebKit::WebView; |
| +namespace { |
| + |
| +// Splits the string in |number| into two pieces, a leading number token (saved |
| +// in |number|) and the rest (saved in |suffix|). Either piece may become empty, |
| +// depending on whether the input had no digits or only digits. Neither argument |
| +// may be NULL. |
| +void SplitLeadingNumberToken(std::string* number, std::string* suffix) { |
| + size_t i = 0; |
| + while (i < number->size() && isdigit((*number)[i])) |
| + ++i; |
| + suffix->assign(*number, i, number->size() - i); |
| + number->resize(i); |
| +} |
| + |
| +} // namespace |
| + |
| namespace extensions_v8 { |
| static const char kSearchBoxExtensionName[] = "v8/SearchBox"; |
| @@ -65,60 +85,55 @@ static const char kDispatchResizeEventScript[] = |
| " true;" |
| "}"; |
| -// Deprecated API support. |
| -// TODO(tonyg): Remove these when they are no longer used. |
| -// ---------------------------------------------------------------------------- |
| -// Script sent as the user is typing and the provider supports instant. |
| -// Params: |
| -// . the text the user typed. |
| -// '46' forces the server to give us verbatim results. |
| -static const char kUserInputScript[] = |
| - "if (window.chrome.userInput)" |
| - " window.chrome.userInput(" |
| - " window.chrome.searchBox.value," |
| - " window.chrome.searchBox.verbatim ? 46 : 0," |
| - " window.chrome.searchBox.selectionStart);"; |
| - |
| -// Script sent when the page is committed and the provider supports instant. |
| -// Params: |
| -// . the text the user typed. |
| -// . boolean indicating if the user pressed enter to accept the text. |
| -static const char kUserDoneScript[] = |
| - "if (window.chrome.userWantsQuery)" |
| - " window.chrome.userWantsQuery(" |
| - " window.chrome.searchBox.value," |
| - " window.chrome.searchBox.verbatim);"; |
| - |
| -// Script sent when the bounds of the omnibox changes and the provider supports |
| -// instant. The params are the bounds relative to the origin of the preview |
| -// (x, y, width, height). |
| -static const char kSetOmniboxBoundsScript[] = |
| - "if (window.chrome.setDropdownDimensions)" |
| - " window.chrome.setDropdownDimensions(" |
| - " window.chrome.searchBox.x," |
| - " window.chrome.searchBox.y," |
| - " window.chrome.searchBox.width," |
| - " window.chrome.searchBox.height);"; |
| +// Extended API. |
| + |
| +static const char kDispatchNativeSuggestionsEventScript[] = |
| + "if (window.chrome &&" |
| + " window.chrome.searchBox &&" |
| + " window.chrome.searchBox.onnativesuggestions &&" |
| + " typeof window.chrome.searchBox.onnativesuggestions == 'function') {" |
| + " window.chrome.searchBox.onnativesuggestions();" |
| + " true;" |
| + "}"; |
| + |
| +static const char kDispatchKeyPressEventScript[] = |
| + "if (window.chrome &&" |
| + " window.chrome.searchBox &&" |
| + " window.chrome.searchBox.onkeypress &&" |
| + " typeof window.chrome.searchBox.onkeypress == 'function') {" |
| + " window.chrome.searchBox.onkeypress(" |
| + " {keyCode:window.chrome.searchBox.keyCode});" |
| + " true;" |
| + "}"; |
| + |
| +static const char kDispatchFocusEventScript[] = |
| + "if (window.chrome &&" |
| + " window.chrome.searchBox &&" |
| + " window.chrome.searchBox.onfocus &&" |
| + " typeof window.chrome.searchBox.onfocus == 'function') {" |
| + " window.chrome.searchBox.onfocus();" |
| + " true;" |
| + "}"; |
| + |
| +static const char kDispatchBlurEventScript[] = |
| + "if (window.chrome &&" |
| + " window.chrome.searchBox &&" |
| + " window.chrome.searchBox.onblur &&" |
| + " typeof window.chrome.searchBox.onblur == 'function') {" |
| + " window.chrome.searchBox.onblur();" |
| + " true;" |
| + "}"; |
| // We first send this script down to determine if the page supports instant. |
| static const char kSupportsInstantScript[] = |
| - "if (window.chrome.sv) true; else false;"; |
| - |
| -// The google.y.first array is a list of functions which are to be executed |
| -// after the external JavaScript used by Google web search loads. The deprecated |
| -// API requires setDropdownDimensions and userInput to be invoked after |
| -// the external JavaScript loads. So if they are not already registered, we add |
| -// them to the array of functions the page will execute after load. This tight |
| -// coupling discourages proliferation of the deprecated API. |
| -static const char kInitScript[] = |
| - "(function() {" |
| - "var initScript = function(){%s%s};" |
| - "if (window.chrome.setDropdownDimensions)" |
| - " initScript();" |
| - "else if (window.google && window.google.y)" |
| - " window.google.y.first.push(initScript);" |
| - "})();"; |
| -// ---------------------------------------------------------------------------- |
| + "if (window.chrome &&" |
| + " window.chrome.searchBox &&" |
| + " window.chrome.searchBox.onsubmit &&" |
| + " typeof window.chrome.searchBox.onsubmit == 'function') {" |
| + " true;" |
| + "} else {" |
| + " false;" |
| + "}"; |
| class SearchBoxExtensionWrapper : public v8::Extension { |
| public: |
| @@ -160,9 +175,42 @@ class SearchBoxExtensionWrapper : public v8::Extension { |
| // Gets the height of the region of the search box that overlaps the window. |
| static v8::Handle<v8::Value> GetHeight(const v8::Arguments& args); |
| + // Gets the native suggestions from search box. |
| + static v8::Handle<v8::Value> GetNativeSuggestions(const v8::Arguments& args); |
| + |
| + // Gets the last key code entered in search box. |
| + static v8::Handle<v8::Value> GetKeyCode(const v8::Arguments& args); |
| + |
| + // Gets some info about the last search session committed in the search box. |
| + static v8::Handle<v8::Value> GetSessionContext(const v8::Arguments& args); |
| + |
| + // Gets whether the search box currently has keyboard focus or not. |
| + static v8::Handle<v8::Value> GetIsFocused(const v8::Arguments& args); |
| + |
| // Sets ordered suggestions. Valid for current |value|. |
| static v8::Handle<v8::Value> SetSuggestions(const v8::Arguments& args); |
| + // Sets the text to be autocompleted into the search box. |
| + static v8::Handle<v8::Value> SetAutocompleteText(const v8::Arguments& args); |
| + |
| + // Like |SetAutocompleteText| but uses a restricted ID to identify the text. |
| + static v8::Handle<v8::Value> SetRestrictedAutocompleteText( |
| + const v8::Arguments& args); |
| + |
| + // Sets the search box text, completely replacing what the user typed. |
| + static v8::Handle<v8::Value> SetValue(const v8::Arguments& args); |
| + |
| + // Like |SetValue| but uses a restricted ID to identify the text. |
| + static v8::Handle<v8::Value> SetRestrictedValue(const v8::Arguments& args); |
| + |
| + // Resize the preview to the given height. |
| + static v8::Handle<v8::Value> SetNonNativeDropdownHeight( |
| + const v8::Arguments& args); |
| + |
| + // Navigate the window to the URL identified by a restricted ID. |
| + static v8::Handle<v8::Value> NavigateContentWindow(const v8::Arguments& args); |
| + |
| + |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); |
| }; |
| @@ -189,8 +237,28 @@ v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( |
| return v8::FunctionTemplate::New(GetWidth); |
| } else if (name->Equals(v8::String::New("GetHeight"))) { |
| return v8::FunctionTemplate::New(GetHeight); |
| + } else if (name->Equals(v8::String::New("GetNativeSuggestions"))) { |
|
David Black
2012/07/24 00:28:40
Access to these new functions should be guarded be
sreeram
2012/07/24 18:09:22
It's painful to guard them properly since it intro
David Black
2012/07/24 22:34:56
Then that guarding code should be moved out to a c
sreeram
2012/07/24 22:56:27
Agreed. However, this CL doesn't expose the sessio
David Black
2012/07/24 23:05:07
From what I can tell from the GetSessionContext fu
sreeram
2012/07/25 17:35:00
All of the accessors (current_url, last_query, or
|
| + return v8::FunctionTemplate::New(GetNativeSuggestions); |
| + } else if (name->Equals(v8::String::New("GetKeyCode"))) { |
| + return v8::FunctionTemplate::New(GetKeyCode); |
| + } else if (name->Equals(v8::String::New("GetSessionContext"))) { |
| + return v8::FunctionTemplate::New(GetSessionContext); |
| + } else if (name->Equals(v8::String::New("GetIsFocused"))) { |
| + return v8::FunctionTemplate::New(GetIsFocused); |
| } else if (name->Equals(v8::String::New("SetSuggestions"))) { |
| return v8::FunctionTemplate::New(SetSuggestions); |
| + } else if (name->Equals(v8::String::New("SetAutocompleteText"))) { |
| + return v8::FunctionTemplate::New(SetAutocompleteText); |
| + } else if (name->Equals(v8::String::New("SetRestrictedAutocompleteText"))) { |
| + return v8::FunctionTemplate::New(SetRestrictedAutocompleteText); |
| + } else if (name->Equals(v8::String::New("SetValue"))) { |
| + return v8::FunctionTemplate::New(SetValue); |
| + } else if (name->Equals(v8::String::New("SetRestrictedValue"))) { |
| + return v8::FunctionTemplate::New(SetRestrictedValue); |
| + } else if (name->Equals(v8::String::New("SetNonNativeDropdownHeight"))) { |
| + return v8::FunctionTemplate::New(SetNonNativeDropdownHeight); |
| + } else if (name->Equals(v8::String::New("NavigateContentWindow"))) { |
| + return v8::FunctionTemplate::New(NavigateContentWindow); |
| } |
| return v8::Handle<v8::FunctionTemplate>(); |
| } |
| @@ -199,7 +267,7 @@ v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( |
| content::RenderView* SearchBoxExtensionWrapper::GetRenderView() { |
| WebFrame* webframe = WebFrame::frameForEnteredContext(); |
| DCHECK(webframe) << "There should be an active frame since we just got " |
| - "a native function called."; |
| + "a native function called."; |
| if (!webframe) return NULL; |
| WebView* webview = webframe->view(); |
| @@ -216,7 +284,7 @@ v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetValue( |
| return v8::String::New( |
| reinterpret_cast<const uint16_t*>( |
| SearchBox::Get(render_view)->value().c_str()), |
| - SearchBox::Get(render_view)->value().length()); |
| + SearchBox::Get(render_view)->value().length()); |
| } |
| // static |
| @@ -275,112 +343,333 @@ v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetHeight( |
| return v8::Int32::New(SearchBox::Get(render_view)->GetRect().height()); |
| } |
| -// Accepts a single argument in form: |
| -// { |
| -// suggestions: [ |
| -// { |
| -// value: "..." |
| -// } |
| -// ] |
| -// } |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetNativeSuggestions( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (!render_view) return v8::Undefined(); |
| + const std::vector<InstantNativeSuggestionsParts>& suggestions = |
| + SearchBox::Get(render_view)->native_suggestions(); |
| + const int rid_base = SearchBox::Get(render_view)->rid_base(); |
| + v8::Handle<v8::Array> suggestions_array(v8::Array::New(suggestions.size())); |
| + for (size_t i = 0; i < suggestions.size(); ++i) { |
| + v8::Handle<v8::Object> suggestion(v8::Object::New()); |
| + suggestion->Set(v8::String::New("provider"), |
| + v8::String::New(suggestions[i].provider.c_str())); |
| + suggestion->Set(v8::String::New("contents"), |
| + v8::String::New(suggestions[i].contents.c_str())); |
| + suggestion->Set(v8::String::New("destination_url"), |
| + v8::String::New(suggestions[i].destination_url.spec().c_str())); |
| + suggestion->Set(v8::String::New("rid"), v8::Uint32::New(rid_base + i)); |
| + |
| + v8::Handle<v8::Object> ranking_data(v8::Object::New()); |
| + ranking_data->Set(v8::String::New("relevance"), |
| + v8::Int32::New(suggestions[i].relevance)); |
| + suggestion->Set(v8::String::New("rankingData"), ranking_data); |
| + |
| + suggestions_array->Set(i, suggestion); |
| + } |
| + |
| + return suggestions_array; |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetKeyCode( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (!render_view) return v8::Undefined(); |
| + return v8::Int32::New(SearchBox::Get(render_view)->key_code()); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetSessionContext( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (!render_view) return v8::Undefined(); |
| + v8::Handle<v8::Object> session_context(v8::Object::New()); |
| + session_context->Set(v8::String::New("lastSearchQuery"), |
| + v8::String::New(SearchBox::Get(render_view)->last_query().c_str())); |
| + // NOTE: SECURITY RISK -- Providing the current url in this way is insecure. |
| + session_context->Set(v8::String::New("currentUrl"), |
| + v8::String::New(SearchBox::Get(render_view)->current_url().c_str())); |
| + return session_context; |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetIsFocused( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (!render_view) return v8::Undefined(); |
| + return v8::Boolean::New(SearchBox::Get(render_view)->is_focused()); |
| +} |
| + |
| // static |
| v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestions( |
|
David Black
2012/07/24 00:28:40
It would probably be best to make a new ExtendedSe
sreeram
2012/07/24 18:09:22
Same reason as before with respect to the guard. B
|
| const v8::Arguments& args) { |
| - std::vector<std::string> suggestions; |
| - InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| - |
| - if (args.Length() && args[0]->IsArray()) { |
| - // For backwards compatibility, also accept an array of strings. |
| - // TODO(tonyg): Remove this when it is confirmed to be unused. |
| - v8::Local<v8::Array> suggestions_array = |
| - v8::Local<v8::Array>::Cast(args[0]); |
| - uint32_t length = suggestions_array->Length(); |
| - for (uint32_t i = 0; i < length; i++) { |
| - std::string suggestion = *v8::String::Utf8Value( |
| - suggestions_array->Get(v8::Integer::New(i))->ToString()); |
| - if (!suggestion.length()) continue; |
| - suggestions.push_back(suggestion); |
| + std::vector<InstantSuggestion> suggestions; |
| + |
| + if (args.Length() && args[0]->IsObject()) { |
| + v8::Local<v8::Object> suggestion_json(args[0]->ToObject()); |
| + |
| + InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| + InstantSuggestionType type = INSTANT_SUGGESTION_SEARCH; |
| + v8::Local<v8::Value> complete_value( |
| + suggestion_json->Get(v8::String::New("complete_behavior"))); |
| + if (complete_value->IsString()) { |
| + if (complete_value->Equals(v8::String::New("now"))) { |
| + behavior = INSTANT_COMPLETE_NOW; |
| + } else if (complete_value->Equals(v8::String::New("never"))) { |
| + behavior = INSTANT_COMPLETE_NEVER; |
| + } else if (complete_value->Equals(v8::String::New("delayed"))) { |
| + behavior = INSTANT_COMPLETE_DELAYED; |
| + } else if (complete_value->Equals(v8::String::New("replace"))) { |
| + behavior = INSTANT_COMPLETE_REPLACE; |
| + } else { |
| + VLOG(1) << "Unsupported complete behavior '" |
| + << *v8::String::Utf8Value(complete_value) << "'"; |
| + } |
| } |
| - } else if (args.Length() && args[0]->IsObject()) { |
| - // Standard version, object argument. |
| - v8::Local<v8::Object> suggestion_json = |
| - v8::Local<v8::Object>::Cast(args[0]); |
| - v8::Local<v8::Value> suggestions_field = |
| - suggestion_json->Get(v8::String::New("suggestions")); |
| + v8::Local<v8::Value> suggestions_field( |
| + suggestion_json->Get(v8::String::New("suggestions"))); |
| if (suggestions_field->IsArray()) { |
| - v8::Local<v8::Array> suggestions_array = |
| - suggestions_field.As<v8::Array>(); |
| - |
| - uint32_t length = suggestions_array->Length(); |
| - for (uint32_t i = 0; i < length; i++) { |
| - v8::Local<v8::Value> suggestion_value = |
| - suggestions_array->Get(v8::Integer::New(i)); |
| + v8::Local<v8::Array> suggestions_array( |
| + suggestions_field.As<v8::Array>()); |
| + size_t length = suggestions_array->Length(); |
| + for (size_t i = 0; i < length; i++) { |
| + v8::Local<v8::Value> suggestion_value(suggestions_array->Get(i)); |
| if (!suggestion_value->IsObject()) continue; |
| - v8::Local<v8::Object> suggestion_object = |
| - suggestion_value.As<v8::Object>(); |
| - v8::Local<v8::Value> suggestion_object_value = |
| - suggestion_object->Get(v8::String::New("value")); |
| + v8::Local<v8::Object> suggestion_object(suggestion_value->ToObject()); |
| + v8::Local<v8::Value> suggestion_object_value( |
| + suggestion_object->Get(v8::String::New("value"))); |
| if (!suggestion_object_value->IsString()) continue; |
| - std::string suggestion = *v8::String::Utf8Value( |
| - suggestion_object_value->ToString()); |
| - if (!suggestion.length()) continue; |
| - suggestions.push_back(suggestion); |
| + std::string text = *v8::String::Utf8Value(suggestion_object_value); |
| + |
| + suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| } |
| } |
| - if (suggestion_json->Has(v8::String::New("complete_behavior"))) { |
| - v8::Local<v8::Value> complete_value = |
| - suggestion_json->Get(v8::String::New("complete_behavior")); |
| - if (complete_value->IsString()) { |
| - if (complete_value->Equals(v8::String::New("never"))) |
| - behavior = INSTANT_COMPLETE_NEVER; |
| - else if (complete_value->Equals(v8::String::New("delayed"))) |
| - behavior = INSTANT_COMPLETE_DELAYED; |
| + } |
| + |
| + if (content::RenderView* render_view = GetRenderView()) |
| + SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetAutocompleteText( |
| + const v8::Arguments& args) { |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsString()) { |
| + std::string text = *v8::String::Utf8Value(args[0]); |
| + InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| + InstantSuggestionType type = INSTANT_SUGGESTION_URL; |
| + |
| + if (args.Length() >= 2 && args[1]->Uint32Value() == 2) { |
| + behavior = INSTANT_COMPLETE_NEVER; |
| + // TODO(sreeram): The page should really set the type explicitly. |
| + type = INSTANT_SUGGESTION_SEARCH; |
| + } |
| + |
| + if (content::RenderView* render_view = GetRenderView()) { |
| + std::vector<InstantSuggestion> suggestions; |
| + suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| + SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| + } |
| + } |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetRestrictedAutocompleteText( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsNumber() && |
| + render_view) { |
| + const int rid = args[0]->Uint32Value(); |
| + const int rid_base = SearchBox::Get(render_view)->rid_base(); |
| + // Note that stale rids, less than the current rid_base, will wrap. |
| + const size_t index = rid - rid_base; |
| + const std::vector<InstantNativeSuggestionsParts>& suggestions = |
| + SearchBox::Get(render_view)->native_suggestions(); |
| + if (index < suggestions.size()) { |
| + std::string text = suggestions[index].destination_url.spec(); |
| + InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| + InstantSuggestionType type = INSTANT_SUGGESTION_URL; |
| + |
| + if (args.Length() >= 2 && args[1]->Uint32Value() == 2) |
| + behavior = INSTANT_COMPLETE_NEVER; |
| + |
| + if (suggestions[index].is_search) { |
| + text = suggestions[index].contents; |
| + type = INSTANT_SUGGESTION_SEARCH; |
| } |
| + |
| + std::vector<InstantSuggestion> suggestions; |
| + suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| + SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| + } else { |
| + VLOG(1) << "Invalid rid " << rid << "; " |
| + << "rid_base is " << rid_base << "."; |
| } |
| } |
| + return v8::Undefined(); |
| +} |
| - if (content::RenderView* render_view = GetRenderView()) |
| - SearchBox::Get(render_view)->SetSuggestions(suggestions, behavior); |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetValue( |
| + const v8::Arguments& args) { |
| + // TODO(sreeram): Make the second argument (type) mandatory. |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsString()) { |
| + std::string text = *v8::String::Utf8Value(args[0]); |
| + InstantCompleteBehavior behavior = INSTANT_COMPLETE_REPLACE; |
| + InstantSuggestionType type = INSTANT_SUGGESTION_SEARCH; |
| + |
| + if (args.Length() >= 2 && args[1]->Uint32Value() == 1) |
| + type = INSTANT_SUGGESTION_URL; |
| + |
| + if (content::RenderView* render_view = GetRenderView()) { |
| + std::vector<InstantSuggestion> suggestions; |
| + suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| + SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| + } |
| + } |
| return v8::Undefined(); |
| } |
| // static |
| -void Dispatch(WebFrame* frame, |
| - WebString event_dispatch_script, |
| - WebString no_event_handler_script) { |
| - DCHECK(frame) << "Dispatch requires frame"; |
| - if (!frame) |
| - return; |
| - |
| - v8::Handle<v8::Value> result = frame->executeScriptAndReturnValue( |
| - WebScriptSource(event_dispatch_script)); |
| - if (result.IsEmpty() || result->IsUndefined() || result->IsNull() || |
| - result->IsFalse()) { |
| - frame->executeScript(WebScriptSource(no_event_handler_script)); |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetRestrictedValue( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsNumber() && |
| + render_view) { |
| + const int rid = args[0]->Uint32Value(); |
| + const int rid_base = SearchBox::Get(render_view)->rid_base(); |
| + // Note that stale rids, less than the current rid_base, will wrap. |
| + const size_t index = rid - rid_base; |
| + const std::vector<InstantNativeSuggestionsParts>& suggestions = |
| + SearchBox::Get(render_view)->native_suggestions(); |
| + if (index < suggestions.size()) { |
| + std::string text = suggestions[index].destination_url.spec(); |
| + InstantCompleteBehavior behavior = INSTANT_COMPLETE_REPLACE; |
| + InstantSuggestionType type = INSTANT_SUGGESTION_URL; |
| + |
| + if ((args.Length() >= 2 && args[1]->Uint32Value() == 0) || |
| + (args.Length() < 2 && suggestions[index].is_search)) { |
| + text = suggestions[index].contents; |
| + type = INSTANT_SUGGESTION_SEARCH; |
| + } |
| + |
| + std::vector<InstantSuggestion> suggestions; |
| + suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| + SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| + } else { |
| + VLOG(1) << "Invalid rid " << rid << "; " |
| + << "rid_base is " << rid_base << "."; |
| + } |
| } |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetNonNativeDropdownHeight( |
| + const v8::Arguments& args) { |
| + if (args.Length() == 1) { |
| + int height = 0; |
| + InstantSizeUnits units = INSTANT_SIZE_PIXELS; |
| + if (args[0]->IsInt32()) { |
| + height = args[0]->Int32Value(); |
| + } else if (args[0]->IsString()) { |
| + std::string height_str = *v8::String::Utf8Value(args[0]); |
| + std::string units_str; |
| + SplitLeadingNumberToken(&height_str, &units_str); |
| + if (!base::StringToInt(height_str, &height)) |
| + return v8::Undefined(); |
| + if (units_str == "%") { |
| + units = INSTANT_SIZE_PERCENT; |
| + } else if (!units_str.empty() && units_str != "px") { |
| + return v8::Undefined(); |
| + } |
| + } else { |
| + return v8::Undefined(); |
| + } |
| + content::RenderView* render_view = GetRenderView(); |
| + if (render_view && height >= 0) |
| + SearchBox::Get(render_view)->SetInstantPreviewHeight(height, units); |
| + } |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::NavigateContentWindow( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (args.Length() == 1 && args[0]->IsNumber() && render_view) { |
| + const int rid = args[0]->Uint32Value(); |
| + const int rid_base = SearchBox::Get(render_view)->rid_base(); |
| + // Note that stale rids, less than the current rid_base, will wrap. |
| + const size_t index = rid - rid_base; |
| + const std::vector<InstantNativeSuggestionsParts>& suggestions = |
| + SearchBox::Get(render_view)->native_suggestions(); |
| + if (index < suggestions.size()) { |
| + // Navigate directly to the selected URL. |
| + // TODO(sreeram): SECURITY RISK! Disconnect the InstantLoader and call |
| + // SearchBox::Reset to not leak sensitive data to the new page. |
| + WebKit::WebURLRequest request(suggestions[index].destination_url); |
| + render_view->GetWebView()->mainFrame()->loadRequest(request); |
| + } else { |
| + VLOG(1) << "Invalid rid " << rid << "; " |
| + << "rid_base is " << rid_base << "."; |
| + } |
| + } |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +void Dispatch(WebFrame* frame, WebString event_dispatch_script) { |
| + DCHECK(frame) << "Dispatch requires frame"; |
| + if (!frame) return; |
| + frame->executeScript(WebScriptSource(event_dispatch_script)); |
| } |
| // static |
| void SearchBoxExtension::DispatchChange(WebFrame* frame) { |
| - Dispatch(frame, kDispatchChangeEventScript, kUserInputScript); |
| + Dispatch(frame, kDispatchChangeEventScript); |
| } |
| // static |
| void SearchBoxExtension::DispatchSubmit(WebFrame* frame) { |
| - Dispatch(frame, kDispatchSubmitEventScript, kUserDoneScript); |
| + Dispatch(frame, kDispatchSubmitEventScript); |
| } |
| // static |
| void SearchBoxExtension::DispatchCancel(WebFrame* frame) { |
| - Dispatch(frame, kDispatchCancelEventScript, kUserDoneScript); |
| + Dispatch(frame, kDispatchCancelEventScript); |
| } |
| // static |
| void SearchBoxExtension::DispatchResize(WebFrame* frame) { |
| - Dispatch(frame, kDispatchResizeEventScript, kSetOmniboxBoundsScript); |
| + Dispatch(frame, kDispatchResizeEventScript); |
| +} |
| + |
| +// static |
| +void SearchBoxExtension::DispatchNativeSuggestions(WebFrame* frame) { |
| + Dispatch(frame, kDispatchNativeSuggestionsEventScript); |
| +} |
| + |
| +// static |
| +void SearchBoxExtension::DispatchKeyPress(WebFrame* frame) { |
| + Dispatch(frame, kDispatchKeyPressEventScript); |
| +} |
| + |
| +// static |
| +void SearchBoxExtension::DispatchFocus(WebFrame* frame) { |
| + Dispatch(frame, kDispatchFocusEventScript); |
| +} |
| + |
| +// static |
| +void SearchBoxExtension::DispatchBlur(WebFrame* frame) { |
| + Dispatch(frame, kDispatchBlurEventScript); |
| } |
| // static |
| @@ -390,20 +679,14 @@ bool SearchBoxExtension::PageSupportsInstant(WebFrame* frame) { |
| v8::Handle<v8::Value> v = frame->executeScriptAndReturnValue( |
| WebScriptSource(kSupportsInstantScript)); |
| - bool supports_deprecated_api = !v.IsEmpty() && v->BooleanValue(); |
| - // TODO(tonyg): Add way of detecting instant support to SearchBox API. |
| - bool supports_searchbox_api = supports_deprecated_api; |
| - |
| - // The deprecated API needs to notify the page of events it may have missed. |
| - // This isn't necessary in the SearchBox API, since the page can query the |
| - // API at any time. |
| - CR_DEFINE_STATIC_LOCAL(std::string, init_script, |
| - (StringPrintf(kInitScript, kSetOmniboxBoundsScript, kUserInputScript))); |
| - if (supports_deprecated_api) { |
| - frame->executeScript(WebScriptSource(WebString::fromUTF8(init_script))); |
| + if (!v.IsEmpty() && v->BooleanValue()) { |
| + // Send a resize message to tell the page that Chrome is actively using |
| + // the searchbox API with it. The page uses the message to transition from |
| + // "homepage" mode to "search" mode. |
| + DispatchResize(frame); |
| + return true; |
| } |
| - |
| - return supports_searchbox_api || supports_deprecated_api; |
| + return false; |
| } |
| // static |