Commit 8d4c276d authored by donnd@chromium.org's avatar donnd@chromium.org

Add onfocuschange to the Extended Search API, with associated isFocused attribute.

BUG=238418

Review URL: https://chromiumcodereview.appspot.com/14646034

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@202937 0039d316-1c4b-4281-b951-d872f2087c98
parent 19f39da0
......@@ -34,21 +34,6 @@ class Image;
class Rect;
}
// Reasons why the Omnibox focus state could change.
enum OmniboxFocusChangeReason {
// Includes any explicit changes to focus. (e.g. user clicking to change
// focus, user tabbing to change focus, any explicit calls to SetFocus,
// etc.)
OMNIBOX_FOCUS_CHANGE_EXPLICIT,
// Focus changed to restore state from a tab the user switched to.
OMNIBOX_FOCUS_CHANGE_TAB_SWITCH,
// Focus changed because user started typing. This only happens when focus
// state is INVISIBLE (and this results in a change to VISIBLE).
OMNIBOX_FOCUS_CHANGE_TYPING,
};
// Reasons why the Omnibox could change into keyword mode.
// These numeric values are used in UMA logs; do not change them.
enum EnteredKeywordModeMethod {
......
......@@ -278,6 +278,7 @@ InstantController::InstantController(BrowserInstantController* browser,
last_transition_type_(content::PAGE_TRANSITION_LINK),
last_match_was_search_(false),
omnibox_focus_state_(OMNIBOX_FOCUS_NONE),
omnibox_focus_change_reason_(OMNIBOX_FOCUS_CHANGE_EXPLICIT),
omnibox_bounds_(-1, -1, 0, 0),
allow_overlay_to_show_search_suggestions_(false),
weak_ptr_factory_(this) {
......@@ -966,22 +967,11 @@ void InstantController::OmniboxFocusChanged(
if (!extended_enabled() && !instant_enabled_)
return;
// Tell the page if the key capture mode changed unless the focus state
// changed because of TYPING. This is because in that case, the browser hasn't
// really stopped capturing key strokes.
//
// (More practically, if we don't do this check, the page would receive
// onkeycapturechange before the corresponding onchange, and the page would
// have no way of telling whether the keycapturechange happened because of
// some actual user action or just because they started typing.)
if (extended_enabled() && GetOverlayContents() &&
reason != OMNIBOX_FOCUS_CHANGE_TYPING) {
const bool is_key_capture_enabled =
omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE;
if (extended_enabled()) {
if (overlay_)
overlay_->KeyCaptureChanged(is_key_capture_enabled);
overlay_->FocusChanged(omnibox_focus_state_, reason);
if (instant_tab_)
instant_tab_->KeyCaptureChanged(is_key_capture_enabled);
instant_tab_->FocusChanged(omnibox_focus_state_, reason);
}
if (state == OMNIBOX_FOCUS_VISIBLE && old_focus_state == OMNIBOX_FOCUS_NONE) {
......@@ -1201,8 +1191,7 @@ void InstantController::InstantPageRenderViewCreated(
// Ensure the searchbox API has the correct initial state.
if (IsContentsFrom(overlay(), contents)) {
overlay_->SetDisplayInstantResults(instant_enabled_);
overlay_->KeyCaptureChanged(
omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE);
overlay_->FocusChanged(omnibox_focus_state_, omnibox_focus_change_reason_);
overlay_->SetOmniboxBounds(omnibox_bounds_);
overlay_->InitializeFonts();
} else if (IsContentsFrom(ntp(), contents)) {
......@@ -1618,8 +1607,8 @@ void InstantController::UpdateInfoForInstantTab() {
instant_tab_->SetOmniboxBounds(omnibox_bounds_);
instant_tab_->InitializeFonts();
StartListeningToMostVisitedChanges();
instant_tab_->KeyCaptureChanged(
omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE);
instant_tab_->FocusChanged(omnibox_focus_state_,
omnibox_focus_change_reason_);
}
}
......
......@@ -505,6 +505,9 @@ class InstantController : public InstantPage::Delegate,
// Omnibox focus state.
OmniboxFocusState omnibox_focus_state_;
// The reason for the most recent omnibox focus change.
OmniboxFocusChangeReason omnibox_focus_change_reason_;
// The search model mode for the active tab.
SearchMode search_mode_;
......
......@@ -118,7 +118,9 @@ class InstantExtendedTest : public InProcessBrowserTest,
on_native_suggestions_calls_(0),
on_change_calls_(0),
submit_count_(0),
on_esc_key_press_event_calls_(0) {
on_esc_key_press_event_calls_(0),
on_focus_changed_calls_(0),
is_focused_(false) {
}
protected:
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
......@@ -182,7 +184,11 @@ class InstantExtendedTest : public InProcessBrowserTest,
GetStringFromJS(contents, "apiHandle.value",
&query_value_) &&
GetIntFromJS(contents, "onEscKeyPressedCalls",
&on_esc_key_press_event_calls_);
&on_esc_key_press_event_calls_) &&
GetIntFromJS(contents, "onFocusChangedCalls",
&on_focus_changed_calls_) &&
GetBoolFromJS(contents, "isFocused",
&is_focused_);
}
TemplateURL* GetDefaultSearchProviderTemplateURL() {
......@@ -234,6 +240,8 @@ class InstantExtendedTest : public InProcessBrowserTest,
int submit_count_;
int on_esc_key_press_event_calls_;
std::string query_value_;
int on_focus_changed_calls_;
bool is_focused_;
};
// Test class used to verify chrome-search: scheme and access policy from the
......@@ -1969,6 +1977,63 @@ IN_PROC_BROWSER_TEST_F(InstantExtendedTest, EscapeClearsOmnibox) {
EXPECT_LT(0, on_esc_key_press_event_calls_);
}
IN_PROC_BROWSER_TEST_F(InstantExtendedTest, FocusApiRespondsToFocusChange) {
ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
EXPECT_FALSE(is_focused_);
EXPECT_EQ(0, on_focus_changed_calls_);
// Focus the omnibox.
FocusOmniboxAndWaitForInstantOverlaySupport();
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_TRUE(is_focused_);
EXPECT_EQ(1, on_focus_changed_calls_);
// Now unfocus the omnibox.
ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER);
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_FALSE(is_focused_);
EXPECT_EQ(2, on_focus_changed_calls_);
// Focus the omnibox again.
// The first focus may have worked only due to initial-state anomalies.
FocusOmnibox();
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_TRUE(is_focused_);
EXPECT_EQ(3, on_focus_changed_calls_);
}
IN_PROC_BROWSER_TEST_F(InstantExtendedTest, FocusApiIgnoresRedundantFocus) {
ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
EXPECT_FALSE(is_focused_);
EXPECT_EQ(0, on_focus_changed_calls_);
// Focus the Omnibox.
FocusOmniboxAndWaitForInstantOverlaySupport();
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_TRUE(is_focused_);
EXPECT_EQ(1, on_focus_changed_calls_);
// When we focus the omnibox again, nothing should change.
FocusOmnibox();
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_TRUE(is_focused_);
EXPECT_EQ(1, on_focus_changed_calls_);
// Now unfocus the omnibox.
ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER);
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_FALSE(is_focused_);
EXPECT_EQ(2, on_focus_changed_calls_);
// When we unfocus again, nothing should change.
ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER);
ASSERT_TRUE(UpdateSearchState(instant()->GetOverlayContents()));
EXPECT_FALSE(is_focused_);
EXPECT_EQ(2, on_focus_changed_calls_);
}
IN_PROC_BROWSER_TEST_F(InstantExtendedTest, OnDefaultSearchProviderChanged) {
InstantService* instant_service =
InstantServiceFactory::GetForProfile(browser()->profile());
......
......@@ -112,9 +112,9 @@ void InstantPage::SetDisplayInstantResults(bool display_instant_results) {
routing_id(), display_instant_results));
}
void InstantPage::KeyCaptureChanged(bool is_key_capture_enabled) {
Send(new ChromeViewMsg_SearchBoxKeyCaptureChanged(
routing_id(), is_key_capture_enabled));
void InstantPage::FocusChanged(OmniboxFocusState state,
OmniboxFocusChangeReason reason) {
Send(new ChromeViewMsg_SearchBoxFocusChanged(routing_id(), state, reason));
}
void InstantPage::SendMostVisitedItems(
......
......@@ -189,8 +189,8 @@ class InstantPage : public content::WebContentsObserver {
// Tells the page whether it is allowed to display Instant results.
void SetDisplayInstantResults(bool display_instant_results);
// Tells the page whether the browser is capturing user key strokes.
void KeyCaptureChanged(bool is_key_capture_enabled);
// Tells the page that the omnibox focus has changed.
void FocusChanged(OmniboxFocusState state, OmniboxFocusChangeReason reason);
// Tells the page about new Most Visited data.
void SendMostVisitedItems(
......
......@@ -20,4 +20,19 @@ enum OmniboxFocusState {
OMNIBOX_FOCUS_INVISIBLE,
};
// Reasons why the Omnibox focus state could change.
enum OmniboxFocusChangeReason {
// Includes any explicit changes to focus. (e.g. user clicking to change
// focus, user tabbing to change focus, any explicit calls to SetFocus,
// etc.)
OMNIBOX_FOCUS_CHANGE_EXPLICIT,
// Focus changed to restore state from a tab the user switched to.
OMNIBOX_FOCUS_CHANGE_TAB_SWITCH,
// Focus changed because user started typing. This only happens when focus
// state is INVISIBLE (and this results in a change to VISIBLE).
OMNIBOX_FOCUS_CHANGE_TYPING,
};
#endif // CHROME_COMMON_OMNIBOX_FOCUS_STATE_H_
......@@ -123,6 +123,7 @@ IPC_ENUM_TRAITS(ChromeViewHostMsg_GetPluginInfo_Status::Value)
IPC_ENUM_TRAITS(InstantCompleteBehavior)
IPC_ENUM_TRAITS(InstantSizeUnits)
IPC_ENUM_TRAITS(InstantSuggestionType)
IPC_ENUM_TRAITS(OmniboxFocusChangeReason)
IPC_ENUM_TRAITS(OmniboxFocusState)
IPC_ENUM_TRAITS(search_provider::OSDDType)
IPC_ENUM_TRAITS(search_provider::InstallState)
......@@ -360,8 +361,9 @@ IPC_MESSAGE_ROUTED2(ChromeViewMsg_SearchBoxFontInformation,
string16 /* omnibox_font */,
size_t /* omnibox_font_size */)
IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxKeyCaptureChanged,
bool /* is_key_capture_enabled */)
IPC_MESSAGE_ROUTED2(ChromeViewMsg_SearchBoxFocusChanged,
OmniboxFocusState /* new_focus_state */,
OmniboxFocusChangeReason /* reason */)
IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxMostVisitedItemsChanged,
std::vector<InstantMostVisitedItemIDPair> /* items */)
......
......@@ -94,6 +94,7 @@ if (!chrome.embeddedSearch) {
native function GetAutocompleteResults();
native function GetDisplayInstantResults();
native function GetFontSize();
native function IsFocused();
native function IsKeyCaptureEnabled();
native function SetQuery();
native function SetQueryFromAutocompleteResult();
......@@ -228,6 +229,7 @@ if (!chrome.embeddedSearch) {
this.__defineGetter__('startMargin', GetStartMargin);
this.__defineGetter__('rtl', GetRightToLeft);
this.__defineGetter__('nativeSuggestions', GetAutocompleteResultsWrapper);
this.__defineGetter__('isFocused', IsFocused);
this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled);
this.__defineGetter__('displayInstantResults', GetDisplayInstantResults);
this.__defineGetter__('font', GetFont);
......@@ -294,6 +296,7 @@ if (!chrome.embeddedSearch) {
this.onmarginchange = null;
this.onnativesuggestions = null;
this.onbarshidden = null;
this.onfocuschange = null;
// DEPRECATED. These methods are from the legacy searchbox API.
// TODO(jered): Delete these.
......
......@@ -34,6 +34,7 @@ SearchBox::SearchBox(content::RenderView* render_view)
selection_start_(0),
selection_end_(0),
start_margin_(0),
is_focused_(false),
is_key_capture_enabled_(false),
display_instant_results_(false),
omnibox_font_size_(0),
......@@ -172,8 +173,7 @@ bool SearchBox::OnMessageReceived(const IPC::Message& message) {
OnCancelSelection)
IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
OnSetDisplayInstantResults)
IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxKeyCaptureChanged,
OnKeyCaptureChange)
IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFocusChanged, OnFocusChanged)
IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged,
OnThemeChanged)
IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFontInformation,
......@@ -324,12 +324,32 @@ void SearchBox::OnCancelSelection(const string16& query,
}
}
void SearchBox::OnKeyCaptureChange(bool is_key_capture_enabled) {
if (is_key_capture_enabled != is_key_capture_enabled_ &&
render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
is_key_capture_enabled_ = is_key_capture_enabled;
DVLOG(1) << render_view() << " OnKeyCaptureChange";
extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange(
void SearchBox::OnFocusChanged(OmniboxFocusState new_focus_state,
OmniboxFocusChangeReason reason) {
bool key_capture_enabled = new_focus_state == OMNIBOX_FOCUS_INVISIBLE;
if (key_capture_enabled != is_key_capture_enabled_) {
// Tell the page if the key capture mode changed unless the focus state
// changed because of TYPING. This is because in that case, the browser
// hasn't really stopped capturing key strokes.
//
// (More practically, if we don't do this check, the page would receive
// onkeycapturechange before the corresponding onchange, and the page would
// have no way of telling whether the keycapturechange happened because of
// some actual user action or just because they started typing.)
if (reason != OMNIBOX_FOCUS_CHANGE_TYPING &&
render_view()->GetWebView() &&
render_view()->GetWebView()->mainFrame()) {
is_key_capture_enabled_ = key_capture_enabled;
DVLOG(1) << render_view() << " OnKeyCaptureChange";
extensions_v8::SearchBoxExtension::DispatchKeyCaptureChange(
render_view()->GetWebView()->mainFrame());
}
}
bool is_focused = new_focus_state == OMNIBOX_FOCUS_VISIBLE;
if (is_focused != is_focused_) {
is_focused_ = is_focused;
DVLOG(1) << render_view() << " OnFocusChange";
extensions_v8::SearchBoxExtension::DispatchFocusChange(
render_view()->GetWebView()->mainFrame());
}
}
......@@ -370,6 +390,7 @@ void SearchBox::Reset() {
selection_end_ = 0;
popup_bounds_ = gfx::Rect();
start_margin_ = 0;
is_focused_ = false;
is_key_capture_enabled_ = false;
theme_info_ = ThemeBackgroundInfo();
// Don't reset display_instant_results_ to prevent clearing it on committed
......
......@@ -11,6 +11,7 @@
#include "base/string16.h"
#include "chrome/common/instant_restricted_id_cache.h"
#include "chrome/common/instant_types.h"
#include "chrome/common/omnibox_focus_state.h"
#include "chrome/common/search_types.h"
#include "content/public/common/page_transition_types.h"
#include "content/public/renderer/render_view_observer.h"
......@@ -66,6 +67,7 @@ class SearchBox : public content::RenderViewObserver,
bool query_is_restricted() const { return query_is_restricted_; }
size_t selection_start() const { return selection_start_; }
size_t selection_end() const { return selection_end_; }
bool is_focused() const { return is_focused_; }
bool is_key_capture_enabled() const { return is_key_capture_enabled_; }
bool display_instant_results() const { return display_instant_results_; }
const string16& omnibox_font() const { return omnibox_font_; }
......@@ -135,7 +137,8 @@ class SearchBox : public content::RenderViewObserver,
bool verbatim,
size_t selection_start,
size_t selection_end);
void OnKeyCaptureChange(bool is_key_capture_enabled);
void OnFocusChanged(OmniboxFocusState new_focus_state,
OmniboxFocusChangeReason reason);
void OnSetDisplayInstantResults(bool display_instant_results);
void OnThemeChanged(const ThemeBackgroundInfo& theme_info);
void OnThemeAreaHeightChanged(int height);
......@@ -160,6 +163,7 @@ class SearchBox : public content::RenderViewObserver,
size_t selection_end_;
int start_margin_;
gfx::Rect popup_bounds_;
bool is_focused_;
bool is_key_capture_enabled_;
ThemeBackgroundInfo theme_info_;
bool display_instant_results_;
......
......@@ -448,6 +448,17 @@ static const char kDispatchBarsHiddenEventScript[] =
" 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;"
"}";
// ----------------------------------------------------------------------------
class SearchBoxExtensionWrapper : public v8::Extension {
......@@ -592,6 +603,9 @@ class SearchBoxExtensionWrapper : public v8::Extension {
static v8::Handle<v8::Value> GetMostVisitedItemData(
const v8::Arguments& args);
// Gets whether the omnibox has focus or not.
static v8::Handle<v8::Value> IsFocused(const v8::Arguments& args);
private:
DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper);
};
......@@ -675,6 +689,8 @@ v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction(
return v8::FunctionTemplate::New(GetSuggestionData);
if (name->Equals(v8::String::New("GetMostVisitedItemData")))
return v8::FunctionTemplate::New(GetMostVisitedItemData);
if (name->Equals(v8::String::New("IsFocused")))
return v8::FunctionTemplate::New(IsFocused);
return v8::Handle<v8::FunctionTemplate>();
}
......@@ -1390,6 +1406,17 @@ v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetMostVisitedItemData(
return GenerateMostVisitedItem(restricted_id, mv_item);
}
// static
v8::Handle<v8::Value> SearchBoxExtensionWrapper::IsFocused(
const v8::Arguments& args) {
content::RenderView* render_view = GetRenderView();
if (!render_view) return v8::Undefined();
bool is_focused = SearchBox::Get(render_view)->is_focused();
DVLOG(1) << render_view << " IsFocused: " << is_focused;
return v8::Boolean::New(is_focused);
}
// static
void SearchBoxExtension::DispatchChange(WebKit::WebFrame* frame) {
Dispatch(frame, kDispatchChangeEventScript);
......@@ -1460,4 +1487,9 @@ void SearchBoxExtension::DispatchBarsHidden(WebKit::WebFrame* frame) {
Dispatch(frame, kDispatchBarsHiddenEventScript);
}
// static
void SearchBoxExtension::DispatchFocusChange(WebKit::WebFrame* frame) {
Dispatch(frame, kDispatchFocusChangedScript);
}
} // namespace extensions_v8
......@@ -41,6 +41,7 @@ class SearchBoxExtension {
static void DispatchMarginChange(WebKit::WebFrame* frame);
static void DispatchThemeChange(WebKit::WebFrame* frame);
static void DispatchBarsHidden(WebKit::WebFrame* frame);
static void DispatchFocusChange(WebKit::WebFrame* frame);
// New Tab Page API.
static void DispatchMostVisitedChanged(WebKit::WebFrame* frame);
......
......@@ -17,6 +17,8 @@ var onNativeSuggestionsCalls = 0;
var onChangeCalls = 0;
var submitCount = 0;
var onEscKeyPressedCalls = 0;
var onFocusChangedCalls = 0;
var isFocused = false;
var onvisibilitycalls = 0;
function getApiHandle() {
......@@ -105,6 +107,11 @@ document.addEventListener("webkitvisibilitychange", function() {
onvisibilitycalls++;
}, false);
function handleFocusChange() {
onFocusChangedCalls++;
isFocused = apiHandle.isFocused;
}
function setUp() {
apiHandle = getApiHandle();
if (!apiHandle)
......@@ -115,6 +122,7 @@ function setUp() {
apiHandle.onsubmit = handleSubmit;
apiHandle.onchange = handleOnChange;
apiHandle.onkeypress = handleKeyPress;
apiHandle.onfocuschange = handleFocusChange;
newTabPageHandle.onmostvisitedchange = handleMostVisitedChange;
if (apiHandle.value) {
handleNativeSuggestions();
......@@ -123,6 +131,7 @@ function setUp() {
if (newTabPageHandle.mostVisited && newTabPageHandle.mostVisited.length) {
handleMostVisitedChange();
}
handleFocusChange();
}
setUp();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment