diff options
author | kmadhusu@chromium.org <kmadhusu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-07 04:55:03 +0000 |
---|---|---|
committer | kmadhusu@chromium.org <kmadhusu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-07 04:55:03 +0000 |
commit | ae4ce05f1ae0e469948a1867618a5744b2cbfa1b (patch) | |
tree | 3a7c1db30b2b5a92d399e098ce96af238ff9f0aa | |
parent | 7604ea9d3cf8f290cd9e1e8655cecd3523bae8b7 (diff) | |
download | chromium_src-ae4ce05f1ae0e469948a1867618a5744b2cbfa1b.zip chromium_src-ae4ce05f1ae0e469948a1867618a5744b2cbfa1b.tar.gz chromium_src-ae4ce05f1ae0e469948a1867618a5744b2cbfa1b.tar.bz2 |
Move instant support to SearchTabHelper.
BUG=none
TEST=none
Review URL: https://chromiumcodereview.appspot.com/14911005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204709 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/ui/search/instant_extended_interactive_uitest.cc | 12 | ||||
-rw-r--r-- | chrome/browser/ui/search/instant_page.cc | 98 | ||||
-rw-r--r-- | chrome/browser/ui/search/instant_page.h | 34 | ||||
-rw-r--r-- | chrome/browser/ui/search/instant_page_unittest.cc | 79 | ||||
-rw-r--r-- | chrome/browser/ui/search/instant_tab.cc | 2 | ||||
-rw-r--r-- | chrome/browser/ui/search/instant_test_utils.cc | 12 | ||||
-rw-r--r-- | chrome/browser/ui/search/search_model.cc | 35 | ||||
-rw-r--r-- | chrome/browser/ui/search/search_model.h | 34 | ||||
-rw-r--r-- | chrome/browser/ui/search/search_model_unittest.cc | 160 | ||||
-rw-r--r-- | chrome/browser/ui/search/search_tab_helper.cc | 51 | ||||
-rw-r--r-- | chrome/browser/ui/search/search_tab_helper.h | 27 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 3 |
12 files changed, 453 insertions, 94 deletions
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc index d5048cb..7506c2c 100644 --- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc +++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc @@ -364,13 +364,15 @@ IN_PROC_BROWSER_TEST_F(InstantExtendedTest, MAYBE_UsesOverlayIfTabNotReady) { ASSERT_NO_FATAL_FAILURE(SetupInstant(browser())); FocusOmniboxAndWaitForInstantOverlayAndNTPSupport(); - // Open a new tab and start typing before InstantTab is properly hooked up. - // Should use the overlay. + // Open a new tab and navigate to instant URL. Start typing before InstantTab + // is properly hooked up. Should use the overlay. ui_test_utils::NavigateToURLWithDisposition( browser(), - GURL(chrome::kChromeUINewTabURL), - NEW_FOREGROUND_TAB, - ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); + instant_url(), + NEW_BACKGROUND_TAB, + ui_test_utils::BROWSER_TEST_NONE); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); + browser()->tab_strip_model()->ActivateTabAt(1, false); ASSERT_TRUE(SetOmniboxTextAndWaitForOverlayToShow("query")); // But Instant tab should still exist. diff --git a/chrome/browser/ui/search/instant_page.cc b/chrome/browser/ui/search/instant_page.cc index 9b4a021..029d7cb 100644 --- a/chrome/browser/ui/search/instant_page.cc +++ b/chrome/browser/ui/search/instant_page.cc @@ -5,6 +5,9 @@ #include "chrome/browser/ui/search/instant_page.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/search/search.h" +#include "chrome/browser/ui/search/search_model.h" +#include "chrome/browser/ui/search/search_tab_helper.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "content/public/browser/navigation_controller.h" @@ -19,6 +22,8 @@ InstantPage::Delegate::~Delegate() { } InstantPage::~InstantPage() { + if (contents()) + GetSearchModel()->RemoveObserver(this); } bool InstantPage::supports_instant() const { @@ -71,16 +76,6 @@ void InstantPage::InitializeFonts() { routing_id(), omnibox_font_name, omnibox_font_size)); } -void InstantPage::DetermineIfPageSupportsInstant() { - if (IsLocal()) { - // Local pages always support Instant. That's why we keep them around. - int page_id = contents()->GetController().GetActiveEntry()->GetPageID(); - OnInstantSupportDetermined(page_id, true); - } else { - Send(new ChromeViewMsg_DetermineIfPageSupportsInstant(routing_id())); - } -} - void InstantPage::SendAutocompleteResults( const std::vector<InstantAutocompleteResult>& results) { Send(new ChromeViewMsg_SearchBoxAutocompleteResults(routing_id(), results)); @@ -129,12 +124,26 @@ void InstantPage::ToggleVoiceSearch() { InstantPage::InstantPage(Delegate* delegate, const std::string& instant_url) : delegate_(delegate), instant_url_(instant_url), - supports_instant_(false), - instant_support_determined_(false) { + supports_instant_(false) { } void InstantPage::SetContents(content::WebContents* contents) { + if (web_contents()) + GetSearchModel()->RemoveObserver(this); + Observe(contents); + + if (!contents) { + supports_instant_ = false; + return; + } + + SearchModel* model = GetSearchModel(); + model->AddObserver(this); + + // Already know whether the page supports instant. + if (model->instant_support() != INSTANT_SUPPORT_UNKNOWN) + SetSupportsInstant(model->instant_support() == INSTANT_SUPPORT_YES); } bool InstantPage::ShouldProcessRenderViewCreated() { @@ -170,21 +179,10 @@ void InstantPage::RenderViewCreated(content::RenderViewHost* render_view_host) { delegate_->InstantPageRenderViewCreated(contents()); } -void InstantPage::DidFinishLoad( - int64 /* frame_id */, - const GURL& /* validated_url */, - bool is_main_frame, - content::RenderViewHost* /* render_view_host */) { - if (is_main_frame && !supports_instant_) - DetermineIfPageSupportsInstant(); -} - bool InstantPage::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(InstantPage, message) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SetSuggestions, OnSetSuggestions) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_InstantSupportDetermined, - OnInstantSupportDetermined) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ShowInstantOverlay, OnShowInstantOverlay) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusOmnibox, OnFocusOmnibox) @@ -237,33 +235,41 @@ void InstantPage::DidFailProvisionalLoad( delegate_->InstantPageLoadFailed(contents()); } -void InstantPage::OnSetSuggestions( - int page_id, - const std::vector<InstantSuggestion>& suggestions) { - if (!contents()->IsActiveEntry(page_id)) - return; - - OnInstantSupportDetermined(page_id, true); - if (!ShouldProcessSetSuggestions()) - return; +void InstantPage::ModelChanged(const SearchModel::State& old_state, + const SearchModel::State& new_state) { + if (old_state.instant_support != new_state.instant_support) + SetSupportsInstant(new_state.instant_support == INSTANT_SUPPORT_YES); +} - delegate_->SetSuggestions(contents(), suggestions); +SearchModel* InstantPage::GetSearchModel() { + return contents() ? + SearchTabHelper::FromWebContents(contents())->model() : NULL; } -void InstantPage::OnInstantSupportDetermined(int page_id, - bool supports_instant) { - if (!contents()->IsActiveEntry(page_id) || supports_instant_) { - // Nothing to do if the page already supports Instant. +void InstantPage::SetSupportsInstant(bool supports_instant) { + // Nothing to do if the page already supports Instant. + if (supports_instant_) return; - } - instant_support_determined_ = true; supports_instant_ = supports_instant; delegate_->InstantSupportDetermined(contents(), supports_instant); // If the page doesn't support Instant, stop listening to it. if (!supports_instant) - Observe(NULL); + SetContents(NULL); +} + +void InstantPage::OnSetSuggestions( + int page_id, + const std::vector<InstantSuggestion>& suggestions) { + if (!contents()->IsActiveEntry(page_id)) + return; + + SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); + if (!ShouldProcessSetSuggestions()) + return; + + delegate_->SetSuggestions(contents(), suggestions); } void InstantPage::OnShowInstantOverlay(int page_id, @@ -272,7 +278,7 @@ void InstantPage::OnShowInstantOverlay(int page_id, if (!contents()->IsActiveEntry(page_id)) return; - OnInstantSupportDetermined(page_id, true); + SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); delegate_->LogDropdownShown(); if (!ShouldProcessShowInstantOverlay()) return; @@ -284,7 +290,7 @@ void InstantPage::OnFocusOmnibox(int page_id, OmniboxFocusState state) { if (!contents()->IsActiveEntry(page_id)) return; - OnInstantSupportDetermined(page_id, true); + SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); if (!ShouldProcessFocusOmnibox()) return; @@ -299,7 +305,7 @@ void InstantPage::OnSearchBoxNavigate(int page_id, if (!contents()->IsActiveEntry(page_id)) return; - OnInstantSupportDetermined(page_id, true); + SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); if (!ShouldProcessNavigateToURL()) return; @@ -308,13 +314,19 @@ void InstantPage::OnSearchBoxNavigate(int page_id, } void InstantPage::OnDeleteMostVisitedItem(const GURL& url) { + // TODO(kmadhusu): Call delegate_->DeleteMostVisitedItem() only if the message + // is for the active entry and if the page should process the message. delegate_->DeleteMostVisitedItem(url); } void InstantPage::OnUndoMostVisitedDeletion(const GURL& url) { + // TODO(kmadhusu): Call delegate_->UndoMostVisitedDeletion() only if the + // message is for the active entry and if the page should process the message. delegate_->UndoMostVisitedDeletion(url); } void InstantPage::OnUndoAllMostVisitedDeletions() { + // TODO(kmadhusu): Call delegate_->UndoAllMostVisitedDeletions() only if the + // message is for the active entry and if the page should process the message. delegate_->UndoAllMostVisitedDeletions(); } diff --git a/chrome/browser/ui/search/instant_page.h b/chrome/browser/ui/search/instant_page.h index 56d1291..7361169 100644 --- a/chrome/browser/ui/search/instant_page.h +++ b/chrome/browser/ui/search/instant_page.h @@ -11,6 +11,7 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/string16.h" +#include "chrome/browser/ui/search/search_model_observer.h" #include "chrome/common/instant_types.h" #include "chrome/common/omnibox_focus_state.h" #include "content/public/browser/web_contents_observer.h" @@ -32,7 +33,8 @@ class Rect; // Instant/Embedded Search API (http://dev.chromium.org/embeddedsearch). // InstantPage is not used directly but via one of its derived classes: // InstantOverlay, InstantNTP and InstantTab. -class InstantPage : public content::WebContentsObserver { +class InstantPage : public content::WebContentsObserver, + public SearchModelObserver { public: // InstantPage calls its delegate in response to messages received from the // page. Each method is called with the |contents| corresponding to the page @@ -119,12 +121,6 @@ class InstantPage : public content::WebContentsObserver { // support suddenly). virtual bool supports_instant() const; - // True if Instant support has been tested and determined for this page at - // least once. Note that Instant support may change in the future. - bool instant_support_determined() const { - return instant_support_determined_; - } - // Returns true if the page is the local NTP (i.e. its URL is // chrome::kChromeSearchLocalNTPURL). virtual bool IsLocal() const; @@ -159,10 +155,6 @@ class InstantPage : public content::WebContentsObserver { // Tells the page about the font information. void InitializeFonts(); - // Tells the renderer to determine if the page supports the Instant API, which - // results in a call to InstantSupportDetermined() when the reply is received. - void DetermineIfPageSupportsInstant(); - // Tells the page about the available autocomplete results. void SendAutocompleteResults( const std::vector<InstantAutocompleteResult>& results); @@ -230,11 +222,6 @@ class InstantPage : public content::WebContentsObserver { // Overridden from content::WebContentsObserver: virtual void RenderViewCreated( content::RenderViewHost* render_view_host) OVERRIDE; - virtual void DidFinishLoad( - int64 frame_id, - const GURL& validated_url, - bool is_main_frame, - content::RenderViewHost* render_view_host) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE; virtual void DidCommitProvisionalLoadForFrame( @@ -254,9 +241,18 @@ class InstantPage : public content::WebContentsObserver { const string16& error_description, content::RenderViewHost* render_view_host) OVERRIDE; + // Overridden from SearchModelObserver: + virtual void ModelChanged(const SearchModel::State& old_state, + const SearchModel::State& new_state) OVERRIDE; + + // Helper to look up the SearchModel for this page. + SearchModel* GetSearchModel(); + + // Update the status of Instant support. + void SetSupportsInstant(bool supports_instant); + void OnSetSuggestions(int page_id, const std::vector<InstantSuggestion>& suggestions); - void OnInstantSupportDetermined(int page_id, bool supports_instant); void OnShowInstantOverlay(int page_id, int height, InstantSizeUnits units); @@ -272,8 +268,10 @@ class InstantPage : public content::WebContentsObserver { Delegate* const delegate_; const std::string instant_url_; + + // TODO(kmadhusu): Remove |supports_instant_| from here and get the details + // from SearchModel. Refer to crbug.com/246323 for more details. bool supports_instant_; - bool instant_support_determined_; DISALLOW_COPY_AND_ASSIGN(InstantPage); }; diff --git a/chrome/browser/ui/search/instant_page_unittest.cc b/chrome/browser/ui/search/instant_page_unittest.cc index 592c510..9c79dd0 100644 --- a/chrome/browser/ui/search/instant_page_unittest.cc +++ b/chrome/browser/ui/search/instant_page_unittest.cc @@ -4,10 +4,14 @@ #include "chrome/browser/ui/search/instant_page.h" +#include "base/command_line.h" #include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/search/search_tab_helper.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "content/public/test/mock_render_process_host.h" #include "googleurl/src/gurl.h" @@ -64,20 +68,33 @@ class FakePage : public InstantPage { } using InstantPage::SetContents; + + private: + DISALLOW_COPY_AND_ASSIGN(FakePage); }; } // namespace class InstantPageTest : public ChromeRenderViewHostTestHarness { public: + virtual void SetUp() OVERRIDE; + scoped_ptr<FakePage> page; FakePageDelegate delegate; }; -TEST_F(InstantPageTest, IsLocal) { +void InstantPageTest::SetUp() { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableInstantExtendedAPI); + ChromeRenderViewHostTestHarness::SetUp(); + SearchTabHelper::CreateForWebContents(web_contents()); page.reset(new FakePage(&delegate, "")); - EXPECT_FALSE(page->IsLocal()); page->SetContents(web_contents()); +} + +TEST_F(InstantPageTest, IsLocal) { + EXPECT_FALSE(page->supports_instant()); + EXPECT_FALSE(page->IsLocal()); NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); EXPECT_TRUE(page->IsLocal()); NavigateAndCommit(GURL("http://example.com")); @@ -87,25 +104,23 @@ TEST_F(InstantPageTest, IsLocal) { } TEST_F(InstantPageTest, DetermineIfPageSupportsInstant_Local) { - page.reset(new FakePage(&delegate, "")); EXPECT_FALSE(page->supports_instant()); - page->SetContents(web_contents()); NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); EXPECT_TRUE(page->IsLocal()); EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true)) .Times(1); - page->DetermineIfPageSupportsInstant(); + SearchTabHelper::FromWebContents(web_contents())-> + DetermineIfPageSupportsInstant(); EXPECT_TRUE(page->supports_instant()); } TEST_F(InstantPageTest, DetermineIfPageSupportsInstant_NonLocal) { - page.reset(new FakePage(&delegate, "")); EXPECT_FALSE(page->supports_instant()); - page->SetContents(web_contents()); - NavigateAndCommit(GURL("http://example.com/")); + NavigateAndCommit(GURL("chrome-search://foo/bar")); EXPECT_FALSE(page->IsLocal()); process()->sink().ClearMessages(); - page->DetermineIfPageSupportsInstant(); + SearchTabHelper::FromWebContents(web_contents())-> + DetermineIfPageSupportsInstant(); const IPC::Message* message = process()->sink().GetFirstMessageMatching( ChromeViewMsg_DetermineIfPageSupportsInstant::ID); ASSERT_TRUE(message != NULL); @@ -140,3 +155,49 @@ TEST_F(InstantPageTest, DispatchRequestToUndoAllMostVisitedDeletions) { ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions( rvh()->GetRoutingID()))); } + +TEST_F(InstantPageTest, PageURLDoesntBelongToInstantRenderer) { + EXPECT_FALSE(page->supports_instant()); + + // Navigate to a page URL that doesn't belong to Instant renderer. + // SearchTabHelper::DeterminerIfPageSupportsInstant() should return + // immediately without dispatching any message to the renderer. + NavigateAndCommit(GURL("http://www.example.com")); + EXPECT_FALSE(page->IsLocal()); + process()->sink().ClearMessages(); + EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), false)) + .Times(1); + + SearchTabHelper::FromWebContents(web_contents())-> + DetermineIfPageSupportsInstant(); + const IPC::Message* message = process()->sink().GetFirstMessageMatching( + ChromeViewMsg_DetermineIfPageSupportsInstant::ID); + ASSERT_TRUE(message == NULL); + EXPECT_FALSE(page->supports_instant()); +} + +// Test to verify that ChromeViewMsg_DetermineIfPageSupportsInstant message +// reply handler updates the instant support state in InstantPage. +TEST_F(InstantPageTest, PageSupportsInstant) { + EXPECT_FALSE(page->supports_instant()); + NavigateAndCommit(GURL("chrome-search://foo/bar")); + process()->sink().ClearMessages(); + SearchTabHelper::FromWebContents(web_contents())-> + DetermineIfPageSupportsInstant(); + const IPC::Message* message = process()->sink().GetFirstMessageMatching( + ChromeViewMsg_DetermineIfPageSupportsInstant::ID); + ASSERT_TRUE(message != NULL); + EXPECT_EQ(web_contents()->GetRoutingID(), message->routing_id()); + + EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true)) + .Times(1); + + // Assume the page supports instant. Invoke the message reply handler to make + // sure the InstantPage is notified about the instant support state. + const content::NavigationEntry* entry = + web_contents()->GetController().GetActiveEntry(); + EXPECT_TRUE(entry); + SearchTabHelper::FromWebContents(web_contents())-> + OnInstantSupportDetermined(entry->GetPageID(), true); + EXPECT_TRUE(page->supports_instant()); +} diff --git a/chrome/browser/ui/search/instant_tab.cc b/chrome/browser/ui/search/instant_tab.cc index d92688a..9ebb814 100644 --- a/chrome/browser/ui/search/instant_tab.cc +++ b/chrome/browser/ui/search/instant_tab.cc @@ -14,8 +14,6 @@ InstantTab::~InstantTab() { void InstantTab::Init(content::WebContents* contents) { SetContents(contents); - if (!contents->IsWaitingForResponse()) - DetermineIfPageSupportsInstant(); } bool InstantTab::ShouldProcessAboutToNavigateMainFrame() { diff --git a/chrome/browser/ui/search/instant_test_utils.cc b/chrome/browser/ui/search/instant_test_utils.cc index 0a58371..b6e1d88 100644 --- a/chrome/browser/ui/search/instant_test_utils.cc +++ b/chrome/browser/ui/search/instant_test_utils.cc @@ -122,10 +122,8 @@ void InstantTestBase::FocusOmniboxAndWaitForInstantOverlaySupport() { chrome::NOTIFICATION_INSTANT_OVERLAY_SUPPORT_DETERMINED, content::NotificationService::AllSources()); FocusOmnibox(); - if (!instant()->overlay() || - !instant()->overlay()->instant_support_determined()) { + if (!instant()->overlay() || !instant()->overlay()->supports_instant()) observer.Wait(); - } } void InstantTestBase::FocusOmniboxAndWaitForInstantOverlayAndNTPSupport() { @@ -136,14 +134,10 @@ void InstantTestBase::FocusOmniboxAndWaitForInstantOverlayAndNTPSupport() { chrome::NOTIFICATION_INSTANT_OVERLAY_SUPPORT_DETERMINED, content::NotificationService::AllSources()); FocusOmnibox(); - if (!instant()->ntp() || - !instant()->ntp()->instant_support_determined()) { + if (!instant()->ntp() || !instant()->ntp()->supports_instant()) ntp_observer.Wait(); - } - if (!instant()->overlay() || - !instant()->overlay()->instant_support_determined()) { + if (!instant()->overlay() || !instant()->overlay()->supports_instant()) overlay_observer.Wait(); - } } void InstantTestBase::SetOmniboxText(const std::string& text) { diff --git a/chrome/browser/ui/search/search_model.cc b/chrome/browser/ui/search/search_model.cc index 53d0fcc..da4bcff 100644 --- a/chrome/browser/ui/search/search_model.cc +++ b/chrome/browser/ui/search/search_model.cc @@ -7,6 +7,24 @@ #include "chrome/browser/search/search.h" #include "chrome/browser/ui/search/search_model_observer.h" +SearchModel::State::State() + : top_bars_visible(true), + instant_support(INSTANT_SUPPORT_UNKNOWN) { +} + +SearchModel::State::State(const SearchMode& mode, + bool top_bars_visible, + InstantSupportState instant_support) + : mode(mode), + top_bars_visible(top_bars_visible), + instant_support(instant_support) { +} + +bool SearchModel::State::operator==(const State& rhs) const { + return mode == rhs.mode && top_bars_visible == rhs.top_bars_visible && + instant_support == rhs.instant_support; +} + SearchModel::SearchModel() { } @@ -77,6 +95,23 @@ void SearchModel::SetTopBarsVisible(bool visible) { ModelChanged(old_state, state_)); } +void SearchModel::SetSupportsInstant(bool supports_instant) { + DCHECK(chrome::IsInstantExtendedAPIEnabled()) + << "Please do not try to set the SearchModel mode without first " + << "checking if Search is enabled."; + + InstantSupportState new_instant_support = supports_instant ? + INSTANT_SUPPORT_YES : INSTANT_SUPPORT_NO; + + if (state_.instant_support == new_instant_support) + return; + + const State old_state = state_; + state_.instant_support = new_instant_support; + FOR_EACH_OBSERVER(SearchModelObserver, observers_, + ModelChanged(old_state, state_)); +} + void SearchModel::AddObserver(SearchModelObserver* observer) { observers_.AddObserver(observer); } diff --git a/chrome/browser/ui/search/search_model.h b/chrome/browser/ui/search/search_model.h index 3f77b87..07c26ef 100644 --- a/chrome/browser/ui/search/search_model.h +++ b/chrome/browser/ui/search/search_model.h @@ -11,25 +11,36 @@ class SearchModelObserver; +// Represents whether a page supports Instant. +enum InstantSupportState { + INSTANT_SUPPORT_NO, + INSTANT_SUPPORT_YES, + INSTANT_SUPPORT_UNKNOWN, +}; + // An observable model for UI components that care about search model state // changes. class SearchModel { public: struct State { - State() : top_bars_visible(true) {} - State(const SearchMode& mode, bool top_bars_visible) - : mode(mode), - top_bars_visible(top_bars_visible) {} + State(); + State(const SearchMode& mode, + bool top_bars_visible, + InstantSupportState instant_support); - bool operator==(const State& rhs) const { - return mode == rhs.mode && top_bars_visible == rhs.top_bars_visible; - } + bool operator==(const State& rhs) const; // The display mode of UI elements such as the toolbar, the tab strip, etc. SearchMode mode; // The visibility of top bars such as bookmark and info bars. bool top_bars_visible; + + // Does the current page support Instant? + // + // TODO(kmadhusu): Use bool instead of a tri-state variable. Refer to + // crbug.com/246323 for more details. + InstantSupportState instant_support; }; SearchModel(); @@ -58,6 +69,15 @@ class SearchModel { // Get the visibility of top bars. bool top_bars_visible() const { return state_.top_bars_visible; } + // Set whether the page supports Instant. Change notifications are sent to + // observers. + void SetSupportsInstant(bool supports_instant); + + // Get whether the page supports Instant. + InstantSupportState instant_support() const { + return state_.instant_support; + } + // Add and remove observers. void AddObserver(SearchModelObserver* observer); void RemoveObserver(SearchModelObserver* observer); diff --git a/chrome/browser/ui/search/search_model_unittest.cc b/chrome/browser/ui/search/search_model_unittest.cc new file mode 100644 index 0000000..88ed3aa --- /dev/null +++ b/chrome/browser/ui/search/search_model_unittest.cc @@ -0,0 +1,160 @@ +// Copyright 2013 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/browser/ui/search/search_model.h" + +#include "base/command_line.h" +#include "chrome/browser/ui/search/search_model_observer.h" +#include "chrome/browser/ui/search/search_tab_helper.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/search_types.h" +#include "chrome/test/base/chrome_render_view_host_test_harness.h" + +namespace { + +class MockSearchModelObserver : public SearchModelObserver { + public: + MockSearchModelObserver(); + virtual ~MockSearchModelObserver(); + + virtual void ModelChanged(const SearchModel::State& old_state, + const SearchModel::State& new_state) OVERRIDE; + + void VerifySearchModelStates(const SearchModel::State& expected_old_state, + const SearchModel::State& expected_new_state); + + void VerifyNotificationCount(int expected_count); + + private: + // How many times we've seen search model changed notifications. + int modelchanged_notification_count_; + + SearchModel::State actual_old_state_; + SearchModel::State actual_new_state_; + + DISALLOW_COPY_AND_ASSIGN(MockSearchModelObserver); +}; + +MockSearchModelObserver::MockSearchModelObserver() + : modelchanged_notification_count_(0) { +} + +MockSearchModelObserver::~MockSearchModelObserver() { +} + +void MockSearchModelObserver::ModelChanged( + const SearchModel::State& old_state, + const SearchModel::State& new_state) { + actual_old_state_ = old_state; + actual_new_state_ = new_state; + modelchanged_notification_count_++; +} + +void MockSearchModelObserver::VerifySearchModelStates( + const SearchModel::State& expected_old_state, + const SearchModel::State& expected_new_state) { + EXPECT_TRUE(actual_old_state_ == expected_old_state); + EXPECT_TRUE(actual_new_state_ == expected_new_state); +} + +void MockSearchModelObserver::VerifyNotificationCount(int expected_count) { + EXPECT_EQ(modelchanged_notification_count_, expected_count); +} + +} // namespace + +class SearchModelTest : public ChromeRenderViewHostTestHarness { + public: + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + MockSearchModelObserver mock_observer; + SearchModel* model; +}; + +void SearchModelTest::SetUp() { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableInstantExtendedAPI); + ChromeRenderViewHostTestHarness::SetUp(); + SearchTabHelper::CreateForWebContents(web_contents()); + SearchTabHelper* search_tab_helper = + SearchTabHelper::FromWebContents(web_contents()); + ASSERT_TRUE(search_tab_helper != NULL); + model = search_tab_helper->model(); + model->AddObserver(&mock_observer); +} + +void SearchModelTest::TearDown() { + model->RemoveObserver(&mock_observer); + ChromeRenderViewHostTestHarness::TearDown(); +} + +TEST_F(SearchModelTest, UpdateSearchModelInstantSupport) { + mock_observer.VerifyNotificationCount(0); + EXPECT_TRUE(model->instant_support() == INSTANT_SUPPORT_UNKNOWN); + SearchModel::State expected_old_state = model->state(); + SearchModel::State expected_new_state(model->state()); + expected_new_state.instant_support = INSTANT_SUPPORT_YES; + + model->SetSupportsInstant(true); + mock_observer.VerifySearchModelStates(expected_old_state, expected_new_state); + mock_observer.VerifyNotificationCount(1); + EXPECT_TRUE(model->instant_support() == INSTANT_SUPPORT_YES); + + expected_old_state = expected_new_state; + expected_new_state.instant_support = INSTANT_SUPPORT_NO; + model->SetSupportsInstant(false); + mock_observer.VerifySearchModelStates(expected_old_state, expected_new_state); + mock_observer.VerifyNotificationCount(2); + + // Notify the observer only if the search model state is changed. + model->SetSupportsInstant(false); + EXPECT_TRUE(model->state() == expected_new_state); + EXPECT_TRUE(model->instant_support() == INSTANT_SUPPORT_NO); + mock_observer.VerifyNotificationCount(2); +} + +TEST_F(SearchModelTest, UpdateSearchModelMode) { + mock_observer.VerifyNotificationCount(0); + SearchMode search_mode(SearchMode::MODE_NTP, SearchMode::ORIGIN_NTP); + SearchModel::State expected_old_state = model->state(); + SearchModel::State expected_new_state(model->state()); + expected_new_state.mode = search_mode; + + model->SetMode(search_mode); + mock_observer.VerifySearchModelStates(expected_old_state, expected_new_state); + mock_observer.VerifyNotificationCount(1); + + search_mode.mode = SearchMode::MODE_SEARCH_RESULTS; + expected_old_state = expected_new_state; + expected_new_state.mode = search_mode; + model->SetMode(search_mode); + mock_observer.VerifySearchModelStates(expected_old_state, expected_new_state); + mock_observer.VerifyNotificationCount(2); + EXPECT_TRUE(model->state() == expected_new_state); +} + +TEST_F(SearchModelTest, UpdateTopBarVisibility) { + mock_observer.VerifyNotificationCount(0); + EXPECT_TRUE(model->top_bars_visible()); + + SearchModel::State expected_old_state = model->state(); + SearchModel::State expected_new_state(model->state()); + expected_new_state.top_bars_visible = false; + + model->SetTopBarsVisible(false); + mock_observer.VerifySearchModelStates(expected_old_state, expected_new_state); + mock_observer.VerifyNotificationCount(1); + EXPECT_FALSE(model->top_bars_visible()); +} + +TEST_F(SearchModelTest, UpdateSearchModelState) { + SearchModel::State expected_new_state(model->state()); + expected_new_state.top_bars_visible = false; + expected_new_state.instant_support = INSTANT_SUPPORT_NO; + EXPECT_FALSE(model->state() == expected_new_state); + model->SetState(expected_new_state); + mock_observer.VerifyNotificationCount(1); + EXPECT_TRUE(model->state() == expected_new_state); +} diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc index 5086f81..29e0e83c 100644 --- a/chrome/browser/ui/search/search_tab_helper.cc +++ b/chrome/browser/ui/search/search_tab_helper.cc @@ -4,12 +4,14 @@ #include "chrome/browser/ui/search/search_tab_helper.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/search/search.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/web_contents.h" DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper); @@ -30,6 +32,14 @@ bool IsSearchResults(const content::WebContents* contents) { return !chrome::GetSearchTerms(contents).empty(); } +// TODO(kmadhusu): Move this helper from anonymous namespace to chrome +// namespace and remove InstantPage::IsLocal(). +bool IsLocal(const content::WebContents* contents) { + return contents && + (contents->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl) || + contents->GetURL() == GURL(chrome::kChromeSearchLocalGoogleNtpUrl)); +} + } // namespace SearchTabHelper::SearchTabHelper(content::WebContents* web_contents) @@ -75,6 +85,13 @@ void SearchTabHelper::NavigationEntryUpdated() { UpdateMode(); } +void SearchTabHelper::InstantSupportChanged(bool supports_instant) { + if (!is_search_enabled_) + return; + + model_.SetSupportsInstant(supports_instant); +} + void SearchTabHelper::Observe( int type, const content::NotificationSource& source, @@ -90,11 +107,22 @@ bool SearchTabHelper::OnMessageReceived(const IPC::Message& message) { OnSearchBoxShowBars) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxHideBars, OnSearchBoxHideBars) + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_InstantSupportDetermined, + OnInstantSupportDetermined) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } +void SearchTabHelper::DidFinishLoad( + int64 /* frame_id */, + const GURL& /* validated_url */, + bool is_main_frame, + content::RenderViewHost* /* render_view_host */) { + if (is_main_frame) + DetermineIfPageSupportsInstant(); +} + void SearchTabHelper::UpdateMode() { SearchMode::Type type = SearchMode::MODE_DEFAULT; SearchMode::Origin origin = SearchMode::ORIGIN_DEFAULT; @@ -117,12 +145,33 @@ void SearchTabHelper::UpdateMode() { // OmniboxEditModel::SetInputInProgress() which is called from // OmniboxEditModel::Revert(). model_.SetState(SearchModel::State(SearchMode(type, origin), - model_.state().top_bars_visible)); + model_.state().top_bars_visible, + model_.instant_support())); } else { model_.SetMode(SearchMode(type, origin)); } } +void SearchTabHelper::DetermineIfPageSupportsInstant() { + Profile* profile = + Profile::FromBrowserContext(web_contents_->GetBrowserContext()); + if (!chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(), + profile)) { + InstantSupportChanged(false); + } else if (IsLocal(web_contents_)) { + // Local pages always support Instant. + InstantSupportChanged(true); + } else { + Send(new ChromeViewMsg_DetermineIfPageSupportsInstant(routing_id())); + } +} + +void SearchTabHelper::OnInstantSupportDetermined(int page_id, + bool supports_instant) { + if (web_contents()->IsActiveEntry(page_id)) + InstantSupportChanged(supports_instant); +} + void SearchTabHelper::OnSearchBoxShowBars(int page_id) { if (web_contents()->IsActiveEntry(page_id)) model_.SetTopBarsVisible(true); diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h index 7b0ff13..d7f260f 100644 --- a/chrome/browser/ui/search/search_tab_helper.h +++ b/chrome/browser/ui/search/search_tab_helper.h @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" #include "chrome/browser/ui/search/search_model.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -17,6 +18,8 @@ namespace content { class WebContents; } +class InstantPageTest; + // Per-tab search "helper". Acts as the owner and controller of the tab's // search UI model. class SearchTabHelper : public content::NotificationObserver, @@ -42,8 +45,19 @@ class SearchTabHelper : public content::NotificationObserver, // the notification system and shouldn't call this method. void NavigationEntryUpdated(); + // Invoked to update the Instant support state. + void InstantSupportChanged(bool supports_instant); + private: friend class content::WebContentsUserData<SearchTabHelper>; + friend class InstantPageTest; + FRIEND_TEST_ALL_PREFIXES(InstantPageTest, + DetermineIfPageSupportsInstant_Local); + FRIEND_TEST_ALL_PREFIXES(InstantPageTest, + DetermineIfPageSupportsInstant_NonLocal); + FRIEND_TEST_ALL_PREFIXES(InstantPageTest, + PageURLDoesntBelongToInstantRenderer); + FRIEND_TEST_ALL_PREFIXES(InstantPageTest, PageSupportsInstant); explicit SearchTabHelper(content::WebContents* web_contents); @@ -54,10 +68,23 @@ class SearchTabHelper : public content::NotificationObserver, // Overridden from contents::WebContentsObserver: virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void DidFinishLoad( + int64 frame_id, + const GURL& validated_url, + bool is_main_frame, + content::RenderViewHost* render_view_host) OVERRIDE; // Sets the mode of the model based on the current URL of web_contents(). void UpdateMode(); + // Tells the renderer to determine if the page supports the Instant API, which + // results in a call to OnInstantSupportDetermined() when the reply + // is received. + void DetermineIfPageSupportsInstant(); + + // Handler for when Instant support has been determined. + void OnInstantSupportDetermined(int page_id, bool supports_instant); + // Handlers for SearchBox API to show and hide top bars (bookmark and info // bars). void OnSearchBoxShowBars(int page_id); diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 675233f..e88e0d9 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1501,6 +1501,7 @@ 'browser/ui/search/instant_controller_unittest.cc', 'browser/ui/search/instant_page_unittest.cc', 'browser/ui/search/search_delegate_unittest.cc', + 'browser/ui/search/search_model_unittest.cc', 'browser/ui/startup/session_crashed_infobar_delegate_unittest.cc', 'browser/ui/sync/one_click_signin_helper_unittest.cc', 'browser/ui/sync/profile_signin_confirmation_helper_unittest.cc', @@ -2319,7 +2320,9 @@ 'browser/ui/fullscreen/fullscreen_controller_state_unittest.cc', 'browser/ui/fullscreen/fullscreen_controller_unittest.cc', 'browser/ui/search/instant_controller_unittest.cc', + 'browser/ui/search/instant_page_unittest.cc', 'browser/ui/search/search_delegate_unittest.cc', + 'browser/ui/search/search_model_unittest.cc', 'browser/ui/tab_contents/tab_contents_iterator_unittest.cc', 'browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc', 'browser/ui/toolbar/toolbar_model_unittest.cc', |