Chromium Code Reviews| Index: chrome/renderer/searchbox/searchbox_extension.cc |
| diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc |
| index ab3ad2b3db99004b7f3af1376ac2510f6ec50124..a92957a8961dcf9043aca41fe718a26240227011 100644 |
| --- a/chrome/renderer/searchbox/searchbox_extension.cc |
| +++ b/chrome/renderer/searchbox/searchbox_extension.cc |
| @@ -4,14 +4,38 @@ |
| #include "chrome/renderer/searchbox/searchbox_extension.h" |
| +#include <ctype.h> |
| +#include <vector> |
| + |
| +#include "base/string_number_conversions.h" |
| +#include "base/string_piece.h" |
| +#include "base/utf_string_conversions.h" |
| #include "chrome/renderer/searchbox/searchbox.h" |
| #include "content/public/renderer/render_view.h" |
| #include "grit/renderer_resources.h" |
| +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h" |
|
sreeram
2012/08/13 16:37:08
We don't need this header.
Shishir
2012/08/13 18:11:12
Done.
|
| #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 "ui/base/resource/resource_bundle.h" |
| #include "v8/include/v8.h" |
| +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"; |
| @@ -63,6 +87,26 @@ static const char kSupportsInstantScript[] = |
| " false;" |
| "}"; |
| +// Extended API. |
| +static const char kDispatchAutocompleteResultsEventScript[] = |
| + "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;" |
| + "}"; |
| + |
| // ---------------------------------------------------------------------------- |
| class SearchBoxExtensionWrapper : public v8::Extension { |
| @@ -105,9 +149,34 @@ 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 autocomplete results from search box. |
| + static v8::Handle<v8::Value> GetAutocompleteResults( |
| + const v8::Arguments& args); |
| + |
| + // Gets the last key code entered in search box. |
| + static v8::Handle<v8::Value> GetKeyCode(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> SetSuggestionInline(const v8::Arguments& args); |
| + |
| + // Like |SetSuggestionInline| but uses a restricted ID to identify the text. |
| + static v8::Handle<v8::Value> SetAutocompleteResultInline( |
| + const v8::Arguments& args); |
| + |
| + // Sets the search box text, completely replacing what the user typed. |
| + static v8::Handle<v8::Value> SetSuggestionReplace(const v8::Arguments& args); |
| + |
| + // Like |SetSuggestionReplace| but uses a restricted ID to identify the text. |
| + static v8::Handle<v8::Value> SetAutocompleteResultReplace( |
| + const v8::Arguments& args); |
| + |
| + // Resize the preview to the given height. |
| + static v8::Handle<v8::Value> SetNonNativeDropdownHeight( |
| + const v8::Arguments& args); |
| + |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); |
| }; |
| @@ -135,8 +204,22 @@ 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("GetAutocompleteResults"))) { |
| + return v8::FunctionTemplate::New(GetAutocompleteResults); |
| + } else if (name->Equals(v8::String::New("GetKeyCode"))) { |
| + return v8::FunctionTemplate::New(GetKeyCode); |
| } else if (name->Equals(v8::String::New("SetSuggestions"))) { |
| return v8::FunctionTemplate::New(SetSuggestions); |
| + } else if (name->Equals(v8::String::New("SetSuggestionInline"))) { |
| + return v8::FunctionTemplate::New(SetSuggestionInline); |
| + } else if (name->Equals(v8::String::New("SetAutocompleteResultInline"))) { |
| + return v8::FunctionTemplate::New(SetAutocompleteResultInline); |
| + } else if (name->Equals(v8::String::New("SetSuggestionReplace"))) { |
| + return v8::FunctionTemplate::New(SetSuggestionReplace); |
| + } else if (name->Equals(v8::String::New("SetAutocompleteResultReplace"))) { |
| + return v8::FunctionTemplate::New(SetAutocompleteResultReplace); |
| + } else if (name->Equals(v8::String::New("SetNonNativeDropdownHeight"))) { |
| + return v8::FunctionTemplate::New(SetNonNativeDropdownHeight); |
| } |
| return v8::Handle<v8::FunctionTemplate>(); |
| } |
| @@ -222,16 +305,55 @@ v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetHeight( |
| } |
| // static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetAutocompleteResults( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (!render_view) return v8::Undefined(); |
| + const std::vector<InstantAutocompleteResult>& suggestions = |
| + SearchBox::Get(render_view)->autocomplete_results(); |
|
sreeram
2012/08/13 16:37:08
suggestions -> results, throughout this method.
Shishir
2012/08/13 18:11:12
Done.
|
| + const int results_base = SearchBox::Get(render_view)->results_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())); |
|
sreeram
2012/08/13 16:37:08
suggestions[i].provider/contents are string16s. .c
Shishir
2012/08/13 18:11:12
Done.
|
| + 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(results_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::SetSuggestions( |
| const v8::Arguments& args) { |
| - std::vector<string16> suggestions; |
| - InstantCompleteBehavior behavior = INSTANT_COMPLETE_NOW; |
| + std::vector<InstantSuggestion> suggestions; |
| if (args.Length() && args[0]->IsObject()) { |
| - v8::Local<v8::Object> suggestion_json = args[0]->ToObject(); |
| + v8::Local<v8::Object> suggestion_json(args[0]->ToObject()); |
| - v8::Local<v8::Value> complete_value = |
| - suggestion_json->Get(v8::String::New("complete_behavior")); |
| + 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; |
| @@ -239,38 +361,180 @@ v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestions( |
| 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) << "'"; |
| } |
| } |
| - |
| - 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>(); |
| - |
| + 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); |
| + v8::Local<v8::Value> suggestion_value(suggestions_array->Get(i)); |
| if (!suggestion_value->IsObject()) continue; |
| - v8::Local<v8::Object> suggestion_object = suggestion_value->ToObject(); |
| - 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"))); |
|
sreeram
2012/08/13 16:37:08
Why replace all the "type foo = bar;" statements w
Shishir
2012/08/13 18:11:12
Done.
|
| if (!suggestion_object_value->IsString()) continue; |
| + string16 text = *v8::String::Value(suggestion_object_value); |
| - string16 suggestion(reinterpret_cast<char16*>(*v8::String::Value( |
| - suggestion_object_value->ToString()))); |
| - suggestions.push_back(suggestion); |
| + suggestions.push_back(InstantSuggestion(text, behavior, type)); |
| } |
| } |
| } |
| if (content::RenderView* render_view = GetRenderView()) |
| - SearchBox::Get(render_view)->SetSuggestions(suggestions, behavior); |
| + SearchBox::Get(render_view)->SetSuggestions(suggestions); |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestionInline( |
| + const v8::Arguments& args) { |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsString()) { |
| + string16 text = *v8::String::Value(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::SetAutocompleteResultInline( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsNumber() && |
| + render_view) { |
| + const int results_id = args[0]->Uint32Value(); |
| + const int results_base = SearchBox::Get(render_view)->results_base(); |
| + // Note that stale results_ids, less than the current results_base, will |
| + // wrap. |
| + const size_t index = results_id - results_base; |
| + const std::vector<InstantAutocompleteResult>& suggestions = |
| + SearchBox::Get(render_view)->autocomplete_results(); |
| + if (index < suggestions.size()) { |
| + string16 text = UTF8ToUTF16(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 results_id " << results_id << "; " |
| + << "results_base is " << results_base << "."; |
| + } |
| + } |
| + return v8::Undefined(); |
| +} |
| + |
| +// static |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetSuggestionReplace( |
| + const v8::Arguments& args) { |
| + // TODO(sreeram): Make the second argument (type) mandatory. |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsString()) { |
| + string16 text = *v8::String::Value(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(); |
| +} |
| + |
| +v8::Handle<v8::Value> SearchBoxExtensionWrapper::SetAutocompleteResultReplace( |
| + const v8::Arguments& args) { |
| + content::RenderView* render_view = GetRenderView(); |
| + if (1 <= args.Length() && args.Length() <= 2 && args[0]->IsNumber() && |
| + render_view) { |
| + const int results_id = args[0]->Uint32Value(); |
| + const int results_base = SearchBox::Get(render_view)->results_base(); |
| + // Note that stale results_ids, less than the current results_base, will |
| + // wrap. |
| + const size_t index = results_id - results_base; |
| + const std::vector<InstantAutocompleteResult>& suggestions = |
| + SearchBox::Get(render_view)->autocomplete_results(); |
| + if (index < suggestions.size()) { |
| + string16 text = UTF8ToUTF16(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 results_id " << results_id << "; " |
| + << "results_base is " << results_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(); |
| } |
| @@ -302,6 +566,16 @@ void SearchBoxExtension::DispatchResize(WebKit::WebFrame* frame) { |
| } |
| // static |
| +void SearchBoxExtension::DispatchAutocompleteResults(WebKit::WebFrame* frame) { |
| + Dispatch(frame, kDispatchAutocompleteResultsEventScript); |
| +} |
| + |
| +// static |
| +void SearchBoxExtension::DispatchKeyPress(WebKit::WebFrame* frame) { |
| + Dispatch(frame, kDispatchKeyPressEventScript); |
| +} |
| + |
| +// static |
| bool SearchBoxExtension::PageSupportsInstant(WebKit::WebFrame* frame) { |
| DCHECK(frame) << "PageSupportsInstant requires frame"; |
| if (!frame) return false; |