diff options
author | samarth@chromium.org <samarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-12 04:18:16 +0000 |
---|---|---|
committer | samarth@chromium.org <samarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-12 04:18:16 +0000 |
commit | 6a6811ae25d84530cfdcb5b73cffddb8f4497a04 (patch) | |
tree | 59baed7c1d2ef0066872b5ec109991d220f97ba6 | |
parent | b358c951e427a6c5a6e14a8c84440f8631cae40a (diff) | |
download | chromium_src-6a6811ae25d84530cfdcb5b73cffddb8f4497a04.zip chromium_src-6a6811ae25d84530cfdcb5b73cffddb8f4497a04.tar.gz chromium_src-6a6811ae25d84530cfdcb5b73cffddb8f4497a04.tar.bz2 |
Instant API: tell page whether the browser is capturing key strokes.
TESTED=manual, per bug
BUG=164782
Review URL: https://chromiumcodereview.appspot.com/11413217
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@172524 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/instant/instant_browsertest.cc | 18 | ||||
-rw-r--r-- | chrome/browser/instant/instant_client.cc | 5 | ||||
-rw-r--r-- | chrome/browser/instant/instant_client.h | 3 | ||||
-rw-r--r-- | chrome/browser/instant/instant_controller.cc | 103 | ||||
-rw-r--r-- | chrome/browser/instant/instant_controller.h | 20 | ||||
-rw-r--r-- | chrome/browser/instant/instant_loader.cc | 4 | ||||
-rw-r--r-- | chrome/browser/instant/instant_loader.h | 1 | ||||
-rw-r--r-- | chrome/browser/ui/omnibox/omnibox_edit_model.cc | 72 | ||||
-rw-r--r-- | chrome/browser/ui/omnibox/omnibox_edit_model.h | 63 | ||||
-rw-r--r-- | chrome/browser/ui/views/omnibox/omnibox_view_views.cc | 26 | ||||
-rw-r--r-- | chrome/browser/ui/views/omnibox/omnibox_view_win.cc | 19 | ||||
-rw-r--r-- | chrome/common/render_messages.h | 3 | ||||
-rw-r--r-- | chrome/renderer/resources/extensions/searchbox_api.js | 3 | ||||
-rw-r--r-- | chrome/renderer/searchbox/searchbox.cc | 14 | ||||
-rw-r--r-- | chrome/renderer/searchbox/searchbox.h | 3 | ||||
-rw-r--r-- | chrome/renderer/searchbox/searchbox_extension.cc | 29 | ||||
-rw-r--r-- | chrome/renderer/searchbox/searchbox_extension.h | 1 |
17 files changed, 275 insertions, 112 deletions
diff --git a/chrome/browser/instant/instant_browsertest.cc b/chrome/browser/instant/instant_browsertest.cc index e97b25f..797688c 100644 --- a/chrome/browser/instant/instant_browsertest.cc +++ b/chrome/browser/instant/instant_browsertest.cc @@ -102,10 +102,13 @@ class InstantTest : public InProcessBrowserTest { void FocusOmnibox() { // If the omnibox already has focus, just notify Instant. - if (omnibox()->model()->has_focus()) - instant()->OmniboxGotFocus(); - else + if (omnibox()->model()->has_focus()) { + instant()->OmniboxFocusChanged(OMNIBOX_FOCUS_VISIBLE, + OMNIBOX_FOCUS_CHANGE_EXPLICIT, NULL); + } + else { browser()->window()->GetLocationBar()->FocusLocation(false); + } } void FocusOmniboxAndWaitForInstantSupport() { @@ -678,7 +681,8 @@ IN_PROC_BROWSER_TEST_F(InstantTest, DoesNotCommitURLsTwo) { // Pretend the omnibox got focus. It already had focus, so we are just trying // to tickle a different code path. - instant()->OmniboxGotFocus(); + instant()->OmniboxFocusChanged(OMNIBOX_FOCUS_VISIBLE, + OMNIBOX_FOCUS_CHANGE_EXPLICIT, NULL); // Commit the URL. As before, check that Instant wasn't committed. browser()->window()->GetLocationBar()->AcceptInput(); @@ -897,7 +901,8 @@ IN_PROC_BROWSER_TEST_F(InstantTest, InstantLoaderRefresh) { EXPECT_TRUE(instant()->loader_->supports_instant()); instant()->HideLoader(); EXPECT_TRUE(instant()->loader_->supports_instant()); - instant()->OmniboxLostFocus(NULL); + instant()->OmniboxFocusChanged(OMNIBOX_FOCUS_NONE, + OMNIBOX_FOCUS_CHANGE_EXPLICIT, NULL); EXPECT_FALSE(instant()->loader_->supports_instant()); // Try with a different ordering. @@ -905,7 +910,8 @@ IN_PROC_BROWSER_TEST_F(InstantTest, InstantLoaderRefresh) { instant()->stale_loader_timer_.Stop(); instant()->OnStaleLoader(); EXPECT_TRUE(instant()->loader_->supports_instant()); - instant()->OmniboxLostFocus(NULL); + instant()->OmniboxFocusChanged(OMNIBOX_FOCUS_NONE, + OMNIBOX_FOCUS_CHANGE_EXPLICIT, NULL); // TODO(sreeram): Currently, OmniboxLostFocus() calls HideLoader(). When it // stops hiding the preview eventually, uncomment these two lines: // EXPECT_TRUE(instant()->loader_->supports_instant()); diff --git a/chrome/browser/instant/instant_client.cc b/chrome/browser/instant/instant_client.cc index 50d7852..6f23d9a 100644 --- a/chrome/browser/instant/instant_client.cc +++ b/chrome/browser/instant/instant_client.cc @@ -71,6 +71,11 @@ void InstantClient::SetDisplayInstantResults(bool display_instant_results) { display_instant_results)); } +void InstantClient::KeyCaptureChanged(bool is_key_capture_enabled) { + Send(new ChromeViewMsg_SearchBoxKeyCaptureChanged(routing_id(), + is_key_capture_enabled)); +} + void InstantClient::DidFinishLoad( int64 /* frame_id */, const GURL& /* validated_url */, diff --git a/chrome/browser/instant/instant_client.h b/chrome/browser/instant/instant_client.h index 821c36c..486ae8d 100644 --- a/chrome/browser/instant/instant_client.h +++ b/chrome/browser/instant/instant_client.h @@ -121,6 +121,9 @@ class InstantClient : 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); + private: // Overridden from content::WebContentsObserver: virtual void DidFinishLoad( diff --git a/chrome/browser/instant/instant_controller.cc b/chrome/browser/instant/instant_controller.cc index d3e6545..9f40c85 100644 --- a/chrome/browser/instant/instant_controller.cc +++ b/chrome/browser/instant/instant_controller.cc @@ -156,7 +156,7 @@ InstantController::InstantController(chrome::BrowserInstantController* browser, last_verbatim_(false), last_transition_type_(content::PAGE_TRANSITION_LINK), last_match_was_search_(false), - is_omnibox_focused_(false), + omnibox_focus_state_(OMNIBOX_FOCUS_NONE), allow_preview_to_show_search_suggestions_(false) { } @@ -547,50 +547,37 @@ bool InstantController::CommitIfPossible(InstantCommitType type) { return true; } -void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { - DVLOG(1) << "OmniboxLostFocus"; - is_omnibox_focused_ = false; - - if (!extended_enabled_ && !instant_enabled_) - return; - - // If the preview is showing custom NTP content, don't hide it, commit it - // (no matter where the user clicked) or try to recreate it. - if (model_.mode().is_ntp()) - return; - - // If the preview is not showing at all, recreate it if it's stale. - if (model_.mode().is_default()) { - OnStaleLoader(); - return; - } - - // The preview is showing search suggestions. If GetPreviewContents() is NULL, - // we are in the commit path. Don't do anything. - if (!GetPreviewContents()) - return; - -#if defined(OS_MACOSX) - // TODO(sreeram): See if Mac really needs this special treatment. - if (!loader_->is_pointer_down_from_activate()) - HideLoader(); -#else - if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), - loader_->contents())) - CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST); - else - HideLoader(); -#endif -} - -void InstantController::OmniboxGotFocus() { - DVLOG(1) << "OmniboxGotFocus"; - is_omnibox_focused_ = true; +void InstantController::OmniboxFocusChanged( + OmniboxFocusState state, + OmniboxFocusChangeReason reason, + gfx::NativeView view_gaining_focus) { + DVLOG(1) << "OmniboxFocusChanged: " << omnibox_focus_state_ << " to " + << state << " for reason " << reason; + OmniboxFocusState old_focus_state = omnibox_focus_state_; + omnibox_focus_state_ = state; if (!extended_enabled_ && !instant_enabled_) return; - CreateDefaultLoader(); + // 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_ && GetPreviewContents() && + reason != OMNIBOX_FOCUS_CHANGE_TYPING) + loader_->KeyCaptureChanged(omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE); + + // If focus went from outside the omnibox to the omnibox, preload the default + // search engine, in anticipation of the user typing a query. If the reverse + // happened, commit or discard the preview. + if (state != OMNIBOX_FOCUS_NONE && old_focus_state == OMNIBOX_FOCUS_NONE) + CreateDefaultLoader(); + else if (state == OMNIBOX_FOCUS_NONE && old_focus_state != OMNIBOX_FOCUS_NONE) + OmniboxLostFocus(view_gaining_focus); } void InstantController::SearchModeChanged( @@ -810,6 +797,36 @@ void InstantController::InstantLoaderAboutToNavigateMainFrame(const GURL& url) { CommitIfPossible(INSTANT_COMMIT_NAVIGATED); } +void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { + // If the preview is showing custom NTP content, don't hide it, commit it + // (no matter where the user clicked) or try to recreate it. + if (model_.mode().is_ntp()) + return; + + // If the preview is not showing at all, recreate it if it's stale. + if (model_.mode().is_default()) { + OnStaleLoader(); + return; + } + + // The preview is showing search suggestions. If GetPreviewContents() is NULL, + // we are in the commit path. Don't do anything. + if (!GetPreviewContents()) + return; + +#if defined(OS_MACOSX) + // TODO(sreeram): See if Mac really needs this special treatment. + if (!loader_->is_pointer_down_from_activate()) + HideLoader(); +#else + if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), + loader_->contents())) + CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST); + else + HideLoader(); +#endif +} + bool InstantController::ResetLoader(const TemplateURL* template_url, const content::WebContents* active_tab) { std::string instant_url; @@ -828,6 +845,7 @@ bool InstantController::ResetLoader(const TemplateURL* template_url, browser_->UpdateThemeInfoForPreview(); loader_->SetDisplayInstantResults(instant_enabled_); loader_->SearchModeChanged(search_mode_); + loader_->KeyCaptureChanged(omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE); } // Restart the stale loader timer. @@ -855,7 +873,8 @@ void InstantController::OnStaleLoader() { // If the preview is showing or the omnibox has focus, don't delete the // loader. It will get refreshed the next time the preview is hidden or the // omnibox loses focus. - if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && + if (!stale_loader_timer_.IsRunning() && + omnibox_focus_state_ == OMNIBOX_FOCUS_NONE && model_.mode().is_default()) { loader_.reset(); CreateDefaultLoader(); diff --git a/chrome/browser/instant/instant_controller.h b/chrome/browser/instant/instant_controller.h index 0f37e86..452e434 100644 --- a/chrome/browser/instant/instant_controller.h +++ b/chrome/browser/instant/instant_controller.h @@ -17,6 +17,7 @@ #include "base/timer.h" #include "chrome/browser/instant/instant_commit_type.h" #include "chrome/browser/instant/instant_model.h" +#include "chrome/browser/ui/omnibox/omnibox_edit_model.h" #include "chrome/common/instant_types.h" #include "chrome/common/search_types.h" #include "content/public/common/page_transition_types.h" @@ -84,12 +85,12 @@ class InstantController { // CommitInstant() on the browser, and returns true. Else, returns false. bool CommitIfPossible(InstantCommitType type); - // The omnibox has lost focus. Commit or discard the preview accordingly. - void OmniboxLostFocus(gfx::NativeView view_gaining_focus); - - // The omnibox has gained focus. Preload the default search engine, in - // anticipation of the user typing a query. - void OmniboxGotFocus(); + // Called to indicate that the omnibox focus state changed with the given + // |reason|. If |focus_state| is FOCUS_NONE, |view_gaining_focus| is set to + // the view gaining focus. + void OmniboxFocusChanged(OmniboxFocusState focus_state, + OmniboxFocusChangeReason reason, + gfx::NativeView view_gaining_focus); // The search mode in the active tab has changed. Pass the message down to // the loader which will notify the renderer. Create |instant_tab_| if the @@ -153,6 +154,9 @@ class InstantController { FRIEND_TEST_ALL_PREFIXES(InstantTest, NonInstantSearchProvider); FRIEND_TEST_ALL_PREFIXES(InstantTest, InstantLoaderRefresh); + // Helper for OmniboxFocusChanged. Commit or discard the preview. + void OmniboxLostFocus(gfx::NativeView view_gaining_focus); + // Creates a new loader if necessary, using the instant_url property of the // |template_url| (for example, if the Instant URL has changed since the last // time the loader was created). Returns false if the |template_url| doesn't @@ -243,8 +247,8 @@ class InstantController { // Used to ensure that the preview page is committable. bool last_match_was_search_; - // True if the omnibox is focused, false otherwise. - bool is_omnibox_focused_; + // Omnibox focus state. + OmniboxFocusState omnibox_focus_state_; // The search model mode for the active tab. chrome::search::Mode search_mode_; diff --git a/chrome/browser/instant/instant_loader.cc b/chrome/browser/instant/instant_loader.cc index 7561ffd..06e61d0 100644 --- a/chrome/browser/instant/instant_loader.cc +++ b/chrome/browser/instant/instant_loader.cc @@ -274,6 +274,10 @@ void InstantLoader::SetDisplayInstantResults(bool display_instant_results) { client_.SetDisplayInstantResults(display_instant_results); } +void InstantLoader::KeyCaptureChanged(bool is_key_capture_enabled) { + client_.KeyCaptureChanged(is_key_capture_enabled); +} + void InstantLoader::SetSuggestions( const std::vector<InstantSuggestion>& suggestions) { InstantSupportDetermined(true); diff --git a/chrome/browser/instant/instant_loader.h b/chrome/browser/instant/instant_loader.h index 25257c7..23b6f06 100644 --- a/chrome/browser/instant/instant_loader.h +++ b/chrome/browser/instant/instant_loader.h @@ -101,6 +101,7 @@ class InstantLoader : public InstantClient::Delegate, void SendThemeBackgroundInfo(const ThemeBackgroundInfo& theme_info); void SendThemeAreaHeight(int height); void SetDisplayInstantResults(bool display_instant_results); + void KeyCaptureChanged(bool is_key_capture_enabled); private: class WebContentsDelegateImpl; diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc index d27e9b1..698fba6 100644 --- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc +++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc @@ -71,12 +71,12 @@ OmniboxEditModel::State::State(bool user_input_in_progress, const string16& user_text, const string16& keyword, bool is_keyword_hint, - bool is_caret_visible) + OmniboxFocusState focus_state) : user_input_in_progress(user_input_in_progress), user_text(user_text), keyword(keyword), is_keyword_hint(is_keyword_hint), - is_caret_visible(is_caret_visible) { + focus_state(focus_state) { } OmniboxEditModel::State::~State() { @@ -91,8 +91,7 @@ OmniboxEditModel::OmniboxEditModel(OmniboxView* view, : view_(view), popup_(NULL), controller_(controller), - has_focus_(false), - is_caret_visible_(true), + focus_state_(OMNIBOX_FOCUS_NONE), user_input_in_progress_(false), just_deleted_text_(false), has_temporary_text_(false), @@ -132,11 +131,11 @@ const OmniboxEditModel::State OmniboxEditModel::GetStateForTabSwitch() { } return State(user_input_in_progress_, user_text_, keyword_, is_keyword_hint_, - is_caret_visible_); + focus_state_); } void OmniboxEditModel::RestoreState(const State& state) { - SetCaretVisibility(state.is_caret_visible); + SetFocusState(state.focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH); // Restore any user editing. if (state.user_input_in_progress) { // NOTE: Be sure and set keyword-related state BEFORE invoking @@ -161,7 +160,7 @@ bool OmniboxEditModel::UpdatePermanentText(const string16& new_permanent_text) { // an edit and then abandoned it and clicked a link on the page.) const bool visibly_changed_permanent_text = (permanent_text_ != new_permanent_text) && - (!user_input_in_progress_ || !has_focus_); + (!user_input_in_progress_ || !has_focus()); permanent_text_ = new_permanent_text; return visibly_changed_permanent_text; @@ -440,7 +439,7 @@ void OmniboxEditModel::Revert() { has_temporary_text_ = false; is_temporary_text_set_by_instant_ = false; view_->SetWindowTextAndCaretPos(permanent_text_, - has_focus_ ? permanent_text_.length() : 0, + has_focus() ? permanent_text_.length() : 0, false, true); AutocompleteActionPredictor* action_predictor = AutocompleteActionPredictorFactory::GetForProfile(profile_); @@ -716,13 +715,12 @@ const AutocompleteResult& OmniboxEditModel::result() const { } void OmniboxEditModel::OnSetFocus(bool control_down) { - has_focus_ = true; + // If the omnibox lost focus while the caret was hidden and then regained + // focus, OnSetFocus() is called and should restore visibility. Note that + // focus can be regained without an accompanying call to + // OmniboxView::SetFocus(), e.g. by tabbing in. + SetFocusState(OMNIBOX_FOCUS_VISIBLE, OMNIBOX_FOCUS_CHANGE_EXPLICIT); control_key_state_ = control_down ? DOWN_WITHOUT_CHANGE : UP; - // Restore caret visibility whenever the user focuses back into the omnibox. - SetCaretVisibility(true); - - if (InstantController* instant = controller_->GetInstant()) - instant->OmniboxGotFocus(); content::WebContents* web_contents = controller_->GetWebContents(); if (web_contents) { @@ -739,17 +737,22 @@ void OmniboxEditModel::OnSetFocus(bool control_down) { } void OmniboxEditModel::SetCaretVisibility(bool visible) { - if (has_focus_ && visible != is_caret_visible_) { - is_caret_visible_ = visible; - view_->ApplyCaretVisibility(); + // Caret visibility only matters if the omnibox has focus. + if (focus_state_ != OMNIBOX_FOCUS_NONE) { + SetFocusState(visible ? OMNIBOX_FOCUS_VISIBLE : OMNIBOX_FOCUS_INVISIBLE, + OMNIBOX_FOCUS_CHANGE_EXPLICIT); } } void OmniboxEditModel::OnWillKillFocus(gfx::NativeView view_gaining_focus) { SetInstantSuggestion(InstantSuggestion()); - if (InstantController* instant = controller_->GetInstant()) - instant->OmniboxLostFocus(view_gaining_focus); + InstantController* instant = controller_->GetInstant(); + if (instant) { + instant->OmniboxFocusChanged(OMNIBOX_FOCUS_NONE, + OMNIBOX_FOCUS_CHANGE_EXPLICIT, + view_gaining_focus); + } // TODO(jered): Rip this out along with StartZeroSuggest. autocomplete_controller_->StopZeroSuggest(); @@ -757,7 +760,10 @@ void OmniboxEditModel::OnWillKillFocus(gfx::NativeView view_gaining_focus) { } void OmniboxEditModel::OnKillFocus() { - has_focus_ = false; + // TODO(samarth): determine if it is safe to move the call to + // OmniboxFocusChanged() from OnWillKillFocus() to here, which would let us + // just call SetFocusState() to handle the state change. + focus_state_ = OMNIBOX_FOCUS_NONE; control_key_state_ = UP; paste_state_ = NONE; } @@ -955,7 +961,7 @@ bool OmniboxEditModel::OnAfterPossibleChange(const string16& old_text, // Restore caret visibility whenever the user changes text or selection in the // omnibox. if (text_differs || selection_differs) - SetCaretVisibility(true); + SetFocusState(OMNIBOX_FOCUS_VISIBLE, OMNIBOX_FOCUS_CHANGE_TYPING); // Modifying the selection counts as accepting the autocompleted text. const bool user_text_changed = @@ -1017,7 +1023,8 @@ bool OmniboxEditModel::OnAfterPossibleChange(const string16& old_text, } void OmniboxEditModel::PopupBoundsChangedTo(const gfx::Rect& bounds) { - if (InstantController* instant = controller_->GetInstant()) + InstantController* instant = controller_->GetInstant(); + if (instant) instant->SetOmniboxBounds(bounds); } @@ -1066,7 +1073,8 @@ void OmniboxEditModel::OnResultChanged(bool default_match_changed) { NotifySearchTabHelper(); } - if (InstantController* instant = controller_->GetInstant()) + InstantController* instant = controller_->GetInstant(); + if (instant) instant->HandleAutocompleteResults(*autocomplete_controller_->providers()); } @@ -1291,3 +1299,21 @@ void OmniboxEditModel::ClassifyStringForPasteAndGo( AutocompleteClassifierFactory::GetForProfile(profile_)->Classify(text, string16(), false, false, match, alternate_nav_url); } + +void OmniboxEditModel::SetFocusState(OmniboxFocusState state, + OmniboxFocusChangeReason reason) { + if (state == focus_state_) + return; + + InstantController* instant = controller_->GetInstant(); + if (instant) + instant->OmniboxFocusChanged(state, reason, NULL); + + // Update state and notify view if the omnibox has focus and the caret + // visibility changed. + const bool was_caret_visible = is_caret_visible(); + focus_state_ = state; + if (focus_state_ != OMNIBOX_FOCUS_NONE && + is_caret_visible() != was_caret_visible) + view_->ApplyCaretVisibility(); +} diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.h b/chrome/browser/ui/omnibox/omnibox_edit_model.h index f8444f6..06f47a2 100644 --- a/chrome/browser/ui/omnibox/omnibox_edit_model.h +++ b/chrome/browser/ui/omnibox/omnibox_edit_model.h @@ -31,6 +31,33 @@ class Image; class Rect; } +// Omnibox focus state. +enum OmniboxFocusState { + // Not focused. + OMNIBOX_FOCUS_NONE, + + // Visibly focused. + OMNIBOX_FOCUS_VISIBLE, + + // Invisibly focused, i.e. focused with a hidden caret. + 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, +}; + class OmniboxEditModel : public AutocompleteControllerDelegate { public: struct State { @@ -38,14 +65,14 @@ class OmniboxEditModel : public AutocompleteControllerDelegate { const string16& user_text, const string16& keyword, bool is_keyword_hint, - bool is_caret_visible); + OmniboxFocusState focus_state); ~State(); bool user_input_in_progress; const string16 user_text; const string16 keyword; const bool is_keyword_hint; - const bool is_caret_visible; + OmniboxFocusState focus_state; }; OmniboxEditModel(OmniboxView* view, @@ -191,8 +218,11 @@ class OmniboxEditModel : public AutocompleteControllerDelegate { const GURL& alternate_nav_url, size_t index); - bool has_focus() const { return has_focus_; } - bool is_caret_visible() const { return is_caret_visible_; } + OmniboxFocusState focus_state() const { return focus_state_; } + bool has_focus() const { return focus_state_ != OMNIBOX_FOCUS_NONE; } + bool is_caret_visible() const { + return focus_state_ == OMNIBOX_FOCUS_VISIBLE; + } // Accessors for keyword-related state (see comments on keyword_ and // is_keyword_hint_). @@ -217,12 +247,13 @@ class OmniboxEditModel : public AutocompleteControllerDelegate { void OnSetFocus(bool control_down); // Sets the visibility of the caret in the omnibox, if it has focus. The - // visibility of the caret is reset to visible if any of the following - // happens: - // - User starts typing in the omnibox - // - User clicks in the omnibox - // - Omnibox loses and then regains focus - // - SetFocus() is explicitly called again + // visibility of the caret is reset to visible if either + // - The user starts typing, or + // - We explicitly focus the omnibox again. + // The latter case must be handled in three separate places--OnSetFocus(), + // OmniboxView::SetFocus(), and the mouse handlers in OmniboxView. See + // accompanying comments for why each of these is necessary. + // // Caret visibility is tracked per-tab and updates automatically upon // switching tabs. void SetCaretVisibility(bool visible); @@ -407,6 +438,12 @@ class OmniboxEditModel : public AutocompleteControllerDelegate { AutocompleteMatch* match, GURL* alternate_nav_url) const; + // If focus_state_ does not match |state|, we update it and notify the + // InstantController about the change (passing along the |reason| for the + // change). If the caret visibility changes, we call ApplyCaretVisibility() on + // the view. + void SetFocusState(OmniboxFocusState state, OmniboxFocusChangeReason reason); + scoped_ptr<AutocompleteController> autocomplete_controller_; OmniboxView* view_; @@ -415,11 +452,7 @@ class OmniboxEditModel : public AutocompleteControllerDelegate { OmniboxEditController* controller_; - // Whether the edit has focus. - bool has_focus_; - - // Is the caret visible? Only meaningful if has_focus_ is true. - bool is_caret_visible_; + OmniboxFocusState focus_state_; // The URL of the currently displayed page. string16 permanent_text_; diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc index fce7818..07b7c56 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc @@ -14,6 +14,7 @@ #include "chrome/browser/chromeos/input_method/input_method_configuration.h" #include "chrome/browser/command_updater.h" #include "chrome/browser/ui/omnibox/omnibox_edit_controller.h" +#include "chrome/browser/ui/omnibox/omnibox_edit_model.h" #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" #include "chrome/browser/ui/search/search.h" #include "chrome/browser/ui/view_ids.h" @@ -361,11 +362,14 @@ bool OmniboxViewViews::HandleKeyReleaseEvent(const ui::KeyEvent& event) { void OmniboxViewViews::HandleMousePressEvent(const ui::MouseEvent& event) { select_all_on_mouse_release_ = (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) && - !textfield_->HasFocus(); - // Restore caret visibility whenever the user clicks in the the omnibox. This - // is not always covered by OnSetFocus() because when clicking while the - // omnibox has invisible focus does not trigger a new OnSetFocus() call. - model()->SetCaretVisibility(true); + (!textfield_->HasFocus() || + (model()->focus_state() == OMNIBOX_FOCUS_INVISIBLE)); + // Restore caret visibility whenever the user clicks in the omnibox in a way + // that would give it focus. We must handle this case separately here because + // if the omnibox currently has invisible focus, the mouse event won't trigger + // either SetFocus() or OmniboxEditModel::OnSetFocus(). + if (select_all_on_mouse_release_) + model()->SetCaretVisibility(true); } void OmniboxViewViews::HandleMouseDragEvent(const ui::MouseEvent& event) { @@ -572,14 +576,14 @@ void OmniboxViewViews::UpdatePopup() { } void OmniboxViewViews::SetFocus() { - // Restore caret visibility if focused explicitly. We need to do this here - // because if we already have invisible focus, the RequestFocus() call below - // will short-circuit, preventing us from reaching - // OmniboxEditModel::OnSetFocus(), which handles restoring visibility when we - // didn't previously have focus. - model()->SetCaretVisibility(true); // In views-implementation, the focus is on textfield rather than OmniboxView. textfield_->RequestFocus(); + // Restore caret visibility if focus is explicitly requested. This is + // necessary because if we already have invisible focus, the RequestFocus() + // call above will short-circuit, preventing us from reaching + // OmniboxEditModel::OnSetFocus(), which handles restoring visibility when the + // omnibox regains focus after losing focus. + model()->SetCaretVisibility(true); } void OmniboxViewViews::ApplyCaretVisibility() { diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_win.cc b/chrome/browser/ui/views/omnibox/omnibox_view_win.cc index 32dda4b..5059591 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_view_win.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_view_win.cc @@ -33,6 +33,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/omnibox/omnibox_edit_controller.h" +#include "chrome/browser/ui/omnibox/omnibox_edit_model.h" #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" #include "chrome/browser/ui/search/search.h" #include "chrome/browser/ui/views/location_bar/location_bar_view.h" @@ -761,10 +762,11 @@ void OmniboxViewWin::UpdatePopup() { void OmniboxViewWin::SetFocus() { ::SetFocus(m_hWnd); - // Restore caret visibility if focused explicitly. We need to do this here - // because if we already have invisible focus, the ::SetFocus call above will - // short-circuit, preventing us from reaching OmniboxEditModel::OnSetFocus(), - // which handles restoring visibility when we didn't previously have focus. + // Restore caret visibility if focus is explicitly requested. This is + // necessary because if we already have invisible focus, the ::SetFocus() + // call above will short-circuit, preventing us from reaching + // OmniboxEditModel::OnSetFocus(), which handles restoring visibility when the + // omnibox regains focus after losing focus. model()->SetCaretVisibility(true); } @@ -1772,7 +1774,8 @@ LRESULT OmniboxViewWin::OnMouseActivate(HWND window, // reached before OnXButtonDown(), preventing us from detecting this properly // there. Also in those cases, we need to already know in OnSetFocus() that // we should not restore the saved selection. - if ((!model()->has_focus() || !model()->is_caret_visible()) && + if ((!model()->has_focus() || + (model()->focus_state() == OMNIBOX_FOCUS_INVISIBLE)) && ((mouse_message == WM_LBUTTONDOWN || mouse_message == WM_RBUTTONDOWN)) && (result == MA_ACTIVATE)) { if (gaining_focus_) { @@ -1783,8 +1786,10 @@ LRESULT OmniboxViewWin::OnMouseActivate(HWND window, } gaining_focus_.reset(new ScopedFreeze(this, GetTextObjectModel())); - // Explicitely set focus visibility in the case of clicking on the omnibox, - // which will remove invisible focus if present. + // Restore caret visibility whenever the user clicks in the omnibox in a + // way that would give it focus. We must handle this case separately here + // because if the omnibox currently has invisible focus, the mouse event + // won't trigger either SetFocus() or OmniboxEditModel::OnSetFocus(). model()->SetCaretVisibility(true); // NOTE: Despite |mouse_message| being WM_XBUTTONDOWN here, we're not diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index f91f2bd..a27f376 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -329,6 +329,9 @@ IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxThemeChanged, IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxThemeAreaHeightChanged, int /* height */) +IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxKeyCaptureChanged, + bool /* is_key_capture_enabled */) + // Toggles visual muting of the render view area. This is on when a constrained // window is showing. IPC_MESSAGE_ROUTED1(ChromeViewMsg_SetVisuallyDeemphasized, diff --git a/chrome/renderer/resources/extensions/searchbox_api.js b/chrome/renderer/resources/extensions/searchbox_api.js index f4e6d2d8..1b32046 100644 --- a/chrome/renderer/resources/extensions/searchbox_api.js +++ b/chrome/renderer/resources/extensions/searchbox_api.js @@ -42,6 +42,7 @@ if (!chrome.searchBox) { native function GetDisplayInstantResults(); native function GetThemeBackgroundInfo(); native function GetThemeAreaHeight(); + native function IsKeyCaptureEnabled(); native function NavigateContentWindow(); native function SetSuggestions(); native function SetQuerySuggestion(); @@ -197,6 +198,7 @@ if (!chrome.searchBox) { this.__defineGetter__('width', GetWidth); this.__defineGetter__('height', GetHeight); this.__defineGetter__('nativeSuggestions', GetAutocompleteResultsWrapper); + this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled); this.__defineGetter__('context', GetContext); this.__defineGetter__('displayInstantResults', GetDisplayInstantResults); this.__defineGetter__('themeBackgroundInfo', GetThemeBackgroundInfo); @@ -237,6 +239,7 @@ if (!chrome.searchBox) { this.onresize = null; this.onautocompleteresults = null; this.onkeypress = null; + this.onkeycapturechange = null; this.oncontextchange = null; }; } diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc index e974186..cd52856 100644 --- a/chrome/renderer/searchbox/searchbox.cc +++ b/chrome/renderer/searchbox/searchbox.cc @@ -17,6 +17,7 @@ SearchBox::SearchBox(content::RenderView* render_view) selection_end_(0), results_base_(0), last_results_base_(0), + is_key_capture_enabled_(false), theme_area_height_(0), display_instant_results_(false) { } @@ -114,6 +115,8 @@ bool SearchBox::OnMessageReceived(const IPC::Message& message) { OnModeChanged) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults, OnSetDisplayInstantResults) + IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxKeyCaptureChanged, + OnKeyCaptureChange) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged, OnThemeChanged) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeAreaHeightChanged, @@ -204,6 +207,16 @@ void SearchBox::OnUpOrDownKeyPressed(int count) { } } +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( + render_view()->GetWebView()->mainFrame()); + } +} + void SearchBox::OnModeChanged(const chrome::search::Mode& mode) { mode_ = mode; if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { @@ -241,6 +254,7 @@ void SearchBox::Reset() { results_base_ = 0; rect_ = gfx::Rect(); autocomplete_results_.clear(); + is_key_capture_enabled_ = false; mode_ = chrome::search::Mode(); theme_info_ = ThemeBackgroundInfo(); theme_area_height_ = 0; diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h index cf3d01d..351316c 100644 --- a/chrome/renderer/searchbox/searchbox.h +++ b/chrome/renderer/searchbox/searchbox.h @@ -45,6 +45,7 @@ class SearchBox : public content::RenderViewObserver, size_t selection_end() const { return selection_end_; } int results_base() const { return results_base_; } const chrome::search::Mode& mode() const { return mode_; } + bool is_key_capture_enabled() const { return is_key_capture_enabled_; } bool display_instant_results() const { return display_instant_results_; } gfx::Rect GetRect(); @@ -71,6 +72,7 @@ class SearchBox : public content::RenderViewObserver, void OnAutocompleteResults( const std::vector<InstantAutocompleteResult>& results); void OnUpOrDownKeyPressed(int count); + void OnKeyCaptureChange(bool is_key_capture_enabled); void OnModeChanged(const chrome::search::Mode& mode); void OnSetDisplayInstantResults(bool display_instant_results); void OnThemeChanged(const ThemeBackgroundInfo& theme_info); @@ -88,6 +90,7 @@ class SearchBox : public content::RenderViewObserver, std::vector<InstantAutocompleteResult> autocomplete_results_; size_t last_results_base_; std::vector<InstantAutocompleteResult> last_autocomplete_results_; + bool is_key_capture_enabled_; chrome::search::Mode mode_; ThemeBackgroundInfo theme_info_; int theme_area_height_; diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc index ba6fad7..7a6e1ad 100644 --- a/chrome/renderer/searchbox/searchbox_extension.cc +++ b/chrome/renderer/searchbox/searchbox_extension.cc @@ -141,6 +141,15 @@ static const char kDispatchUpOrDownKeyPressEventScript[] = " true;" "}"; +static const char kDispatchKeyCaptureChangeScript[] = + "if (window.chrome &&" + " window.chrome.searchBox &&" + " window.chrome.searchBox.onkeycapturechange &&" + " typeof window.chrome.searchBox.onkeycapturechange == 'function') {" + " window.chrome.searchBox.onkeycapturechange();" + " true;" + "}"; + static const char kDispatchContextChangeEventScript[] = "if (window.chrome &&" " window.chrome.searchBox &&" @@ -234,6 +243,9 @@ class SearchBoxExtensionWrapper : public v8::Extension { // "top". static v8::Handle<v8::Value> GetThemeAreaHeight(const v8::Arguments& args); + // Gets whether the browser is capturing key strokes. + static v8::Handle<v8::Value> IsKeyCaptureEnabled(const v8::Arguments& args); + // Navigates the window to a URL represented by either a URL string or a // restricted ID. static v8::Handle<v8::Value> NavigateContentWindow(const v8::Arguments& args); @@ -303,6 +315,8 @@ v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( return v8::FunctionTemplate::New(GetThemeBackgroundInfo); if (name->Equals(v8::String::New("GetThemeAreaHeight"))) return v8::FunctionTemplate::New(GetThemeAreaHeight); + if (name->Equals(v8::String::New("IsKeyCaptureEnabled"))) + return v8::FunctionTemplate::New(IsKeyCaptureEnabled); if (name->Equals(v8::String::New("NavigateContentWindow"))) return v8::FunctionTemplate::New(NavigateContentWindow); if (name->Equals(v8::String::New("SetSuggestions"))) @@ -445,6 +459,16 @@ v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetAutocompleteResults( } // static +v8::Handle<v8::Value> SearchBoxExtensionWrapper::IsKeyCaptureEnabled( + const v8::Arguments& args) { + content::RenderView* render_view = GetRenderView(); + if (!render_view) return v8::Undefined(); + + return v8::Boolean::New(SearchBox::Get(render_view)-> + is_key_capture_enabled()); +} + +// static v8::Handle<v8::Value> SearchBoxExtensionWrapper::GetContext( const v8::Arguments& args) { content::RenderView* render_view = GetRenderView(); @@ -830,6 +854,11 @@ void SearchBoxExtension::DispatchUpOrDownKeyPress(WebKit::WebFrame* frame, } // static +void SearchBoxExtension::DispatchKeyCaptureChange(WebKit::WebFrame* frame) { + Dispatch(frame, kDispatchKeyCaptureChangeScript); +} + +// static void SearchBoxExtension::DispatchContextChange(WebKit::WebFrame* frame) { Dispatch(frame, kDispatchContextChangeEventScript); } diff --git a/chrome/renderer/searchbox/searchbox_extension.h b/chrome/renderer/searchbox/searchbox_extension.h index 04eadfc..51fb15b 100644 --- a/chrome/renderer/searchbox/searchbox_extension.h +++ b/chrome/renderer/searchbox/searchbox_extension.h @@ -36,6 +36,7 @@ class SearchBoxExtension { static void DispatchOnWindowReady(WebKit::WebFrame* frame); static void DispatchAutocompleteResults(WebKit::WebFrame* frame); static void DispatchUpOrDownKeyPress(WebKit::WebFrame* frame, int count); + static void DispatchKeyCaptureChange(WebKit::WebFrame* frame); static void DispatchFocus(WebKit::WebFrame* frame); static void DispatchBlur(WebKit::WebFrame* frame); static void DispatchContextChange(WebKit::WebFrame* frame); |