blob: 7fb049bdd956cf63072ce6f5d6070a6acd7e6959 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/renderer/searchbox/searchbox_extension.h"
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/i18n/rtl.h"
#include "base/json/string_escape.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/search/instant_types.h"
#include "chrome/common/search/ntp_logging_events.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/renderer_resources.h"
#include "chrome/renderer/searchbox/searchbox.h"
#include "components/crx_file/id_util.h"
#include "components/ntp_tiles/tile_source.h"
#include "components/ntp_tiles/tile_visual_type.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/window_open_disposition.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "url/gurl.h"
#include "url/url_constants.h"
#include "v8/include/v8.h"
namespace {
const char kCSSBackgroundImageFormat[] = "-webkit-image-set("
"url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND?%s) 1x, "
"url(chrome-search://theme/IDR_THEME_NTP_BACKGROUND@2x?%s) 2x)";
const char kCSSBackgroundColorFormat[] = "rgba(%d,%d,%d,%s)";
const char kCSSBackgroundPositionCenter[] = "center";
const char kCSSBackgroundPositionLeft[] = "left";
const char kCSSBackgroundPositionTop[] = "top";
const char kCSSBackgroundPositionRight[] = "right";
const char kCSSBackgroundPositionBottom[] = "bottom";
const char kCSSBackgroundRepeatNo[] = "no-repeat";
const char kCSSBackgroundRepeatX[] = "repeat-x";
const char kCSSBackgroundRepeatY[] = "repeat-y";
const char kCSSBackgroundRepeat[] = "repeat";
const char kThemeAttributionFormat[] = "-webkit-image-set("
"url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION?%s) 1x, "
"url(chrome-search://theme/IDR_THEME_NTP_ATTRIBUTION@2x?%s) 2x)";
const char kLTRHtmlTextDirection[] = "ltr";
const char kRTLHtmlTextDirection[] = "rtl";
// Converts a V8 value to a string16.
base::string16 V8ValueToUTF16(v8::Local<v8::Value> v) {
v8::String::Value s(v);
return base::string16(reinterpret_cast<const base::char16*>(*s), s.length());
}
// Converts string16 to V8 String.
v8::Local<v8::String> UTF16ToV8String(v8::Isolate* isolate,
const base::string16& s) {
return v8::String::NewFromTwoByte(isolate,
reinterpret_cast<const uint16_t*>(s.data()),
v8::String::kNormalString,
s.size());
}
// Converts std::string to V8 String.
v8::Local<v8::String> UTF8ToV8String(v8::Isolate* isolate,
const std::string& s) {
return v8::String::NewFromUtf8(
isolate, s.data(), v8::String::kNormalString, s.size());
}
// Throws a TypeError on the current V8 context if the args are invalid.
void ThrowInvalidParameters(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
isolate->ThrowException(v8::Exception::TypeError(
v8::String::NewFromUtf8(isolate, "Invalid parameters")));
}
void Dispatch(blink::WebFrame* frame, const blink::WebString& script) {
if (!frame) return;
frame->ExecuteScript(blink::WebScriptSource(script));
}
v8::Local<v8::String> GenerateThumbnailURL(
v8::Isolate* isolate,
int render_view_id,
InstantRestrictedID most_visited_item_id) {
return UTF8ToV8String(
isolate,
base::StringPrintf(
"chrome-search://thumb/%d/%d", render_view_id, most_visited_item_id));
}
v8::Local<v8::String> GenerateThumb2URL(v8::Isolate* isolate,
const GURL& page_url,
const GURL& fallback_thumb_url) {
return UTF8ToV8String(isolate,
base::StringPrintf("chrome-search://thumb2/%s?fb=%s",
page_url.spec().c_str(),
fallback_thumb_url.spec().c_str()));
}
// Populates a Javascript MostVisitedItem object from |mv_item|.
// NOTE: Includes "url", "title" and "domain" which are private data, so should
// not be returned to the Instant page. These should be erased before returning
// the object. See GetMostVisitedItemsWrapper() in searchbox_api.js.
v8::Local<v8::Object> GenerateMostVisitedItem(
v8::Isolate* isolate,
int render_view_id,
InstantRestrictedID restricted_id,
const InstantMostVisitedItem& mv_item) {
// We set the "dir" attribute of the title, so that in RTL locales, a LTR
// title is rendered left-to-right and truncated from the right. For
// example, the title of http://msdn.microsoft.com/en-us/default.aspx is
// "MSDN: Microsoft developer network". In RTL locales, in the New Tab
// page, if the "dir" of this title is not specified, it takes Chrome UI's
// directionality. So the title will be truncated as "soft developer
// network". Setting the "dir" attribute as "ltr" renders the truncated
// title as "MSDN: Microsoft D...". As another example, the title of
// http://yahoo.com is "Yahoo!". In RTL locales, in the New Tab page, the
// title will be rendered as "!Yahoo" if its "dir" attribute is not set to
// "ltr".
std::string direction;
if (base::i18n::StringContainsStrongRTLChars(mv_item.title))
direction = kRTLHtmlTextDirection;
else
direction = kLTRHtmlTextDirection;
base::string16 title = mv_item.title;
if (title.empty())
title = base::UTF8ToUTF16(mv_item.url.spec());
v8::Local<v8::Object> obj = v8::Object::New(isolate);
obj->Set(v8::String::NewFromUtf8(isolate, "renderViewId"),
v8::Int32::New(isolate, render_view_id));
obj->Set(v8::String::NewFromUtf8(isolate, "rid"),
v8::Int32::New(isolate, restricted_id));
// If the suggestion already has a suggested thumbnail, we create a thumbnail
// URL with both the local thumbnail and the proposed one as a fallback.
// Otherwise, we just pass on the generated one.
obj->Set(v8::String::NewFromUtf8(isolate, "thumbnailUrl"),
mv_item.thumbnail.is_valid()
? GenerateThumb2URL(isolate, mv_item.url, mv_item.thumbnail)
: GenerateThumbnailURL(isolate, render_view_id, restricted_id));
// If the suggestion already has a favicon, we populate the element with it.
if (!mv_item.favicon.spec().empty()) {
obj->Set(v8::String::NewFromUtf8(isolate, "faviconUrl"),
UTF8ToV8String(isolate, mv_item.favicon.spec()));
}
obj->Set(v8::String::NewFromUtf8(isolate, "tileSource"),
v8::Integer::New(isolate, static_cast<int>(mv_item.source)));
obj->Set(v8::String::NewFromUtf8(isolate, "title"),
UTF16ToV8String(isolate, title));
obj->Set(v8::String::NewFromUtf8(isolate, "domain"),
UTF8ToV8String(isolate, mv_item.url.host()));
obj->Set(v8::String::NewFromUtf8(isolate, "direction"),
UTF8ToV8String(isolate, direction));
obj->Set(v8::String::NewFromUtf8(isolate, "url"),
UTF8ToV8String(isolate, mv_item.url.spec()));
return obj;
}
// Returns the main frame of the render frame for the current JS context if it
// matches |origin|, otherwise returns NULL. Used to restrict methods that
// access suggestions and most visited data to pages with origin
// chrome-search://most-visited and chrome-search://suggestions.
content::RenderFrame* GetRenderFrameWithCheckedOrigin(const GURL& origin) {
blink::WebLocalFrame* webframe =
blink::WebLocalFrame::FrameForCurrentContext();
if (!webframe)
return NULL;
auto* main_frame = content::RenderFrame::FromWebFrame(webframe->LocalRoot());
if (!main_frame || !main_frame->IsMainFrame())
return NULL;
GURL url(webframe->GetDocument().Url());
if (url.GetOrigin() != origin.GetOrigin())
return NULL;
return main_frame;
}
} // namespace
namespace internal { // for testing.
// Returns an array with the RGBA color components.
v8::Local<v8::Value> RGBAColorToArray(v8::Isolate* isolate,
const RGBAColor& color) {
v8::Local<v8::Array> color_array = v8::Array::New(isolate, 4);
color_array->Set(0, v8::Int32::New(isolate, color.r));
color_array->Set(1, v8::Int32::New(isolate, color.g));
color_array->Set(2, v8::Int32::New(isolate, color.b));
color_array->Set(3, v8::Int32::New(isolate, color.a));
return color_array;
}
// Resolves a possibly relative URL using the current URL.
GURL ResolveURL(const GURL& current_url,
const base::string16& possibly_relative_url) {
if (current_url.is_valid() && !possibly_relative_url.empty())
return current_url.Resolve(possibly_relative_url);
return GURL(possibly_relative_url);
}
} // namespace internal
namespace extensions_v8 {
static const char kSearchBoxExtensionName[] = "v8/EmbeddedSearch";
// We first send this script down to determine if the page supports instant.
static const char kSupportsInstantScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.searchBox &&"
" window.chrome.embeddedSearch.searchBox.onsubmit &&"
" typeof window.chrome.embeddedSearch.searchBox.onsubmit =="
" 'function') {"
" true;"
"} else {"
" false;"
"}";
static const char kDispatchChromeIdentityCheckResult[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.newTabPage &&"
" window.chrome.embeddedSearch.newTabPage.onsignedincheckdone &&"
" typeof window.chrome.embeddedSearch.newTabPage"
" .onsignedincheckdone === 'function') {"
" window.chrome.embeddedSearch.newTabPage.onsignedincheckdone(%s, %s);"
" true;"
"}";
static const char kDispatchFocusChangedScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.searchBox &&"
" window.chrome.embeddedSearch.searchBox.onfocuschange &&"
" typeof window.chrome.embeddedSearch.searchBox.onfocuschange =="
" 'function') {"
" window.chrome.embeddedSearch.searchBox.onfocuschange();"
" true;"
"}";
static const char kDispatchHistorySyncCheckResult[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.newTabPage &&"
" window.chrome.embeddedSearch.newTabPage.onhistorysynccheckdone &&"
" typeof window.chrome.embeddedSearch.newTabPage"
" .onhistorysynccheckdone === 'function') {"
" window.chrome.embeddedSearch.newTabPage.onhistorysynccheckdone(%s);"
" true;"
"}";
static const char kDispatchInputCancelScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.newTabPage &&"
" window.chrome.embeddedSearch.newTabPage.oninputcancel &&"
" typeof window.chrome.embeddedSearch.newTabPage.oninputcancel =="
" 'function') {"
" window.chrome.embeddedSearch.newTabPage.oninputcancel();"
" true;"
"}";
static const char kDispatchInputStartScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.newTabPage &&"
" window.chrome.embeddedSearch.newTabPage.oninputstart &&"
" typeof window.chrome.embeddedSearch.newTabPage.oninputstart =="
" 'function') {"
" window.chrome.embeddedSearch.newTabPage.oninputstart();"
" true;"
"}";
static const char kDispatchKeyCaptureChangeScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.searchBox &&"
" window.chrome.embeddedSearch.searchBox.onkeycapturechange &&"
" typeof window.chrome.embeddedSearch.searchBox.onkeycapturechange =="
" 'function') {"
" window.chrome.embeddedSearch.searchBox.onkeycapturechange();"
" true;"
"}";
static const char kDispatchMostVisitedChangedScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.newTabPage &&"
" window.chrome.embeddedSearch.newTabPage.onmostvisitedchange &&"
" typeof window.chrome.embeddedSearch.newTabPage.onmostvisitedchange =="
" 'function') {"
" window.chrome.embeddedSearch.newTabPage.onmostvisitedchange();"
" true;"
"}";
static const char kDispatchSubmitEventScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.searchBox &&"
" window.chrome.embeddedSearch.searchBox.onsubmit &&"
" typeof window.chrome.embeddedSearch.searchBox.onsubmit =="
" 'function') {"
" window.chrome.embeddedSearch.searchBox.onsubmit();"
" true;"
"}";
static const char kDispatchSuggestionChangeEventScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.searchBox &&"
" window.chrome.embeddedSearch.searchBox.onsuggestionchange &&"
" typeof window.chrome.embeddedSearch.searchBox.onsuggestionchange =="
" 'function') {"
" window.chrome.embeddedSearch.searchBox.onsuggestionchange();"
" true;"
"}";
static const char kDispatchThemeChangeEventScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
" window.chrome.embeddedSearch.newTabPage &&"
" window.chrome.embeddedSearch.newTabPage.onthemechange &&"
" typeof window.chrome.embeddedSearch.newTabPage.onthemechange =="
" 'function') {"
" window.chrome.embeddedSearch.newTabPage.onthemechange();"
" true;"
"}";
// ----------------------------------------------------------------------------
class SearchBoxExtensionWrapper : public v8::Extension {
public:
explicit SearchBoxExtensionWrapper(const base::StringPiece& code);
// Allows v8's javascript code to call the native functions defined
// in this class for window.chrome.
v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate*,
v8::Local<v8::String> name) override;
// Helper function to find the main RenderFrame. May return NULL.
static content::RenderFrame* GetRenderFrame();
// Sends a Chrome identity check to the browser.
static void CheckIsUserSignedInToChromeAs(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Checks whether the user syncs their history.
static void CheckIsUserSyncingHistory(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Deletes a Most Visited item.
static void DeleteMostVisitedItem(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets Most Visited Items.
static void GetMostVisitedItems(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets the raw data for a most visited item including its raw URL.
// GetRenderFrameWithCheckedOrigin() enforces that only code in the origin
// chrome-search://most-visited can call this function.
static void GetMostVisitedItemData(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets the submitted value of the user's search query.
static void GetQuery(const v8::FunctionCallbackInfo<v8::Value>& args);
// Returns true if the Searchbox itself is oriented right-to-left.
static void GetRightToLeft(const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets the Embedded Search request params. Used for logging purposes.
static void GetSearchRequestParams(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets the current top suggestion to prefetch search results.
static void GetSuggestionToPrefetch(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets the background info of the theme currently adopted by browser.
// Call only when overlay is showing NTP page.
static void GetThemeBackgroundInfo(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets whether the omnibox has focus or not.
static void IsFocused(const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets whether user input is in progress.
static void IsInputInProgress(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Gets whether the browser is capturing key strokes.
static void IsKeyCaptureEnabled(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Logs information from the iframes/titles on the NTP.
static void LogEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
// Logs an impression on one of the Most Visited tile on the NTP.
static void LogMostVisitedImpression(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Logs a navigation on one of the Most Visited tile on the NTP.
static void LogMostVisitedNavigation(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Pastes provided value or clipboard's content into the omnibox.
static void Paste(const v8::FunctionCallbackInfo<v8::Value>& args);
// Start capturing user key strokes.
static void StartCapturingKeyStrokes(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Stop capturing user key strokes.
static void StopCapturingKeyStrokes(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Undoes the deletion of all Most Visited itens.
static void UndoAllMostVisitedDeletions(
const v8::FunctionCallbackInfo<v8::Value>& args);
// Undoes the deletion of a Most Visited item.
static void UndoMostVisitedDeletion(
const v8::FunctionCallbackInfo<v8::Value>& args);
private:
DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper);
};
// static
v8::Extension* SearchBoxExtension::Get() {
return new SearchBoxExtensionWrapper(ResourceBundle::GetSharedInstance().
GetRawDataResource(IDR_SEARCHBOX_API));
}
// static
bool SearchBoxExtension::PageSupportsInstant(blink::WebFrame* frame) {
if (!frame) return false;
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
v8::Local<v8::Value> v = frame->ExecuteScriptAndReturnValue(
blink::WebScriptSource(kSupportsInstantScript));
return !v.IsEmpty() && v->BooleanValue();
}
// static
void SearchBoxExtension::DispatchChromeIdentityCheckResult(
blink::WebFrame* frame,
const base::string16& identity,
bool identity_match) {
std::string escaped_identity = base::GetQuotedJSONString(identity);
blink::WebString script(blink::WebString::FromUTF8(base::StringPrintf(
kDispatchChromeIdentityCheckResult, escaped_identity.c_str(),
identity_match ? "true" : "false")));
Dispatch(frame, script);
}
// static
void SearchBoxExtension::DispatchFocusChange(blink::WebFrame* frame) {
Dispatch(frame, kDispatchFocusChangedScript);
}
// static
void SearchBoxExtension::DispatchHistorySyncCheckResult(
blink::WebFrame* frame,
bool sync_history) {
blink::WebString script(blink::WebString::FromUTF8(base::StringPrintf(
kDispatchHistorySyncCheckResult, sync_history ? "true" : "false")));
Dispatch(frame, script);
}
// static
void SearchBoxExtension::DispatchInputCancel(blink::WebFrame* frame) {
Dispatch(frame, kDispatchInputCancelScript);
}
// static
void SearchBoxExtension::DispatchInputStart(blink::WebFrame* frame) {
Dispatch(frame, kDispatchInputStartScript);
}
// static
void SearchBoxExtension::DispatchKeyCaptureChange(blink::WebFrame* frame) {
Dispatch(frame, kDispatchKeyCaptureChangeScript);
}
// static
void SearchBoxExtension::DispatchMostVisitedChanged(
blink::WebFrame* frame) {
Dispatch(frame, kDispatchMostVisitedChangedScript);
}
// static
void SearchBoxExtension::DispatchSubmit(blink::WebFrame* frame) {
Dispatch(frame, kDispatchSubmitEventScript);
}
// static
void SearchBoxExtension::DispatchSuggestionChange(blink::WebFrame* frame) {
Dispatch(frame, kDispatchSuggestionChangeEventScript);
}
// static
void SearchBoxExtension::DispatchThemeChange(blink::WebFrame* frame) {
Dispatch(frame, kDispatchThemeChangeEventScript);
}
SearchBoxExtensionWrapper::SearchBoxExtensionWrapper(
const base::StringPiece& code)
: v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) {
}
v8::Local<v8::FunctionTemplate>
SearchBoxExtensionWrapper::GetNativeFunctionTemplate(
v8::Isolate* isolate,
v8::Local<v8::String> name) {
if (name->Equals(
v8::String::NewFromUtf8(isolate, "CheckIsUserSignedInToChromeAs")))
return v8::FunctionTemplate::New(isolate, CheckIsUserSignedInToChromeAs);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "CheckIsUserSyncingHistory")))
return v8::FunctionTemplate::New(isolate, CheckIsUserSyncingHistory);
if (name->Equals(v8::String::NewFromUtf8(isolate, "DeleteMostVisitedItem")))
return v8::FunctionTemplate::New(isolate, DeleteMostVisitedItem);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItems")))
return v8::FunctionTemplate::New(isolate, GetMostVisitedItems);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItemData")))
return v8::FunctionTemplate::New(isolate, GetMostVisitedItemData);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetQuery")))
return v8::FunctionTemplate::New(isolate, GetQuery);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetRightToLeft")))
return v8::FunctionTemplate::New(isolate, GetRightToLeft);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSearchRequestParams")))
return v8::FunctionTemplate::New(isolate, GetSearchRequestParams);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetSuggestionToPrefetch")))
return v8::FunctionTemplate::New(isolate, GetSuggestionToPrefetch);
if (name->Equals(v8::String::NewFromUtf8(isolate, "GetThemeBackgroundInfo")))
return v8::FunctionTemplate::New(isolate, GetThemeBackgroundInfo);
if (name->Equals(v8::String::NewFromUtf8(isolate, "IsFocused")))
return v8::FunctionTemplate::New(isolate, IsFocused);
if (name->Equals(v8::String::NewFromUtf8(isolate, "IsInputInProgress")))
return v8::FunctionTemplate::New(isolate, IsInputInProgress);
if (name->Equals(v8::String::NewFromUtf8(isolate, "IsKeyCaptureEnabled")))
return v8::FunctionTemplate::New(isolate, IsKeyCaptureEnabled);
if (name->Equals(v8::String::NewFromUtf8(isolate, "LogEvent")))
return v8::FunctionTemplate::New(isolate, LogEvent);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "LogMostVisitedImpression"))) {
return v8::FunctionTemplate::New(isolate, LogMostVisitedImpression);
}
if (name->Equals(
v8::String::NewFromUtf8(isolate, "LogMostVisitedNavigation"))) {
return v8::FunctionTemplate::New(isolate, LogMostVisitedNavigation);
}
if (name->Equals(v8::String::NewFromUtf8(isolate, "Paste")))
return v8::FunctionTemplate::New(isolate, Paste);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "StartCapturingKeyStrokes")))
return v8::FunctionTemplate::New(isolate, StartCapturingKeyStrokes);
if (name->Equals(v8::String::NewFromUtf8(isolate, "StopCapturingKeyStrokes")))
return v8::FunctionTemplate::New(isolate, StopCapturingKeyStrokes);
if (name->Equals(
v8::String::NewFromUtf8(isolate, "UndoAllMostVisitedDeletions")))
return v8::FunctionTemplate::New(isolate, UndoAllMostVisitedDeletions);
if (name->Equals(v8::String::NewFromUtf8(isolate, "UndoMostVisitedDeletion")))
return v8::FunctionTemplate::New(isolate, UndoMostVisitedDeletion);
return v8::Local<v8::FunctionTemplate>();
}
// static
content::RenderFrame* SearchBoxExtensionWrapper::GetRenderFrame() {
blink::WebLocalFrame* webframe =
blink::WebLocalFrame::FrameForCurrentContext();
if (!webframe) return NULL;
auto* main_frame = content::RenderFrame::FromWebFrame(webframe->LocalRoot());
if (!main_frame || !main_frame->IsMainFrame())
return NULL;
return main_frame;
}
// static
void SearchBoxExtensionWrapper::CheckIsUserSignedInToChromeAs(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
if (!args.Length() || args[0]->IsUndefined()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame << " CheckIsUserSignedInToChromeAs";
SearchBox::Get(render_frame)
->CheckIsUserSignedInToChromeAs(V8ValueToUTF16(args[0]));
}
// static
void SearchBoxExtensionWrapper::CheckIsUserSyncingHistory(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
DVLOG(1) << render_frame << " CheckIsUserSyncingHistory";
SearchBox::Get(render_frame)->CheckIsUserSyncingHistory();
}
// static
void SearchBoxExtensionWrapper::DeleteMostVisitedItem(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
if (!args.Length()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame
<< " DeleteMostVisitedItem: " << args[0]->ToInteger()->Value();
SearchBox::Get(render_frame)
->DeleteMostVisitedItem(args[0]->ToInteger()->Value());
}
// static
void SearchBoxExtensionWrapper::GetMostVisitedItems(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
DVLOG(1) << render_frame << " GetMostVisitedItems";
const SearchBox* search_box = SearchBox::Get(render_frame);
std::vector<InstantMostVisitedItemIDPair> instant_mv_items;
search_box->GetMostVisitedItems(&instant_mv_items);
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Array> v8_mv_items =
v8::Array::New(isolate, instant_mv_items.size());
for (size_t i = 0; i < instant_mv_items.size(); ++i) {
v8_mv_items->Set(
i, GenerateMostVisitedItem(
isolate, render_frame->GetRenderView()->GetRoutingID(),
instant_mv_items[i].first, instant_mv_items[i].second));
}
args.GetReturnValue().Set(v8_mv_items);
}
// static
void SearchBoxExtensionWrapper::GetMostVisitedItemData(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrameWithCheckedOrigin(
GURL(chrome::kChromeSearchMostVisitedUrl));
if (!render_frame)
return;
// Need an rid argument.
if (!args.Length() || !args[0]->IsNumber()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame << " GetMostVisitedItem";
InstantRestrictedID restricted_id = args[0]->IntegerValue();
InstantMostVisitedItem mv_item;
if (!SearchBox::Get(render_frame)
->GetMostVisitedItemWithID(restricted_id, &mv_item)) {
return;
}
v8::Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(GenerateMostVisitedItem(
isolate, render_frame->GetRenderView()->GetRoutingID(), restricted_id,
mv_item));
}
// static
void SearchBoxExtensionWrapper::GetQuery(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
const base::string16& query = SearchBox::Get(render_frame)->query();
DVLOG(1) << render_frame << " GetQuery: '" << query << "'";
v8::Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(UTF16ToV8String(isolate, query));
}
// static
void SearchBoxExtensionWrapper::GetRightToLeft(
const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(base::i18n::IsRTL());
}
// static
void SearchBoxExtensionWrapper::GetSearchRequestParams(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
const EmbeddedSearchRequestParams& params =
SearchBox::Get(render_frame)->GetEmbeddedSearchRequestParams();
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Object> data = v8::Object::New(isolate);
if (!params.search_query.empty()) {
data->Set(v8::String::NewFromUtf8(isolate, kSearchQueryKey),
UTF16ToV8String(isolate, params.search_query));
}
if (!params.original_query.empty()) {
data->Set(v8::String::NewFromUtf8(isolate, kOriginalQueryKey),
UTF16ToV8String(isolate, params.original_query));
}
if (!params.rlz_parameter_value.empty()) {
data->Set(v8::String::NewFromUtf8(isolate, kRLZParameterKey),
UTF16ToV8String(isolate, params.rlz_parameter_value));
}
if (!params.input_encoding.empty()) {
data->Set(v8::String::NewFromUtf8(isolate, kInputEncodingKey),
UTF16ToV8String(isolate, params.input_encoding));
}
if (!params.assisted_query_stats.empty()) {
data->Set(v8::String::NewFromUtf8(isolate, kAssistedQueryStatsKey),
UTF16ToV8String(isolate, params.assisted_query_stats));
}
args.GetReturnValue().Set(data);
}
// static
void SearchBoxExtensionWrapper::GetSuggestionToPrefetch(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
const InstantSuggestion& suggestion =
SearchBox::Get(render_frame)->suggestion();
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Object> data = v8::Object::New(isolate);
data->Set(v8::String::NewFromUtf8(isolate, "text"),
UTF16ToV8String(isolate, suggestion.text));
data->Set(v8::String::NewFromUtf8(isolate, "metadata"),
UTF8ToV8String(isolate, suggestion.metadata));
args.GetReturnValue().Set(data);
}
// static
void SearchBoxExtensionWrapper::GetThemeBackgroundInfo(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
DVLOG(1) << render_frame << " GetThemeBackgroundInfo";
const ThemeBackgroundInfo& theme_info =
SearchBox::Get(render_frame)->GetThemeBackgroundInfo();
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Object> info = v8::Object::New(isolate);
info->Set(v8::String::NewFromUtf8(isolate, "usingDefaultTheme"),
v8::Boolean::New(isolate, theme_info.using_default_theme));
// The theme background color is in RGBA format "rgba(R,G,B,A)" where R, G and
// B are between 0 and 255 inclusive, and A is a double between 0 and 1
// inclusive.
// This is the CSS "background-color" format.
// Value is always valid.
// TODO(jfweitz): Remove this field after GWS is modified to use the new
// backgroundColorRgba field.
info->Set(
v8::String::NewFromUtf8(isolate, "colorRgba"),
UTF8ToV8String(
isolate,
// Convert the alpha using DoubleToString because StringPrintf will
// use
// locale specific formatters (e.g., use , instead of . in German).
base::StringPrintf(
kCSSBackgroundColorFormat,
theme_info.background_color.r,
theme_info.background_color.g,
theme_info.background_color.b,
base::DoubleToString(theme_info.background_color.a / 255.0)
.c_str())));
// Theme color for background as an array with the RGBA components in order.
// Value is always valid.
info->Set(v8::String::NewFromUtf8(isolate, "backgroundColorRgba"),
internal::RGBAColorToArray(isolate, theme_info.background_color));
// Theme color for text as an array with the RGBA components in order.
// Value is always valid.
info->Set(v8::String::NewFromUtf8(isolate, "textColorRgba"),
internal::RGBAColorToArray(isolate, theme_info.text_color));
// Theme color for links as an array with the RGBA components in order.
// Value is always valid.
info->Set(v8::String::NewFromUtf8(isolate, "linkColorRgba"),
internal::RGBAColorToArray(isolate, theme_info.link_color));
// Theme color for light text as an array with the RGBA components in order.
// Value is always valid.
info->Set(v8::String::NewFromUtf8(isolate, "textColorLightRgba"),
internal::RGBAColorToArray(isolate, theme_info.text_color_light));
// Theme color for header as an array with the RGBA components in order.
// Value is always valid.
info->Set(v8::String::NewFromUtf8(isolate, "headerColorRgba"),
internal::RGBAColorToArray(isolate, theme_info.header_color));
// Theme color for section border as an array with the RGBA components in
// order. Value is always valid.
info->Set(
v8::String::NewFromUtf8(isolate, "sectionBorderColorRgba"),
internal::RGBAColorToArray(isolate, theme_info.section_border_color));
// The theme alternate logo value indicates a white logo when TRUE and a
// colorful one when FALSE.
info->Set(v8::String::NewFromUtf8(isolate, "alternateLogo"),
v8::Boolean::New(isolate, theme_info.logo_alternate));
// The theme background image url is of format kCSSBackgroundImageFormat
// where both instances of "%s" are replaced with the id that identifies the
// theme.
// This is the CSS "background-image" format.
// Value is only valid if there's a custom theme background image.
if (crx_file::id_util::IdIsValid(theme_info.theme_id)) {
info->Set(v8::String::NewFromUtf8(isolate, "imageUrl"),
UTF8ToV8String(isolate,
base::StringPrintf(kCSSBackgroundImageFormat,
theme_info.theme_id.c_str(),
theme_info.theme_id.c_str())));
// The theme background image horizontal alignment is one of "left",
// "right", "center".
// This is the horizontal component of the CSS "background-position" format.
// Value is only valid if |imageUrl| is not empty.
std::string alignment = kCSSBackgroundPositionCenter;
if (theme_info.image_horizontal_alignment ==
THEME_BKGRND_IMAGE_ALIGN_LEFT) {
alignment = kCSSBackgroundPositionLeft;
} else if (theme_info.image_horizontal_alignment ==
THEME_BKGRND_IMAGE_ALIGN_RIGHT) {
alignment = kCSSBackgroundPositionRight;
}
info->Set(v8::String::NewFromUtf8(isolate, "imageHorizontalAlignment"),
UTF8ToV8String(isolate, alignment));
// The theme background image vertical alignment is one of "top", "bottom",
// "center".
// This is the vertical component of the CSS "background-position" format.
// Value is only valid if |image_url| is not empty.
if (theme_info.image_vertical_alignment == THEME_BKGRND_IMAGE_ALIGN_TOP) {
alignment = kCSSBackgroundPositionTop;
} else if (theme_info.image_vertical_alignment ==
THEME_BKGRND_IMAGE_ALIGN_BOTTOM) {
alignment = kCSSBackgroundPositionBottom;
} else {
alignment = kCSSBackgroundPositionCenter;
}
info->Set(v8::String::NewFromUtf8(isolate, "imageVerticalAlignment"),
UTF8ToV8String(isolate, alignment));
// The tiling of the theme background image is one of "no-repeat",
// "repeat-x", "repeat-y", "repeat".
// This is the CSS "background-repeat" format.
// Value is only valid if |image_url| is not empty.
std::string tiling = kCSSBackgroundRepeatNo;
switch (theme_info.image_tiling) {
case THEME_BKGRND_IMAGE_NO_REPEAT:
tiling = kCSSBackgroundRepeatNo;
break;
case THEME_BKGRND_IMAGE_REPEAT_X:
tiling = kCSSBackgroundRepeatX;
break;
case THEME_BKGRND_IMAGE_REPEAT_Y:
tiling = kCSSBackgroundRepeatY;
break;
case THEME_BKGRND_IMAGE_REPEAT:
tiling = kCSSBackgroundRepeat;
break;
}
info->Set(v8::String::NewFromUtf8(isolate, "imageTiling"),
UTF8ToV8String(isolate, tiling));
// The theme background image height is only valid if |imageUrl| is valid.
info->Set(v8::String::NewFromUtf8(isolate, "imageHeight"),
v8::Int32::New(isolate, theme_info.image_height));
// The attribution URL is only valid if the theme has attribution logo.
if (theme_info.has_attribution) {
info->Set(
v8::String::NewFromUtf8(isolate, "attributionUrl"),
UTF8ToV8String(isolate,
base::StringPrintf(kThemeAttributionFormat,
theme_info.theme_id.c_str(),
theme_info.theme_id.c_str())));
}
}
args.GetReturnValue().Set(info);
}
// static
void SearchBoxExtensionWrapper::IsFocused(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
bool is_focused = SearchBox::Get(render_frame)->is_focused();
DVLOG(1) << render_frame << " IsFocused: " << is_focused;
args.GetReturnValue().Set(is_focused);
}
// static
void SearchBoxExtensionWrapper::IsInputInProgress(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
bool is_input_in_progress =
SearchBox::Get(render_frame)->is_input_in_progress();
DVLOG(1) << render_frame << " IsInputInProgress: " << is_input_in_progress;
args.GetReturnValue().Set(is_input_in_progress);
}
// static
void SearchBoxExtensionWrapper::IsKeyCaptureEnabled(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
args.GetReturnValue().Set(
SearchBox::Get(render_frame)->is_key_capture_enabled());
}
// static
void SearchBoxExtensionWrapper::LogEvent(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrameWithCheckedOrigin(
GURL(chrome::kChromeSearchMostVisitedUrl));
if (!render_frame)
return;
if (!args.Length() || !args[0]->IsNumber()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame << " LogEvent";
if (args[0]->Uint32Value() <= NTP_EVENT_TYPE_LAST) {
NTPLoggingEventType event =
static_cast<NTPLoggingEventType>(args[0]->Uint32Value());
SearchBox::Get(render_frame)->LogEvent(event);
}
}
// static
void SearchBoxExtensionWrapper::LogMostVisitedImpression(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrameWithCheckedOrigin(
GURL(chrome::kChromeSearchMostVisitedUrl));
if (!render_frame)
return;
if (args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsNumber() ||
!args[2]->IsNumber()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame << " LogMostVisitedImpression";
if (args[1]->Uint32Value() <= static_cast<int>(ntp_tiles::TileSource::LAST) &&
args[2]->Uint32Value() <= ntp_tiles::TileVisualType::TILE_TYPE_MAX) {
auto tile_source =
static_cast<ntp_tiles::TileSource>(args[1]->Uint32Value());
auto tile_type =
static_cast<ntp_tiles::TileVisualType>(args[2]->Uint32Value());
SearchBox::Get(render_frame)
->LogMostVisitedImpression(args[0]->IntegerValue(), tile_source,
tile_type);
}
}
// static
void SearchBoxExtensionWrapper::LogMostVisitedNavigation(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrameWithCheckedOrigin(
GURL(chrome::kChromeSearchMostVisitedUrl));
if (!render_frame)
return;
if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame << " LogMostVisitedNavigation";
if (args[1]->Uint32Value() <= static_cast<int>(ntp_tiles::TileSource::LAST) &&
args[2]->Uint32Value() <= ntp_tiles::TileVisualType::TILE_TYPE_MAX) {
auto tile_source =
static_cast<ntp_tiles::TileSource>(args[1]->Uint32Value());
auto tile_type =
static_cast<ntp_tiles::TileVisualType>(args[2]->Uint32Value());
SearchBox::Get(render_frame)
->LogMostVisitedNavigation(args[0]->IntegerValue(), tile_source,
tile_type);
}
}
// static
void SearchBoxExtensionWrapper::Paste(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
base::string16 text;
if (!args[0]->IsUndefined())
text = V8ValueToUTF16(args[0]);
DVLOG(1) << render_frame << " Paste: " << text;
SearchBox::Get(render_frame)->Paste(text);
}
// static
void SearchBoxExtensionWrapper::StartCapturingKeyStrokes(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
DVLOG(1) << render_frame << " StartCapturingKeyStrokes";
SearchBox::Get(render_frame)->StartCapturingKeyStrokes();
}
// static
void SearchBoxExtensionWrapper::StopCapturingKeyStrokes(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
DVLOG(1) << render_frame << " StopCapturingKeyStrokes";
SearchBox::Get(render_frame)->StopCapturingKeyStrokes();
}
// static
void SearchBoxExtensionWrapper::UndoAllMostVisitedDeletions(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame)
return;
DVLOG(1) << render_frame << " UndoAllMostVisitedDeletions";
SearchBox::Get(render_frame)->UndoAllMostVisitedDeletions();
}
// static
void SearchBoxExtensionWrapper::UndoMostVisitedDeletion(
const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderFrame* render_frame = GetRenderFrame();
if (!render_frame) {
return;
}
if (!args.Length()) {
ThrowInvalidParameters(args);
return;
}
DVLOG(1) << render_frame << " UndoMostVisitedDeletion";
SearchBox::Get(render_frame)
->UndoMostVisitedDeletion(args[0]->ToInteger()->Value());
}
} // namespace extensions_v8