diff options
Diffstat (limited to 'chrome/browser')
27 files changed, 1959 insertions, 1432 deletions
diff --git a/chrome/browser/history/DEPS b/chrome/browser/history/DEPS index e9482fc7..0553d7b 100644 --- a/chrome/browser/history/DEPS +++ b/chrome/browser/history/DEPS @@ -26,7 +26,7 @@ include_rules = [ "!chrome/browser/content_settings/cookie_settings.h", "!chrome/browser/diagnostics/sqlite_diagnostics.h", "!chrome/browser/favicon/favicon_service.h", - "!chrome/browser/instant/instant_loader.h", + "!chrome/browser/instant/instant_overlay.h", "!chrome/browser/prefs/pref_service.h", "!chrome/browser/prefs/scoped_user_pref_update.h", "!chrome/browser/prerender/prerender_contents.h", diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc index fc4db96..56b2dee 100644 --- a/chrome/browser/history/history_tab_helper.cc +++ b/chrome/browser/history/history_tab_helper.cc @@ -8,7 +8,7 @@ #include "chrome/browser/history/history_service.h" #include "chrome/browser/history/history_service_factory.h" -#include "chrome/browser/instant/instant_loader.h" +#include "chrome/browser/instant/instant_overlay.h" #include "chrome/browser/prerender/prerender_contents.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/prerender/prerender_manager_factory.h" @@ -127,10 +127,10 @@ void HistoryTabHelper::DidNavigateAnyFrame( } } - InstantLoader* instant_loader = - InstantLoader::FromWebContents(web_contents()); - if (instant_loader) { - instant_loader->DidNavigate(add_page_args); + InstantOverlay* instant_overlay = + InstantOverlay::FromWebContents(web_contents()); + if (instant_overlay) { + instant_overlay->DidNavigate(add_page_args); return; } diff --git a/chrome/browser/instant/instant_browsertest.cc b/chrome/browser/instant/instant_browsertest.cc index 88e79c9..b29cdd7 100644 --- a/chrome/browser/instant/instant_browsertest.cc +++ b/chrome/browser/instant/instant_browsertest.cc @@ -4,7 +4,7 @@ #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/history/history_service_factory.h" -#include "chrome/browser/instant/instant_loader.h" +#include "chrome/browser/instant/instant_overlay.h" #include "chrome/browser/instant/instant_service.h" #include "chrome/browser/instant/instant_service_factory.h" #include "chrome/browser/instant/instant_test_utils.h" @@ -16,9 +16,11 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_notification_types.h" #include "chrome/common/url_constants.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "grit/generated_resources.h" @@ -31,6 +33,14 @@ class InstantTest : public InstantTestBase { instant_url_ = test_server()->GetURL("files/instant.html?"); } + void FocusOmniboxAndWaitForInstantSupport() { + content::WindowedNotificationObserver observer( + chrome::NOTIFICATION_INSTANT_OVERLAY_SUPPORT_DETERMINED, + content::NotificationService::AllSources()); + FocusOmnibox(); + observer.Wait(); + } + bool UpdateSearchState(content::WebContents* contents) WARN_UNUSED_RESULT { return GetIntFromJS(contents, "onvisibilitycalls", &onvisibilitycalls_) && GetIntFromJS(contents, "onchangecalls", &onchangecalls_) && @@ -65,7 +75,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, OmniboxFocusLoadsInstant) { EXPECT_FALSE(omnibox()->model()->has_focus()); // Delete any existing preview. - instant()->loader_.reset(); + instant()->overlay_.reset(); EXPECT_FALSE(instant()->GetPreviewContents()); // Refocus the omnibox. The InstantController should've preloaded Instant. @@ -78,7 +88,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, OmniboxFocusLoadsInstant) { EXPECT_TRUE(preview_tab); // Check that the page supports Instant, but it isn't showing. - EXPECT_TRUE(instant()->loader_->supports_instant()); + EXPECT_TRUE(instant()->overlay_->supports_instant()); EXPECT_FALSE(instant()->IsPreviewingSearchResults()); EXPECT_TRUE(instant()->model()->mode().is_default()); @@ -115,7 +125,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, SetWithTemplateURL) { EXPECT_FALSE(omnibox()->model()->has_focus()); // Delete any existing preview. - instant()->loader_.reset(); + instant()->overlay_.reset(); EXPECT_FALSE(instant()->GetPreviewContents()); // Refocus the omnibox. The InstantController should've preloaded Instant. @@ -128,7 +138,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, SetWithTemplateURL) { EXPECT_TRUE(preview_tab); // Check that the page supports Instant, but it isn't showing. - EXPECT_TRUE(instant()->loader_->supports_instant()); + EXPECT_TRUE(instant()->overlay_->supports_instant()); EXPECT_FALSE(instant()->IsPreviewingSearchResults()); EXPECT_TRUE(instant()->model()->mode().is_default()); } @@ -213,7 +223,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, OnSubmitEvent) { EXPECT_FALSE(instant()->IsPreviewingSearchResults()); EXPECT_TRUE(instant()->model()->mode().is_default()); - // The old loader is deleted and a new one is created. + // The old overlay is deleted and a new one is created. EXPECT_TRUE(instant()->GetPreviewContents()); EXPECT_NE(instant()->GetPreviewContents(), preview_tab); @@ -273,7 +283,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, OnCancelEvent) { EXPECT_FALSE(instant()->IsPreviewingSearchResults()); EXPECT_TRUE(instant()->model()->mode().is_default()); - // The old loader is deleted and a new one is created. + // The old overlay is deleted and a new one is created. EXPECT_TRUE(instant()->GetPreviewContents()); EXPECT_NE(instant()->GetPreviewContents(), preview_tab); @@ -777,37 +787,37 @@ IN_PROC_BROWSER_TEST_F(InstantTest, MAYBE_NewWindowDismissesInstant) { EXPECT_TRUE(instant()->model()->mode().is_default()); } -// Test that the Instant loader is recreated when all these conditions are met: -// - The stale loader timer has fired. +// Test that the Instant overlay is recreated when all these conditions are met: +// - The stale overlay timer has fired. // - The preview is not showing. // - The omnibox doesn't have focus. -IN_PROC_BROWSER_TEST_F(InstantTest, InstantLoaderRefresh) { +IN_PROC_BROWSER_TEST_F(InstantTest, InstantOverlayRefresh) { ASSERT_NO_FATAL_FAILURE(SetupInstant()); FocusOmniboxAndWaitForInstantSupport(); // The preview is refreshed only after all three conditions above are met. SetOmniboxTextAndWaitForInstantToShow("query"); - instant()->stale_loader_timer_.Stop(); - instant()->OnStaleLoader(); - EXPECT_TRUE(instant()->loader_->supports_instant()); - instant()->HideLoader(); - EXPECT_TRUE(instant()->loader_->supports_instant()); + instant()->overlay_->is_stale_ = true; + instant()->ReloadOverlayIfStale(); + EXPECT_TRUE(instant()->overlay_->supports_instant()); + instant()->HideOverlay(); + EXPECT_TRUE(instant()->overlay_->supports_instant()); instant()->OmniboxFocusChanged(OMNIBOX_FOCUS_NONE, OMNIBOX_FOCUS_CHANGE_EXPLICIT, NULL); - EXPECT_FALSE(instant()->loader_->supports_instant()); + EXPECT_FALSE(instant()->overlay_->supports_instant()); // Try with a different ordering. SetOmniboxTextAndWaitForInstantToShow("query"); - instant()->stale_loader_timer_.Stop(); - instant()->OnStaleLoader(); - EXPECT_TRUE(instant()->loader_->supports_instant()); + instant()->overlay_->is_stale_ = true; + instant()->ReloadOverlayIfStale(); + EXPECT_TRUE(instant()->overlay_->supports_instant()); instant()->OmniboxFocusChanged(OMNIBOX_FOCUS_NONE, OMNIBOX_FOCUS_CHANGE_EXPLICIT, NULL); - // TODO(sreeram): Currently, OmniboxLostFocus() calls HideLoader(). When it + // TODO(sreeram): Currently, OmniboxLostFocus() calls HideOverlay(). When it // stops hiding the preview eventually, uncomment these two lines: - // EXPECT_TRUE(instant()->loader_->supports_instant()); - // instant()->HideLoader(); - EXPECT_FALSE(instant()->loader_->supports_instant()); + // EXPECT_TRUE(instant()->overlay_->supports_instant()); + // instant()->HideOverlay(); + EXPECT_FALSE(instant()->overlay_->supports_instant()); } // Test that suggestions are case insensitive. http://crbug.com/150728 @@ -917,7 +927,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, MAYBE_CommitInNewTab) { EXPECT_FALSE(instant()->IsPreviewingSearchResults()); EXPECT_TRUE(instant()->model()->mode().is_default()); - // The old loader is deleted and a new one is created. + // The old overlay is deleted and a new one is created. EXPECT_TRUE(instant()->GetPreviewContents()); EXPECT_NE(instant()->GetPreviewContents(), preview_tab); @@ -971,7 +981,7 @@ IN_PROC_BROWSER_TEST_F(InstantTest, SuggestionsAreReusable) { EXPECT_EQ(ASCIIToUTF16(""), omnibox()->GetInstantSuggestion()); } -// Test that instant loader is recreated if it gets destroyed. +// Test that instant overlay is recreated if it gets destroyed. IN_PROC_BROWSER_TEST_F(InstantTest, InstantRenderViewGone) { ASSERT_NO_FATAL_FAILURE(SetupInstant()); FocusOmniboxAndWaitForInstantSupport(); diff --git a/chrome/browser/instant/instant_client.cc b/chrome/browser/instant/instant_client.cc deleted file mode 100644 index ab5d339..0000000 --- a/chrome/browser/instant/instant_client.cc +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/instant/instant_client.h" - -#include "base/utf_string_conversions.h" -#include "chrome/common/render_messages.h" -#include "content/public/browser/web_contents.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/font.h" - -InstantClient::Delegate::~Delegate() { -} - -InstantClient::InstantClient(Delegate* delegate) : delegate_(delegate) { -} - -InstantClient::~InstantClient() { -} - -void InstantClient::SetContents(content::WebContents* contents) { - Observe(contents); -} - -void InstantClient::Update(const string16& text, - size_t selection_start, - size_t selection_end, - bool verbatim) { - Send(new ChromeViewMsg_SearchBoxChange(routing_id(), text, verbatim, - selection_start, selection_end)); -} - -void InstantClient::Submit(const string16& text) { - Send(new ChromeViewMsg_SearchBoxSubmit(routing_id(), text)); -} - -void InstantClient::Cancel(const string16& text) { - Send(new ChromeViewMsg_SearchBoxCancel(routing_id(), text)); -} - -void InstantClient::SetPopupBounds(const gfx::Rect& bounds) { - Send(new ChromeViewMsg_SearchBoxPopupResize(routing_id(), bounds)); -} - -void InstantClient::SetMarginSize(const int start, const int end) { - Send(new ChromeViewMsg_SearchBoxMarginChange(routing_id(), start, end)); -} - -void InstantClient::InitializeFonts() { - const gfx::Font& omnibox_font = - ui::ResourceBundle::GetSharedInstance().GetFont( - ui::ResourceBundle::MediumFont); - string16 omnibox_font_name = UTF8ToUTF16(omnibox_font.GetFontName()); - size_t omnibox_font_size = omnibox_font.GetFontSize(); - Send(new ChromeViewMsg_SearchBoxFontInformation( - routing_id(), omnibox_font_name, omnibox_font_size)); -} - -void InstantClient::DetermineIfPageSupportsInstant() { - Send(new ChromeViewMsg_DetermineIfPageSupportsInstant(routing_id())); -} - -void InstantClient::SendAutocompleteResults( - const std::vector<InstantAutocompleteResult>& results) { - Send(new ChromeViewMsg_SearchBoxAutocompleteResults(routing_id(), results)); -} - -void InstantClient::UpOrDownKeyPressed(int count) { - Send(new ChromeViewMsg_SearchBoxUpOrDownKeyPressed(routing_id(), count)); -} - -void InstantClient::SearchModeChanged(const chrome::search::Mode& mode) { - Send(new ChromeViewMsg_SearchBoxModeChanged(routing_id(), mode)); -} - -void InstantClient::SendThemeBackgroundInfo( - const ThemeBackgroundInfo& theme_info) { - Send(new ChromeViewMsg_SearchBoxThemeChanged(routing_id(), theme_info)); -} - -void InstantClient::SendThemeAreaHeight(int height) { - Send(new ChromeViewMsg_SearchBoxThemeAreaHeightChanged(routing_id(), height)); -} - -void InstantClient::SetDisplayInstantResults(bool display_instant_results) { - Send(new ChromeViewMsg_SearchBoxSetDisplayInstantResults(routing_id(), - display_instant_results)); -} - -void InstantClient::KeyCaptureChanged(bool is_key_capture_enabled) { - Send(new ChromeViewMsg_SearchBoxKeyCaptureChanged(routing_id(), - is_key_capture_enabled)); -} - -void InstantClient::RenderViewCreated( - content::RenderViewHost* render_view_host) { - delegate_->RenderViewCreated(); -} - -void InstantClient::DidFinishLoad( - int64 /* frame_id */, - const GURL& /* validated_url */, - bool is_main_frame, - content::RenderViewHost* /* render_view_host */) { - if (is_main_frame) - DetermineIfPageSupportsInstant(); -} - -bool InstantClient::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(InstantClient, message) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SetSuggestions, SetSuggestions) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_InstantSupportDetermined, - InstantSupportDetermined) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ShowInstantPreview, - ShowInstantPreview) - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_StartCapturingKeyStrokes, - StartCapturingKeyStrokes); - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_StopCapturingKeyStrokes, - StopCapturingKeyStrokes); - IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxNavigate, - SearchBoxNavigate); - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void InstantClient::RenderViewGone(base::TerminationStatus status) { - delegate_->RenderViewGone(); -} - -void InstantClient::DidCommitProvisionalLoadForFrame( - int64 frame_id, - bool is_main_frame, - const GURL& url, - content::PageTransition transition_type, - content::RenderViewHost* render_view_host) { - if (!is_main_frame) - return; - delegate_->AboutToNavigateMainFrame(url); -} - -void InstantClient::SetSuggestions( - int page_id, - const std::vector<InstantSuggestion>& suggestions) { - if (web_contents()->IsActiveEntry(page_id)) - delegate_->SetSuggestions(suggestions); -} - -void InstantClient::InstantSupportDetermined(int page_id, bool result) { - if (web_contents()->IsActiveEntry(page_id)) - delegate_->InstantSupportDetermined(result); -} - -void InstantClient::ShowInstantPreview(int page_id, - InstantShownReason reason, - int height, - InstantSizeUnits units) { - if (web_contents()->IsActiveEntry(page_id)) - delegate_->ShowInstantPreview(reason, height, units); -} - -void InstantClient::StartCapturingKeyStrokes(int page_id) { - if (web_contents()->IsActiveEntry(page_id)) - delegate_->StartCapturingKeyStrokes(); -} - -void InstantClient::StopCapturingKeyStrokes(int page_id) { - if (web_contents()->IsActiveEntry(page_id)) - delegate_->StopCapturingKeyStrokes(); -} - -void InstantClient::SearchBoxNavigate(int page_id, - const GURL& url, - content::PageTransition transition) { - if (web_contents()->IsActiveEntry(page_id)) - delegate_->NavigateToURL(url, transition); -} diff --git a/chrome/browser/instant/instant_client.h b/chrome/browser/instant/instant_client.h deleted file mode 100644 index dffdc6b..0000000 --- a/chrome/browser/instant/instant_client.h +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_INSTANT_INSTANT_CLIENT_H_ -#define CHROME_BROWSER_INSTANT_INSTANT_CLIENT_H_ - -#include <vector> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/string16.h" -#include "chrome/common/instant_types.h" -#include "content/public/browser/web_contents_observer.h" - -namespace chrome { -namespace search { -struct Mode; -} -} - -namespace content { -class WebContents; -} - -namespace gfx { -class Rect; -} - -// InstantClient is used to exchange messages between its delegate and a page -// that supports the Instant API (http://dev.chromium.org/searchbox). -class InstantClient : public content::WebContentsObserver { - public: - // When InstantClient receives messages from the page, it calls the following - // methods on its delegate. - class Delegate { - public: - // Called when the page has suggestions. Usually in response to Change(), - // SendAutocompleteResults() or UpOrDownKeyPressed(). - virtual void SetSuggestions( - const std::vector<InstantSuggestion>& suggestions) = 0; - - // Called upon determination of Instant API support. Usually in response to - // SetContents() or when the page first finishes loading. - virtual void InstantSupportDetermined(bool supports_instant) = 0; - - // Called when the page wants to be shown. Usually in response to Change(), - // SendAutocompleteResults() or SearchModeChanged(). - virtual void ShowInstantPreview(InstantShownReason reason, - int height, - InstantSizeUnits units) = 0; - - // Called when the page wants the browser to start capturing user key - // strokes. - virtual void StartCapturingKeyStrokes() = 0; - - // Called when the page wants the browser to stop capturing user key - // strokes. - virtual void StopCapturingKeyStrokes() = 0; - - // Called when the underlying RenderView crashes. - virtual void RenderViewGone() = 0; - - // Called when the page is about to navigate. - virtual void AboutToNavigateMainFrame(const GURL& url) = 0; - - // Called when the SearchBox wants to navigate to the specified URL. - virtual void NavigateToURL(const GURL& url, - content::PageTransition transition) = 0; - - // Called when a RenderView is created, so that state can be initialized. - virtual void RenderViewCreated() = 0; - - protected: - virtual ~Delegate(); - }; - - // Doesn't take ownership of |delegate|. - explicit InstantClient(Delegate* delegate); - virtual ~InstantClient(); - - // Sets |contents| as the page to communicate with. |contents| can be NULL, - // which effectively stops all communication. - void SetContents(content::WebContents* contents); - - // Tells the page that the user typed |text| into the omnibox. If |verbatim| - // is false, the page predicts the query the user means to type and fetches - // results for the prediction. If |verbatim| is true, |text| is taken as the - // exact query (no prediction is made). - void Update(const string16& text, - size_t selection_start, - size_t selection_end, - bool verbatim); - - // Tells the page that the user pressed Enter in the omnibox. - void Submit(const string16& text); - - // Tells the page that the user clicked on it. Nothing is being cancelled; the - // poor choice of name merely reflects the IPC of the same (poor) name. - void Cancel(const string16& text); - - // Tells the page the bounds of the omnibox dropdown (in screen coordinates). - // This is used by the page to offset the results to avoid them being covered - // by the omnibox dropdown. - void SetPopupBounds(const gfx::Rect& bounds); - - // Tells the page what size start and end margins to use. - void SetMarginSize(const int start, const int end); - - // 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); - - // Tells the page that the user pressed Up or Down in the omnibox. |count| is - // a repeat count, negative for moving up, positive for moving down. - void UpOrDownKeyPressed(int count); - - // Tells the page that the active tab's search mode has changed. - void SearchModeChanged(const chrome::search::Mode& mode); - - // Tells the page about the current theme background. - void SendThemeBackgroundInfo(const ThemeBackgroundInfo& theme_info); - - // Tells the page about the current theme area height. - void SendThemeAreaHeight(int height); - - // 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 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( - int64 frame_id, - bool is_main_frame, - const GURL& url, - content::PageTransition transition_type, - content::RenderViewHost* render_view_host) OVERRIDE; - - void SetSuggestions(int page_id, - const std::vector<InstantSuggestion>& suggestions); - void InstantSupportDetermined(int page_id, bool result); - void ShowInstantPreview(int page_id, - InstantShownReason reason, - int height, - InstantSizeUnits units); - void StartCapturingKeyStrokes(int page_id); - void StopCapturingKeyStrokes(int page_id); - void SearchBoxNavigate(int page_id, const GURL& url, - content::PageTransition transition); - - Delegate* const delegate_; - - DISALLOW_COPY_AND_ASSIGN(InstantClient); -}; - -#endif // CHROME_BROWSER_INSTANT_INSTANT_CLIENT_H_ diff --git a/chrome/browser/instant/instant_controller.cc b/chrome/browser/instant/instant_controller.cc index 72bf030..80f9b79 100644 --- a/chrome/browser/instant/instant_controller.cc +++ b/chrome/browser/instant/instant_controller.cc @@ -14,7 +14,8 @@ #include "chrome/browser/history/history_service.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/history/history_tab_helper.h" -#include "chrome/browser/instant/instant_loader.h" +#include "chrome/browser/instant/instant_ntp.h" +#include "chrome/browser/instant/instant_overlay.h" #include "chrome/browser/instant/instant_tab.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/search_engines/template_url_service.h" @@ -23,6 +24,7 @@ #include "chrome/browser/ui/search/search_tab_helper.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.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/render_widget_host_view.h" @@ -45,10 +47,6 @@ const int kUpdateBoundsDelayMS = 1000; // before we give up and blacklist it for the rest of the browsing session. const int kMaxInstantSupportFailures = 10; -// If an Instant page has not been used in these many milliseconds, it is -// reloaded so that the page does not become stale. -const int kStaleLoaderTimeoutMS = 3 * 3600 * 1000; - // For reporting events of interest. enum InstantControllerEvent { INSTANT_CONTROLLER_EVENT_URL_ADDED_TO_BLACKLIST = 0, @@ -160,6 +158,11 @@ bool IsFullHeight(const InstantModel& model) { return model.height() == 100 && model.height_units() == INSTANT_SIZE_PERCENT; } +bool IsContentsFrom(const InstantPage* page, + const content::WebContents* contents) { + return page && (page->contents() == contents); +} + } // namespace // static @@ -217,7 +220,7 @@ bool InstantController::Update(const AutocompleteMatch& match, if (instant_tab_) instant_tab_->Update(string16(), 0, 0, true); else - HideLoader(); + HideOverlay(); last_match_was_search_ = false; return false; } @@ -239,13 +242,13 @@ bool InstantController::Update(const AutocompleteMatch& match, // The preview is being clicked and will commit soon. Don't change anything. // TODO(sreeram): Add a browser test for this. - if (loader_ && loader_->is_pointer_down_from_activate()) + if (overlay_ && overlay_->is_pointer_down_from_activate()) return false; // In non-extended mode, SearchModeChanged() is never called, so fake it. The // mode is set to "disallow suggestions" here, so that if one of the early // "return false" conditions is hit, suggestions will be disallowed. If the - // query is sent to the loader, the mode is set to "allow" further below. + // query is sent to the overlay, the mode is set to "allow" further below. if (!extended_enabled_) search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; @@ -256,15 +259,15 @@ bool InstantController::Update(const AutocompleteMatch& match, if (!extended_enabled_ && (!last_match_was_search_ || match.type == AutocompleteMatch::SEARCH_OTHER_ENGINE)) { - HideLoader(); + HideOverlay(); return false; } - // If we have an |instant_tab_| use it, else ensure we have a loader that is + // If we have an |instant_tab_| use it, else ensure we have a overlay that is // current or is using local preview. - if (!instant_tab_ && !(loader_ && loader_->IsUsingLocalPreview()) && - !EnsureLoaderIsCurrent(false)) { - HideLoader(); + if (!instant_tab_ && !(overlay_ && overlay_->IsUsingLocalPreview()) && + !EnsureOverlayIsCurrent(false)) { + HideOverlay(); return false; } @@ -289,7 +292,7 @@ bool InstantController::Update(const AutocompleteMatch& match, } else if (!full_text.empty()) { // If |full_text| is empty, the user is on the NTP. The preview may // be showing custom NTP content; hide only if that's not the case. - HideLoader(); + HideOverlay(); } } else if (full_text.empty()) { // The user is typing, and backspaced away all omnibox text. Clear @@ -302,13 +305,13 @@ bool InstantController::Update(const AutocompleteMatch& match, } else if (search_mode_.is_origin_ntp()) { // On the NTP, tell the preview to clear old results. Don't hide the // preview so it can show a blank page or logo if it wants. - loader_->Update(string16(), 0, 0, true); + overlay_->Update(string16(), 0, 0, true); } else { - HideLoader(); + HideOverlay(); } } else { // The user switched to a tab with partial text already in the omnibox. - HideLoader(); + HideOverlay(); // The new tab may or may not be a search results page; we don't know // since SearchModeChanged() hasn't been called yet. If it later turns @@ -327,15 +330,15 @@ bool InstantController::Update(const AutocompleteMatch& match, if (instant_tab_) instant_tab_->Update(string16(), 0, 0, true); else if (search_mode_.is_origin_ntp()) - loader_->Update(string16(), 0, 0, true); + overlay_->Update(string16(), 0, 0, true); else - HideLoader(); + HideOverlay(); return false; } } else if (!omnibox_popup_is_open || full_text.empty()) { // In the non-extended case, hide the preview as long as the user isn't // actively typing a non-empty query. - HideLoader(); + HideOverlay(); return false; } @@ -387,13 +390,13 @@ bool InstantController::Update(const AutocompleteMatch& match, // For extended mode, if the loader is not ready at this point, switch over // to a backup loader. - if (extended_enabled_ && !loader_->supports_instant() && - !loader_->IsUsingLocalPreview() && browser_->GetActiveWebContents()) { - CreateLoader(kLocalOmniboxPopupURL, browser_->GetActiveWebContents()); + if (extended_enabled_ && !overlay_->supports_instant() && + !overlay_->IsUsingLocalPreview() && browser_->GetActiveWebContents()) { + CreateOverlay(kLocalOmniboxPopupURL, browser_->GetActiveWebContents()); } - loader_->Update(extended_enabled_ ? user_text : full_text, - selection_start, selection_end, verbatim); + overlay_->Update(extended_enabled_ ? user_text : full_text, + selection_start, selection_end, verbatim); } content::NotificationService::current()->Notify( @@ -408,6 +411,18 @@ bool InstantController::Update(const AutocompleteMatch& match, return true; } +scoped_ptr<content::WebContents> InstantController::ReleaseNTPContents() { + if (!extended_enabled_ || !ntp_) + return scoped_ptr<content::WebContents>(NULL); + + LOG_INSTANT_DEBUG_EVENT(this, "ReleaseNTPContents"); + + scoped_ptr<content::WebContents> ntp_contents = ntp_->ReleaseContents(); + ntp_.reset(); + ResetNTP(); + return ntp_contents.Pass(); +} + // TODO(tonyg): This method only fires when the omnibox bounds change. It also // needs to fire when the preview bounds change (e.g.: open/close info bar). void InstantController::SetPopupBounds(const gfx::Rect& bounds) { @@ -434,8 +449,10 @@ void InstantController::SetMarginSize(int start, int end) { start_margin_ = start; end_margin_ = end; - if (loader_) - loader_->SetMarginSize(start_margin_, end_margin_); + if (overlay_) + overlay_->SetMarginSize(start_margin_, end_margin_); + if (ntp_) + ntp_->SetMarginSize(start_margin_, end_margin_); if (instant_tab_) instant_tab_->SetMarginSize(start_margin_, end_margin_); } @@ -445,7 +462,7 @@ void InstantController::HandleAutocompleteResults( if (!extended_enabled_) return; - if (!instant_tab_ && !loader_) + if (!instant_tab_ && !overlay_) return; DVLOG(1) << "AutocompleteResults:"; @@ -474,26 +491,26 @@ void InstantController::HandleAutocompleteResults( if (instant_tab_) instant_tab_->SendAutocompleteResults(results); else - loader_->SendAutocompleteResults(results); + overlay_->SendAutocompleteResults(results); } bool InstantController::OnUpOrDownKeyPressed(int count) { if (!extended_enabled_) return false; - if (!instant_tab_ && !loader_) + if (!instant_tab_ && !overlay_) return false; if (instant_tab_) instant_tab_->UpOrDownKeyPressed(count); else - loader_->UpOrDownKeyPressed(count); + overlay_->UpOrDownKeyPressed(count); return true; } content::WebContents* InstantController::GetPreviewContents() const { - return loader_ ? loader_->contents() : NULL; + return overlay_ ? overlay_->contents() : NULL; } bool InstantController::IsPreviewingSearchResults() const { @@ -530,21 +547,21 @@ bool InstantController::CommitIfPossible(InstantCommitType type) { // There may re-entrance here, from the call to browser_->CommitInstant below, // which can cause a TabDeactivated notification which gets back here. - // In this case, loader_->ReleaseContents() was called already. + // In this case, overlay_->ReleaseContents() was called already. if (!GetPreviewContents()) return false; // Never commit the local omnibox. - if (loader_->IsUsingLocalPreview()) + if (overlay_->IsUsingLocalPreview()) return false; if (type == INSTANT_COMMIT_FOCUS_LOST) - loader_->Cancel(last_omnibox_text_); + overlay_->Cancel(last_omnibox_text_); else if (type != INSTANT_COMMIT_NAVIGATED && type != INSTANT_COMMIT_CLICKED_QUERY_SUGGESTION) - loader_->Submit(last_omnibox_text_); + overlay_->Submit(last_omnibox_text_); - content::WebContents* preview = loader_->ReleaseContents(); + scoped_ptr<content::WebContents> preview = overlay_->ReleaseContents(); if (extended_enabled_) { // Consider what's happening: @@ -577,7 +594,7 @@ bool InstantController::CommitIfPossible(InstantCommitType type) { entry->SetVirtualURL(GURL( url + "#q=" + net::EscapeQueryParamValue(query, true))); - chrome::search::SearchTabHelper::FromWebContents(preview)-> + chrome::search::SearchTabHelper::FromWebContents(preview.get())-> NavigationEntryUpdated(); } } @@ -586,11 +603,11 @@ bool InstantController::CommitIfPossible(InstantCommitType type) { // the navigation to history ourselves. Else, the page will navigate after // commit, and it will be added to history in the usual manner. const history::HistoryAddPageArgs& last_navigation = - loader_->last_navigation(); + overlay_->last_navigation(); if (!last_navigation.url.is_empty()) { content::NavigationEntry* entry = preview->GetController().GetActiveEntry(); - // The last navigation should be the same as the active entry if the loader + // The last navigation should be the same as the active entry if the overlay // is in search mode. During navigation, the active entry could have // changed since DidCommitProvisionalLoadForFrame is called after the entry // is changed. @@ -601,7 +618,7 @@ bool InstantController::CommitIfPossible(InstantCommitType type) { // Add the page to history. HistoryTabHelper* history_tab_helper = - HistoryTabHelper::FromWebContents(preview); + HistoryTabHelper::FromWebContents(preview.get()); history_tab_helper->UpdateHistoryForNavigation(last_navigation); // Update the page title. @@ -623,28 +640,31 @@ bool InstantController::CommitIfPossible(InstantCommitType type) { if (type != INSTANT_COMMIT_PRESSED_ALT_ENTER) { content::WebContents* active_tab = browser_->GetActiveWebContents(); - AddSessionStorageHistogram(extended_enabled_, active_tab, preview); + AddSessionStorageHistogram(extended_enabled_, active_tab, preview.get()); preview->GetController().CopyStateFromAndPrune( &active_tab->GetController()); } - // Browser takes ownership of the preview. - browser_->CommitInstant(preview, type == INSTANT_COMMIT_PRESSED_ALT_ENTER); + // Save notification source before we release the preview. + content::Source<content::WebContents> notification_source(preview.get()); + + browser_->CommitInstant(preview.Pass(), + type == INSTANT_COMMIT_PRESSED_ALT_ENTER); content::NotificationService::current()->Notify( chrome::NOTIFICATION_INSTANT_COMMITTED, - content::Source<content::WebContents>(preview), + notification_source, content::NotificationService::NoDetails()); - // Hide explicitly. See comments in HideLoader() for why. + // Hide explicitly. See comments in HideOverlay() for why. model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); - // Delay deletion as we could've gotten here from an InstantLoader method. - MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); + // Delay deletion as we could've gotten here from an InstantOverlay method. + MessageLoop::current()->DeleteSoon(FROM_HERE, overlay_.release()); - // Try to create another loader immediately so that it is ready for the next + // Try to create another overlay immediately so that it is ready for the next // user interaction. - EnsureLoaderIsCurrent(false); + EnsureOverlayIsCurrent(false); LOG_INSTANT_DEBUG_EVENT(this, "Committed"); return true; @@ -672,15 +692,21 @@ void InstantController::OmniboxFocusChanged( // 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); + reason != OMNIBOX_FOCUS_CHANGE_TYPING) { + const bool is_key_capture_enabled = + omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE; + if (overlay_) + overlay_->KeyCaptureChanged(is_key_capture_enabled); + if (instant_tab_) + instant_tab_->KeyCaptureChanged(is_key_capture_enabled); + } // 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) { // On explicit user actions, ignore the Instant blacklist. - EnsureLoaderIsCurrent(reason == OMNIBOX_FOCUS_CHANGE_EXPLICIT); + EnsureOverlayIsCurrent(reason == OMNIBOX_FOCUS_CHANGE_EXPLICIT); } else if (state == OMNIBOX_FOCUS_NONE && old_focus_state != OMNIBOX_FOCUS_NONE) { OmniboxLostFocus(view_gaining_focus); @@ -699,10 +725,10 @@ void InstantController::SearchModeChanged( search_mode_ = new_mode; if (!new_mode.is_search_suggestions()) - HideLoader(); + HideOverlay(); - if (loader_) - loader_->SearchModeChanged(new_mode); + if (overlay_) + overlay_->SearchModeChanged(new_mode); ResetInstantTab(); } @@ -713,10 +739,8 @@ void InstantController::ActiveTabChanged() { LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged"); - // When switching tabs, always hide the preview, except if it's showing NTP - // content, and the new tab is also an NTP. - if (!search_mode_.is_ntp() || !model_.mode().is_ntp()) - HideLoader(); + // When switching tabs, always hide the preview. + HideOverlay(); if (extended_enabled_) ResetInstantTab(); @@ -744,9 +768,11 @@ void InstantController::SetInstantEnabled(bool instant_enabled, instant_enabled_ = instant_enabled; use_local_preview_only_ = use_local_preview_only; HideInternal(); - loader_.reset(); + overlay_.reset(); if (extended_enabled_ || instant_enabled_) - EnsureLoaderIsCurrent(false); + EnsureOverlayIsCurrent(false); + if (extended_enabled_) + ResetNTP(); if (instant_tab_) instant_tab_->SetDisplayInstantResults(instant_enabled_); } @@ -755,16 +781,159 @@ void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) { if (!extended_enabled_) return; - if (loader_) - loader_->SendThemeBackgroundInfo(theme_info); + if (overlay_) + overlay_->SendThemeBackgroundInfo(theme_info); + if (ntp_) + ntp_->SendThemeBackgroundInfo(theme_info); + if (instant_tab_) + instant_tab_->SendThemeBackgroundInfo(theme_info); } void InstantController::ThemeAreaHeightChanged(int height) { if (!extended_enabled_) return; - if (loader_) - loader_->SendThemeAreaHeight(height); + if (overlay_) + overlay_->SendThemeAreaHeight(height); + if (ntp_) + ntp_->SendThemeAreaHeight(height); + if (instant_tab_) + instant_tab_->SendThemeAreaHeight(height); +} + +void InstantController::SwappedOverlayContents() { + model_.SetPreviewContents(GetPreviewContents()); +} + +void InstantController::FocusedOverlayContents() { +#if defined(USE_AURA) + // On aura the omnibox only receives a focus lost if we initiate the focus + // change. This does that. + if (!model_.mode().is_default()) + browser_->InstantPreviewFocused(); +#endif +} + +void InstantController::ReloadOverlayIfStale() { + // The local popup is never stale. + if (overlay_ && overlay_->IsUsingLocalPreview()) + return; + + // If the preview is showing or the omnibox has focus, don't delete the + // overlay. It will get refreshed the next time the preview is hidden or the + // omnibox loses focus. + if ((!overlay_ || overlay_->is_stale()) && + omnibox_focus_state_ == OMNIBOX_FOCUS_NONE && + model_.mode().is_default()) { + overlay_.reset(); + EnsureOverlayIsCurrent(false); + } +} + +void InstantController::LogDebugEvent(const std::string& info) const { + DVLOG(1) << info; + + debug_events_.push_front(std::make_pair( + base::Time::Now().ToInternalValue(), info)); + static const size_t kMaxDebugEventSize = 2000; + if (debug_events_.size() > kMaxDebugEventSize) + debug_events_.pop_back(); +} + +// TODO(shishir): We assume that the WebContent's current RenderViewHost is the +// RenderViewHost being created which is not always true. Fix this. +void InstantController::InstantPageRenderViewCreated( + const content::WebContents* contents) { + if (!extended_enabled_) + return; + + // Update theme info so that the page picks it up. + browser_->UpdateThemeInfoForPreview(); + + // Ensure the searchbox API has the correct initial state. + if (IsContentsFrom(overlay(), contents)) { + overlay_->SetDisplayInstantResults(instant_enabled_); + overlay_->SearchModeChanged(search_mode_); + overlay_->KeyCaptureChanged( + omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE); + overlay_->SetMarginSize(start_margin_, end_margin_); + overlay_->InitializeFonts(); + } else if (IsContentsFrom(ntp(), contents)) { + ntp_->SetDisplayInstantResults(instant_enabled_); + ntp_->SetMarginSize(start_margin_, end_margin_); + ntp_->InitializeFonts(); + } else { + NOTREACHED(); + } +} + +void InstantController::InstantSupportDetermined( + const content::WebContents* contents, + bool supports_instant) { + if (IsContentsFrom(instant_tab(), contents)) { + if (!supports_instant) + MessageLoop::current()->DeleteSoon(FROM_HERE, instant_tab_.release()); + } else if (IsContentsFrom(ntp(), contents)) { + if (supports_instant) + RemoveFromBlacklist(ntp_->instant_url()); + else + BlacklistAndResetNTP(); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED, + content::Source<InstantController>(this), + content::NotificationService::NoDetails()); + + } else if (IsContentsFrom(overlay(), contents)) { + if (supports_instant) + RemoveFromBlacklist(overlay_->instant_url()); + else + BlacklistAndResetOverlay(); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_INSTANT_OVERLAY_SUPPORT_DETERMINED, + content::Source<InstantController>(this), + content::NotificationService::NoDetails()); + } +} + +void InstantController::InstantPageRenderViewGone( + const content::WebContents* contents) { + if (IsContentsFrom(overlay(), contents)) + BlacklistAndResetOverlay(); + else if (IsContentsFrom(ntp(), contents)) + BlacklistAndResetNTP(); + else + NOTREACHED(); +} + +void InstantController::InstantPageAboutToNavigateMainFrame( + const content::WebContents* contents, + const GURL& url) { + DCHECK(IsContentsFrom(overlay(), contents)); + + // If the page does not yet support instant, we allow redirects and other + // navigations to go through since the instant URL can redirect - e.g. to + // country specific pages. + if (!overlay_->supports_instant()) + return; + + GURL instant_url(overlay_->instant_url()); + + // If we are navigating to the instant URL, do nothing. + if (url == instant_url) + return; + + // Commit the navigation if either: + // - The page is in NTP mode (so it could only navigate on a user click) or + // - The page is not in NTP mode and we are navigating to a URL with a + // different host or path than the instant URL. This enables the instant + // page when it is showing search results to change the query parameters + // and fragments of the URL without it navigating. + if (model_.mode().is_ntp() || + (url.host() != instant_url.host() || url.path() != instant_url.path())) { + CommitIfPossible(INSTANT_COMMIT_NAVIGATED); + } } void InstantController::SetSuggestions( @@ -773,13 +942,13 @@ void InstantController::SetSuggestions( LOG_INSTANT_DEBUG_EVENT(this, "SetSuggestions"); // Ignore if the message is from an unexpected source. - if (instant_tab_) { - if (instant_tab_->contents() != contents) - return; - } else if (!loader_ || loader_->contents() != contents || - !allow_preview_to_show_search_suggestions_) { + if (IsContentsFrom(ntp(), contents)) + return; + if (instant_tab_ && !IsContentsFrom(instant_tab(), contents)) + return; + if (IsContentsFrom(overlay(), contents) && + !allow_preview_to_show_search_suggestions_) return; - } InstantSuggestion suggestion; if (!suggestions.empty()) @@ -860,122 +1029,48 @@ void InstantController::SetSuggestions( } } - // Extended mode pages will call ShowLoader() when they are ready. + // Extended mode pages will call ShowOverlay() when they are ready. if (!extended_enabled_) - ShowLoader(INSTANT_SHOWN_QUERY_SUGGESTIONS, 100, INSTANT_SIZE_PERCENT); + ShowOverlay(INSTANT_SHOWN_QUERY_SUGGESTIONS, 100, INSTANT_SIZE_PERCENT); } -void InstantController::InstantSupportDetermined( - const content::WebContents* contents, - bool supports_instant) { - LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( - "InstantSupportDetermined: supports_instant=%d", supports_instant)); - - if (instant_tab_ && instant_tab_->contents() == contents) { - if (!supports_instant) - MessageLoop::current()->DeleteSoon(FROM_HERE, instant_tab_.release()); - return; - } - - if (loader_ && loader_->contents() == contents) { - if (supports_instant) { - if (blacklisted_urls_.erase(loader_->instant_url())) { - RecordEventHistogram( - INSTANT_CONTROLLER_EVENT_URL_REMOVED_FROM_BLACKLIST); - } - } else { - ++blacklisted_urls_[loader_->instant_url()]; - RecordEventHistogram(INSTANT_CONTROLLER_EVENT_URL_ADDED_TO_BLACKLIST); - HideInternal(); - delete loader_->ReleaseContents(); - MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); - EnsureLoaderIsCurrent(false); - } - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, - content::Source<InstantController>(this), - content::NotificationService::NoDetails()); - } -} - -void InstantController::ShowInstantPreview(InstantShownReason reason, +void InstantController::ShowInstantPreview(const content::WebContents* contents, + InstantShownReason reason, int height, InstantSizeUnits units) { - if (extended_enabled_) - ShowLoader(reason, height, units); -} - -void InstantController::StartCapturingKeyStrokes() { - // Ignore unless the loader is active and on the NTP. - if (extended_enabled_ && !instant_tab_ && model_.mode().is_ntp()) - browser_->FocusOmniboxInvisibly(); -} - -void InstantController::StopCapturingKeyStrokes() { - // Ignore unless the loader is active and on the NTP, and the omnibox has - // invisible focus. - if (extended_enabled_ && !instant_tab_ && model_.mode().is_ntp() && - omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE) - loader_->contents()->Focus(); + if (extended_enabled_ && IsContentsFrom(overlay(), contents)) + ShowOverlay(reason, height, units); } -void InstantController::SwappedWebContents() { - model_.SetPreviewContents(GetPreviewContents()); -} - -void InstantController::InstantLoaderContentsFocused() { -#if defined(USE_AURA) - // On aura the omnibox only receives a focus lost if we initiate the focus - // change. This does that. - if (!model_.mode().is_default()) - browser_->InstantPreviewFocused(); -#endif -} - -void InstantController::InstantLoaderRenderViewGone() { - ++blacklisted_urls_[loader_->instant_url()]; - HideInternal(); - delete loader_->ReleaseContents(); - // Delay deletion as we have gotten here from an InstantLoader method. - MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); - EnsureLoaderIsCurrent(false); -} - -void InstantController::InstantLoaderAboutToNavigateMainFrame(const GURL& url) { - // If the page does not yet support instant, we allow redirects and other - // navigations to go through since the instant URL can redirect - e.g. to - // country specific pages. - if (!loader_->supports_instant()) +void InstantController::StartCapturingKeyStrokes( + const content::WebContents* contents) { + if (!extended_enabled_) return; - GURL instant_url(loader_->instant_url()); + DCHECK(IsContentsFrom(instant_tab(), contents)); + browser_->FocusOmniboxInvisibly(); +} - // If we are navigating to the instant URL, do nothing. - if (url == instant_url) +void InstantController::StopCapturingKeyStrokes( + content::WebContents* contents) { + // Nothing to do if omnibox doesn't have invisible focus. + if (!extended_enabled_ || omnibox_focus_state_ != OMNIBOX_FOCUS_INVISIBLE) return; - // Commit the navigation if either: - // - The page is in NTP mode (so it could only navigate on a user click) or - // - The page is not in NTP mode and we are navigating to a URL with a - // different host or path than the instant URL. This enables the instant - // page when it is showing search results to change the query parameters - // and fragments of the URL without it navigating. - if (model_.mode().is_ntp() || - (url.host() != instant_url.host() || url.path() != instant_url.path())) { - CommitIfPossible(INSTANT_COMMIT_NAVIGATED); - } + DCHECK(IsContentsFrom(instant_tab(), contents)); + contents->Focus(); } -void InstantController::InstantLoaderRenderViewCreated() { +void InstantController::NavigateToURL(const content::WebContents* contents, + const GURL& url, + content::PageTransition transition) { + // TODO(samarth): handle case where contents are no longer "active" (e.g. user + // has switched tabs). if (!extended_enabled_) return; - - // Ensure the searchbox API has the correct initial state. - loader_->SetDisplayInstantResults(instant_enabled_); - loader_->SearchModeChanged(search_mode_); - loader_->KeyCaptureChanged(omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE); - loader_->SetMarginSize(start_margin_, end_margin_); - loader_->InitializeFonts(); + if (overlay_) + HideOverlay(); + browser_->OpenURLInCurrentTab(url, transition); } void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { @@ -986,8 +1081,8 @@ void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { // If the preview is not showing at all, recreate it if it's stale. if (model_.mode().is_default()) { - OnStaleLoader(); - MaybeSwitchToRemoteLoader(); + ReloadOverlayIfStale(); + MaybeSwitchToRemoteOverlay(); return; } @@ -998,47 +1093,39 @@ void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { #if defined(OS_MACOSX) // TODO(sreeram): See if Mac really needs this special treatment. - if (!loader_->is_pointer_down_from_activate()) - HideLoader(); + if (!overlay_->is_pointer_down_from_activate()) + HideOverlay(); #else if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), - loader_->contents())) + overlay_->contents())) CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST); else - HideLoader(); + HideOverlay(); #endif } -void InstantController::NavigateToURL(const GURL& url, - content::PageTransition transition) { - LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( - "NavigateToURL: url='%s'", url.spec().c_str())); - - if (!extended_enabled_) +void InstantController::ResetNTP() { + ntp_.reset(); + std::string instant_url; + if (!GetInstantURL(browser_->profile(), false, &instant_url)) return; - if (loader_) - HideLoader(); - browser_->OpenURLInCurrentTab(url, transition); -} - -void InstantController::LogDebugEvent(const std::string& info) const { - DVLOG(1) << info; - debug_events_.push_front(std::make_pair( - base::Time::Now().ToInternalValue(), info)); - static const size_t kMaxDebugEventSize = 2000; - if (debug_events_.size() > kMaxDebugEventSize) - debug_events_.pop_back(); + ntp_.reset(new InstantNTP(this, instant_url)); + ntp_->InitContents(browser_->profile(), browser_->GetActiveWebContents(), + base::Bind(&InstantController::ResetNTP, + base::Unretained(this))); } -bool InstantController::EnsureLoaderIsCurrent(bool ignore_blacklist) { +bool InstantController::EnsureOverlayIsCurrent(bool ignore_blacklist) { // If there's no active tab, the browser is closing. const content::WebContents* active_tab = browser_->GetActiveWebContents(); if (!active_tab) return false; + Profile* profile = Profile::FromBrowserContext( + active_tab->GetBrowserContext()); std::string instant_url; - if (!GetInstantURL(active_tab, ignore_blacklist, &instant_url)) { + if (!GetInstantURL(profile, ignore_blacklist, &instant_url)) { // If we are in extended mode, fallback to the local popup. if (extended_enabled_) instant_url = kLocalOmniboxPopupURL; @@ -1046,79 +1133,58 @@ bool InstantController::EnsureLoaderIsCurrent(bool ignore_blacklist) { return false; } - if (!loader_ || loader_->instant_url() != instant_url) - CreateLoader(instant_url, active_tab); + if (!overlay_ || overlay_->instant_url() != instant_url) + CreateOverlay(instant_url, active_tab); return true; } -void InstantController::CreateLoader(const std::string& instant_url, - const content::WebContents* active_tab) { - // Update theme info so that the loader picks up the correct fonts. - if (extended_enabled_) - browser_->UpdateThemeInfoForPreview(); - +void InstantController::CreateOverlay(const std::string& instant_url, + const content::WebContents* active_tab) { HideInternal(); - loader_.reset(new InstantLoader(this, instant_url)); - loader_->InitContents(active_tab); + overlay_.reset(new InstantOverlay(this, instant_url)); + overlay_->InitContents(browser_->profile(), active_tab); LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( - "CreateLoader: instant_url='%s'", instant_url.c_str())); - - // Restart the stale loader timer. - stale_loader_timer_.Start(FROM_HERE, - base::TimeDelta::FromMilliseconds(kStaleLoaderTimeoutMS), this, - &InstantController::OnStaleLoader); -} - -void InstantController::OnStaleLoader() { - // The local popup is never stale. - if (loader_ && loader_->IsUsingLocalPreview()) - return; - - // 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() && - omnibox_focus_state_ == OMNIBOX_FOCUS_NONE && - model_.mode().is_default()) { - loader_.reset(); - EnsureLoaderIsCurrent(false); - } + "CreateOverlay: instant_url='%s'", instant_url.c_str())); } -void InstantController::MaybeSwitchToRemoteLoader() { - if (!loader_ || omnibox_focus_state_ != OMNIBOX_FOCUS_NONE || +void InstantController::MaybeSwitchToRemoteOverlay() { + if (!overlay_ || omnibox_focus_state_ != OMNIBOX_FOCUS_NONE || !model_.mode().is_default()) { return; } - EnsureLoaderIsCurrent(false); + EnsureOverlayIsCurrent(false); } void InstantController::ResetInstantTab() { // Do not wire up the InstantTab if instant should only use local previews, to // prevent it from sending data to the page. - if (search_mode_.is_origin_search() && !use_local_preview_only_) { + if (!search_mode_.is_origin_default() && !use_local_preview_only_) { content::WebContents* active_tab = browser_->GetActiveWebContents(); if (!instant_tab_ || active_tab != instant_tab_->contents()) { - instant_tab_.reset(new InstantTab(this, active_tab)); - instant_tab_->Init(); + instant_tab_.reset(new InstantTab(this)); + instant_tab_->Init(active_tab); + // Update theme info for this tab. + browser_->UpdateThemeInfoForPreview(); instant_tab_->SetDisplayInstantResults(instant_enabled_); instant_tab_->SetMarginSize(start_margin_, end_margin_); instant_tab_->InitializeFonts(); + instant_tab_->KeyCaptureChanged( + omnibox_focus_state_ == OMNIBOX_FOCUS_INVISIBLE); } - // Hide the |loader_| since we are now using |instant_tab_| instead. - HideLoader(); + // Hide the |overlay_| since we are now using |instant_tab_| instead. + HideOverlay(); } else { instant_tab_.reset(); } } -void InstantController::HideLoader() { +void InstantController::HideOverlay() { HideInternal(); - OnStaleLoader(); - MaybeSwitchToRemoteLoader(); + ReloadOverlayIfStale(); + MaybeSwitchToRemoteOverlay(); } void InstantController::HideInternal() { @@ -1133,35 +1199,36 @@ void InstantController::HideInternal() { allow_preview_to_show_search_suggestions_ = false; // Send a message asking the preview to clear out old results. - loader_->Update(string16(), 0, 0, true); + overlay_->Update(string16(), 0, 0, true); } // Clear the first interaction timestamp for later use. first_interaction_time_ = base::Time(); } -void InstantController::ShowLoader(InstantShownReason reason, - int height, - InstantSizeUnits units) { - // If we are on a committed search results page, the |loader_| is not in use. +void InstantController::ShowOverlay(InstantShownReason reason, + int height, + InstantSizeUnits units) { + // If we are on a committed search results page, the |overlay_| is not in use. if (instant_tab_) return; LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( "Show: reason=%d height=%d units=%d", reason, height, units)); - // Must be on NTP to show NTP content. - if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp()) + // INSTANT_SHOWN_CUSTOM_NTP_CONTENT is no longer supported. + // TODO(samarth): remove once the server has been updated. + if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT) return; - // Must have updated omnibox after the last HideLoader() to show suggestions. + // Must have updated omnibox after the last HideOverlay() to show suggestions. if ((reason == INSTANT_SHOWN_QUERY_SUGGESTIONS || reason == INSTANT_SHOWN_CLICKED_QUERY_SUGGESTION) && !allow_preview_to_show_search_suggestions_) return; // The page is trying to hide itself. Hide explicitly (i.e., don't use - // HideLoader()) so that it can change its mind. + // HideOverlay()) so that it can change its mind. if (height == 0) { model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); return; @@ -1180,7 +1247,7 @@ void InstantController::ShowLoader(InstantShownReason reason, // - The page wants to show custom NTP content. // - The page is over a website other than search or an NTP, and is not // already showing at 100% height. - if (loader_->IsUsingLocalPreview() || !instant_enabled_ || + if (overlay_->IsUsingLocalPreview() || !instant_enabled_ || reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT || (search_mode_.is_origin_default() && !IsFullHeight(model_))) model_.SetPreviewState(search_mode_, height, units); @@ -1196,8 +1263,8 @@ void InstantController::ShowLoader(InstantShownReason reason, } void InstantController::SendPopupBoundsToPage() { - if (last_popup_bounds_ == popup_bounds_ || !loader_ || - loader_->is_pointer_down_from_activate()) + if (last_popup_bounds_ == popup_bounds_ || !overlay_ || + overlay_->is_pointer_down_from_activate()) return; last_popup_bounds_ = popup_bounds_; @@ -1219,13 +1286,13 @@ void InstantController::SendPopupBoundsToPage() { DCHECK_LE(0, intersection.width()); DCHECK_LE(0, intersection.height()); - loader_->SetPopupBounds(intersection); + overlay_->SetPopupBounds(intersection); } -bool InstantController::GetInstantURL(const content::WebContents* active_tab, +bool InstantController::GetInstantURL(Profile* profile, bool ignore_blacklist, std::string* instant_url) const { - DCHECK(active_tab); + DCHECK(profile); instant_url->clear(); if (extended_enabled_ && use_local_preview_only_) { @@ -1234,8 +1301,7 @@ bool InstantController::GetInstantURL(const content::WebContents* active_tab, } const TemplateURL* template_url = TemplateURLServiceFactory::GetForProfile( - Profile::FromBrowserContext(active_tab->GetBrowserContext()))-> - GetDefaultSearchProvider(); + profile)->GetDefaultSearchProvider(); if (!template_url) { LOG_INSTANT_DEBUG_EVENT(this, "GetInstantURL: No template URL"); @@ -1316,3 +1382,26 @@ bool InstantController::GetInstantURL(const content::WebContents* active_tab, return true; } + +void InstantController::BlacklistAndResetNTP() { + ++blacklisted_urls_[ntp_->instant_url()]; + RecordEventHistogram(INSTANT_CONTROLLER_EVENT_URL_ADDED_TO_BLACKLIST); + delete ntp_->ReleaseContents().release(); + MessageLoop::current()->DeleteSoon(FROM_HERE, ntp_.release()); + ResetNTP(); +} + +void InstantController::BlacklistAndResetOverlay() { + ++blacklisted_urls_[overlay_->instant_url()]; + RecordEventHistogram(INSTANT_CONTROLLER_EVENT_URL_ADDED_TO_BLACKLIST); + HideInternal(); + delete overlay_->ReleaseContents().release(); + MessageLoop::current()->DeleteSoon(FROM_HERE, overlay_.release()); + EnsureOverlayIsCurrent(false); +} + +void InstantController::RemoveFromBlacklist(const std::string& url) { + if (blacklisted_urls_.erase(url)) { + RecordEventHistogram(INSTANT_CONTROLLER_EVENT_URL_REMOVED_FROM_BLACKLIST); + } +} diff --git a/chrome/browser/instant/instant_controller.h b/chrome/browser/instant/instant_controller.h index 40bbfc8..1a08425 100644 --- a/chrome/browser/instant/instant_controller.h +++ b/chrome/browser/instant/instant_controller.h @@ -19,6 +19,7 @@ #include "base/timer.h" #include "chrome/browser/instant/instant_commit_type.h" #include "chrome/browser/instant/instant_model.h" +#include "chrome/browser/instant/instant_page.h" #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" #include "chrome/common/instant_types.h" #include "chrome/common/search_types.h" @@ -29,7 +30,8 @@ struct AutocompleteMatch; class AutocompleteProvider; -class InstantLoader; +class InstantNTP; +class InstantOverlay; class InstantTab; class TemplateURL; @@ -45,17 +47,32 @@ class WebContents; #define LOG_INSTANT_DEBUG_EVENT(controller, message) \ controller->LogDebugEvent(message) -// InstantController maintains a WebContents that is intended to give a preview -// of search suggestions and results. InstantController is owned by Browser via -// BrowserInstantController. -class InstantController { +// InstantController drives Chrome Instant, i.e., the browser implementation of +// the Embedded Search API (see http://dev.chromium.org/embeddedsearch). +// +// In extended mode, InstantController maintains and coordinates three +// instances of InstantPage: +// (1) An InstantOverlay instance that is used to show search suggestions and +// results in an overlay over a non-search page. +// (2) An InstantNTP instance which is a preloaded sarch page that will be +// swapped-in the next time the user navigates to the New Tab Page. It is +// never shown to the user in an uncommitted state. +// (3) An InstantTab instance which points to the currently active tab, if it +// supports the Embedded Search API. +// +// All three are backed by a WebContents. InstantOverlay and InstantNTP own +// their corresponding WebContents; InstantTab does not. In non-extended mode, +// only an InstantOverlay instance is kept. +// +// InstantController is owned by Browser via BrowserInstantController. +class InstantController : public InstantPage::Delegate { public: // The URL for the local omnibox popup. static const char* kLocalOmniboxPopupURL; InstantController(chrome::BrowserInstantController* browser, bool extended_enabled); - ~InstantController(); + virtual ~InstantController(); // Invoked as the user types into the omnibox. |user_text| is what the user // has typed. |full_text| is what the omnibox is showing. These may differ if @@ -75,6 +92,10 @@ class InstantController { bool escape_pressed, bool is_keyword_search); + // Releases and returns the NTP WebContents. May be NULL. Loads a new + // WebContents for the NTP. + scoped_ptr<content::WebContents> ReleaseNTPContents() WARN_UNUSED_RESULT; + // Sets the bounds of the omnibox popup, in screen coordinates. void SetPopupBounds(const gfx::Rect& bounds); @@ -108,7 +129,7 @@ class InstantController { 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 + // the overlay which will notify the renderer. Create |instant_tab_| if the // |new_mode| reflects an Instant search results page. void SearchModeChanged(const chrome::search::Mode& old_mode, const chrome::search::Mode& new_mode); @@ -131,58 +152,16 @@ class InstantController { // The theme area height has changed. Pass the message to the preview page. void ThemeAreaHeightChanged(int height); - // Returns the transition type of the last AutocompleteMatch passed to Update. - content::PageTransition last_transition_type() const { - return last_transition_type_; - } - - // Non-const for Add/RemoveObserver only. Other model changes should only - // happen through the InstantController interface. - InstantModel* model() { return &model_; } - - // Invoked by the page when it has suggested text. - void SetSuggestions(const content::WebContents* contents, - const std::vector<InstantSuggestion>& suggestions); + // Called when someone else swapped in a different contents in the |overlay_|. + void SwappedOverlayContents(); - // Invoked by the page when its support for the Instant API is determined. - void InstantSupportDetermined(const content::WebContents* contents, - bool supports_instant); - - // Invoked by InstantLoader to request that the preview be shown. - void ShowInstantPreview(InstantShownReason reason, - int height, - InstantSizeUnits units); - - // Invoked by InstantLoader to request the browser to start capturing user key - // strokes. - void StartCapturingKeyStrokes(); - - // Invoked by InstantLoader to request the browser to stop capturing user key - // strokes. - void StopCapturingKeyStrokes(); - - // Invoked by InstantLoader when it has swapped a different WebContents into - // the preview, usually because a prerendered page was navigated to. - void SwappedWebContents(); - - // Invoked by InstantLoader when the preview gains focus, usually due to the - // user clicking on it. - void InstantLoaderContentsFocused(); - - // Invoked by the InstantLoader when its RenderView crashes. - void InstantLoaderRenderViewGone(); - - // Invoked by InstantLoader when the instant page is about to navigate. - void InstantLoaderAboutToNavigateMainFrame(const GURL& url); - - // Invoked by InstantLoader when it's underlying RenderView is created. - // TODO(shishir): We assume that the WebContent's current RenderViewHost is - // the RenderViewHost being created which is not always true. Fix this. - void InstantLoaderRenderViewCreated(); + // Called when contents for |overlay_| received focus. + void FocusedOverlayContents(); - // Invoked by the InstantLoader when the instant page wants to navigate to - // the speicfied URL. - void NavigateToURL(const GURL& url, content::PageTransition transition); + // Called when the |overlay_| might be stale. If it's actually stale, and the + // omnibox doesn't have focus, and the preview isn't showing, the |overlay_| + // is deleted and recreated. Else the refresh is skipped. + void ReloadOverlayIfStale(); // Adds a new event to |debug_events_| and also DVLOG's it. Ensures that // |debug_events_| doesn't get too large. @@ -193,39 +172,79 @@ class InstantController { return debug_events_; } + // Returns the transition type of the last AutocompleteMatch passed to Update. + content::PageTransition last_transition_type() const { + return last_transition_type_; + } + + // Non-const for Add/RemoveObserver only. Other model changes should only + // happen through the InstantController interface. + InstantModel* model() { return &model_; } + private: FRIEND_TEST_ALL_PREFIXES(InstantTest, OmniboxFocusLoadsInstant); FRIEND_TEST_ALL_PREFIXES(InstantTest, SetWithTemplateURL); FRIEND_TEST_ALL_PREFIXES(InstantTest, NonInstantSearchProvider); - FRIEND_TEST_ALL_PREFIXES(InstantTest, InstantLoaderRefresh); + FRIEND_TEST_ALL_PREFIXES(InstantTest, InstantOverlayRefresh); FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ExtendedModeIsOn); FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, OmniboxFocusLoadsInstant); + FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, NTPIsPreloaded); + FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, PreloadedNTPIsUsedInNewTab); + FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, PreloadedNTPIsUsedInSameTab); + FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation); + + // Overridden from InstantPage::Delegate: + // TODO(shishir): We assume that the WebContent's current RenderViewHost is + // the RenderViewHost being created which is not always true. Fix this. + virtual void InstantPageRenderViewCreated( + const content::WebContents* contents) OVERRIDE; + virtual void InstantSupportDetermined( + const content::WebContents* contents, + bool supports_instant) OVERRIDE; + virtual void InstantPageRenderViewGone( + const content::WebContents* contents) OVERRIDE; + virtual void InstantPageAboutToNavigateMainFrame( + const content::WebContents* contents, + const GURL& url) OVERRIDE; + virtual void SetSuggestions( + const content::WebContents* contents, + const std::vector<InstantSuggestion>& suggestions) OVERRIDE; + virtual void ShowInstantPreview( + const content::WebContents* contents, + InstantShownReason reason, + int height, + InstantSizeUnits units) OVERRIDE; + virtual void StartCapturingKeyStrokes( + const content::WebContents* contents) OVERRIDE; + virtual void StopCapturingKeyStrokes(content::WebContents* contents) OVERRIDE; + virtual void NavigateToURL( + const content::WebContents* contents, + const GURL& url, + content::PageTransition transition) OVERRIDE; // Helper for OmniboxFocusChanged. Commit or discard the preview. void OmniboxLostFocus(gfx::NativeView view_gaining_focus); - // Ensures that |loader_| uses the Instant URL returned by GetInstantURL(), - // creating a new loader if necessary. In extended mode, will fallback to + // Creates a new NTP, using the instant_url property of the default + // TemplateURL. + void ResetNTP(); + + // Ensures that |overlay_| uses the Instant URL returned by GetInstantURL(), + // creating a new overlay if necessary. In extended mode, will fallback to // using the kLocalOmniboxPopupURL as the Instant URL in case GetInstantURL() // returns false. Returns true if an Instant URL could be determined. // For |ignore_blacklist| look at comments in |GetInstantURL|. - bool EnsureLoaderIsCurrent(bool ignore_blacklist); + bool EnsureOverlayIsCurrent(bool ignore_blacklist); - // Recreates the |loader_| with the input |instant_url|. The caller should - // ensure that the |loader_| is not already on the stack since it is deleted - // in this call. - void CreateLoader(const std::string& instant_url, - const content::WebContents* active_tab); + // Recreates the |overlay_| with |instant_url|. Note that |overlay_| is + // deleted in this call. + void CreateOverlay(const std::string& instant_url, + const content::WebContents* active_tab); - // Called when the |loader_| might be stale. If it's actually stale, and the - // omnibox doesn't have focus, and the preview isn't showing, the |loader_| is - // deleted and recreated. Else the refresh is skipped. - void OnStaleLoader(); - - // If the |loader_| being used is in fallback mode, it will be switched back - // to the remote loader if the loader is not showing and the omnibox does not - // have focus. - void MaybeSwitchToRemoteLoader(); + // If the |overlay_| being used is in fallback mode, it will be switched back + // to the remote overlay if the overlay is not showing and the omnibox does + // not have focus. + void MaybeSwitchToRemoteOverlay(); // If the active tab is an Instant search results page, sets |instant_tab_| to // point to it. Else, deletes any existing |instant_tab_|. @@ -233,17 +252,17 @@ class InstantController { // Hide the preview. Also sends an onchange event (with blank query) to the // preview, telling it to clear out results for any old queries. - void HideLoader(); + void HideOverlay(); - // Like HideLoader(), but doesn't call OnStaleLoader(). Use HideLoader() - // unless you are going to call loader_.reset() yourself subsequently. + // Like HideOverlay(), but doesn't call OnStaleOverlay(). Use HideOverlay() + // unless you are going to call overlay_.reset() yourself subsequently. void HideInternal(); - // Counterpart to HideLoader(). Asks the |browser_| to display the preview - // with the given |height|. - void ShowLoader(InstantShownReason reason, - int height, - InstantSizeUnits units); + // Counterpart to HideOverlay(). Asks the |browser_| to display the preview + // with the given |height| in |units|. + void ShowOverlay(InstantShownReason reason, + int height, + InstantSizeUnits units); // Send the omnibox popup bounds to the page. void SendPopupBoundsToPage(); @@ -261,10 +280,22 @@ class InstantController { // the blacklist. // // Returns true if a valid Instant URL could be found that is not blacklisted. - bool GetInstantURL(const content::WebContents* active_tab, + bool GetInstantURL(Profile* profile, bool ignore_blacklist, std::string* instant_url) const; + // Adds the URL for the page to the blacklist. Deletes the contents held and + // recreates a new page. + void BlacklistAndResetOverlay(); + void BlacklistAndResetNTP(); + + // Removes |url| from the blacklist. + void RemoveFromBlacklist(const std::string& url); + + InstantOverlay* overlay() const { return overlay_.get(); } + InstantTab* instant_tab() const { return instant_tab_.get(); } + InstantNTP* ntp() const { return ntp_.get(); } + chrome::BrowserInstantController* const browser_; // Whether the extended API and regular API are enabled. If both are false, @@ -275,19 +306,22 @@ class InstantController { // If true, the instant URL is set to kLocalOmniboxPopupURL. bool use_local_preview_only_; - // The state of the preview page, i.e., the page owned by |loader_|. Ignored + // The state of the preview page, i.e., the page owned by |overlay_|. Ignored // if |instant_tab_| is in use. InstantModel model_; - // The preview WebContents. - scoped_ptr<InstantLoader> loader_; - - // A committed WebContents that supports Instant. If non-NULL, the |loader_| - // is guaranteed to be hidden and messages will be sent to this instead. + // The three instances of InstantPage maintained by InstantController as + // described above. All three may be non-NULL in extended mode. If + // |instant_tab_| is not NULL, then |overlay_| is guaranteed to be hidden and + // messages will be sent to |instant_tab_| instead. + // + // In non-extended mode, only |overlay_| is ever non-NULL. + scoped_ptr<InstantOverlay> overlay_; + scoped_ptr<InstantNTP> ntp_; scoped_ptr<InstantTab> instant_tab_; // The most recent full_text passed to Update(). If empty, we'll not accept - // search suggestions from |loader_| or |instant_tab_|. + // search suggestions from |overlay_| or |instant_tab_|. string16 last_omnibox_text_; // True if the last Update() had an inline autocompletion. Used only to make @@ -330,9 +364,6 @@ class InstantController { // Timer used to update the bounds of the omnibox popup. base::OneShotTimer<InstantController> update_bounds_timer_; - // Timer used to ensure that the Instant page does not get too stale. - base::OneShotTimer<InstantController> stale_loader_timer_; - // For each key K => value N, the map says that we found that the search // engine identified by Instant URL K didn't support the Instant API, or // caused RenderView crashes in each of the last N times that we loaded it. diff --git a/chrome/browser/instant/instant_extended_browsertest.cc b/chrome/browser/instant/instant_extended_browsertest.cc index 6defb4b..33195fc 100644 --- a/chrome/browser/instant/instant_extended_browsertest.cc +++ b/chrome/browser/instant/instant_extended_browsertest.cc @@ -3,11 +3,20 @@ // found in the LICENSE file. #include "chrome/browser/instant/instant_commit_type.h" -#include "chrome/browser/instant/instant_loader.h" +#include "chrome/browser/instant/instant_ntp.h" +#include "chrome/browser/instant/instant_overlay.h" +#include "chrome/browser/instant/instant_service.h" +#include "chrome/browser/instant/instant_service_factory.h" #include "chrome/browser/instant/instant_test_utils.h" #include "chrome/browser/ui/search/search.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/url_constants.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" class InstantExtendedTest : public InstantTestBase { protected: @@ -18,6 +27,18 @@ class InstantExtendedTest : public InstantTestBase { GetURL("files/instant_extended.html?strk=1&"); } + void FocusOmniboxAndWaitForInstantSupport() { + content::WindowedNotificationObserver ntp_observer( + chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED, + content::NotificationService::AllSources()); + content::WindowedNotificationObserver overlay_observer( + chrome::NOTIFICATION_INSTANT_OVERLAY_SUPPORT_DETERMINED, + content::NotificationService::AllSources()); + FocusOmnibox(); + ntp_observer.Wait(); + overlay_observer.Wait(); + } + std::string GetOmniboxText() { return UTF16ToUTF8(omnibox()->GetText()); } @@ -52,7 +73,7 @@ IN_PROC_BROWSER_TEST_F(InstantExtendedTest, OmniboxFocusLoadsInstant) { EXPECT_FALSE(omnibox()->model()->has_focus()); // Delete any existing preview. - instant()->loader_.reset(); + instant()->overlay_.reset(); EXPECT_FALSE(instant()->GetPreviewContents()); // Refocus the omnibox. The InstantController should've preloaded Instant. @@ -65,7 +86,7 @@ IN_PROC_BROWSER_TEST_F(InstantExtendedTest, OmniboxFocusLoadsInstant) { EXPECT_TRUE(preview_tab); // Check that the page supports Instant, but it isn't showing. - EXPECT_TRUE(instant()->loader_->supports_instant()); + EXPECT_TRUE(instant()->overlay_->supports_instant()); EXPECT_FALSE(instant()->IsPreviewingSearchResults()); EXPECT_TRUE(instant()->model()->mode().is_default()); @@ -168,3 +189,155 @@ IN_PROC_BROWSER_TEST_F(InstantExtendedTest, NavigateSuggestionsWithArrowKeys) { SendUpArrow(); EXPECT_EQ("hello", GetOmniboxText()); } + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, NTPIsPreloaded) { + // Setup Instant. + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + FocusOmniboxAndWaitForInstantSupport(); + + // NTP contents should be preloaded. + ASSERT_NE(static_cast<InstantNTP*>(NULL), instant()->ntp()); + content::WebContents* ntp_contents = instant()->ntp_->contents(); + EXPECT_TRUE(ntp_contents); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, PreloadedNTPIsUsedInNewTab) { + // Setup Instant. + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + FocusOmniboxAndWaitForInstantSupport(); + + // NTP contents should be preloaded. + ASSERT_NE(static_cast<InstantNTP*>(NULL), instant()->ntp()); + content::WebContents* ntp_contents = instant()->ntp_->contents(); + EXPECT_TRUE(ntp_contents); + + // Open new tab. Preloaded NTP contents should have been used. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); + content::WebContents* active_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ(ntp_contents, active_tab); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, PreloadedNTPIsUsedInSameTab) { + // Setup Instant. + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + FocusOmniboxAndWaitForInstantSupport(); + + // NTP contents should be preloaded. + ASSERT_NE(static_cast<InstantNTP*>(NULL), instant()->ntp()); + content::WebContents* ntp_contents = instant()->ntp_->contents(); + EXPECT_TRUE(ntp_contents); + + // Open new tab. Preloaded NTP contents should have been used. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + content::WebContents* active_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ(ntp_contents, active_tab); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, OmniboxHasFocusOnNewTab) { + // Setup Instant. + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + FocusOmniboxAndWaitForInstantSupport(); + + // Explicitly unfocus the omnibox. + EXPECT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); + ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER); + EXPECT_FALSE(omnibox()->model()->has_focus()); + + // Open new tab. Preloaded NTP contents should have been used. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); + + // Omnibox should have focus. + EXPECT_TRUE(omnibox()->model()->has_focus()); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, OmniboxEmptyOnNewTabPage) { + // Setup Instant. + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + FocusOmniboxAndWaitForInstantSupport(); + + // Open new tab. Preloaded NTP contents should have been used. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + + // Omnibox should be empty. + EXPECT_TRUE(omnibox()->GetText().empty()); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, InputOnNTPDoesntShowOverlay) { + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + + // Focus omnibox and confirm overlay isn't shown. + FocusOmniboxAndWaitForInstantSupport(); + content::WebContents* preview_tab = instant()->GetPreviewContents(); + EXPECT_TRUE(preview_tab); + EXPECT_FALSE(instant()->IsPreviewingSearchResults()); + EXPECT_TRUE(instant()->model()->mode().is_default()); + + // Navigate to the NTP. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + + // Typing in the omnibox should not show the overlay. + SetOmniboxText("query"); + EXPECT_FALSE(instant()->IsPreviewingSearchResults()); + EXPECT_TRUE(instant()->model()->mode().is_default()); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedTest, ProcessIsolation) { + // Prior to setup no render process is dedicated to Instant. + InstantService* instant_service = + InstantServiceFactory::GetForProfile(browser()->profile()); + ASSERT_NE(static_cast<InstantService*>(NULL), instant_service); + EXPECT_EQ(0, instant_service->GetInstantProcessCount()); + + // Setup Instant. + ASSERT_NO_FATAL_FAILURE(SetupInstant()); + FocusOmniboxAndWaitForInstantSupport(); + + // Now there should be a registered Instant render process. + EXPECT_LT(0, instant_service->GetInstantProcessCount()); + + // And the Instant overlay and ntp should live inside it. + content::WebContents* preview = instant()->GetPreviewContents(); + EXPECT_TRUE(instant_service->IsInstantProcess( + preview->GetRenderProcessHost()->GetID())); + content::WebContents* ntp_contents = instant()->ntp_->contents(); + EXPECT_TRUE(instant_service->IsInstantProcess( + ntp_contents->GetRenderProcessHost()->GetID())); + + // Navigating to the NTP should use the Instant render process. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + content::WebContents* active_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_TRUE(instant_service->IsInstantProcess( + active_tab->GetRenderProcessHost()->GetID())); + + // Navigating elsewhere should not use the Instant render process. + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIAboutURL)); + EXPECT_FALSE(instant_service->IsInstantProcess( + active_tab->GetRenderProcessHost()->GetID())); +} diff --git a/chrome/browser/instant/instant_loader.cc b/chrome/browser/instant/instant_loader.cc index 18f4bdd..543a580 100644 --- a/chrome/browser/instant/instant_loader.cc +++ b/chrome/browser/instant/instant_loader.cc @@ -8,348 +8,73 @@ #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h" #include "chrome/browser/favicon/favicon_tab_helper.h" #include "chrome/browser/history/history_tab_helper.h" -#include "chrome/browser/instant/instant_controller.h" #include "chrome/browser/safe_browsing/safe_browsing_tab_observer.h" +#include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" #include "chrome/browser/ui/search/search_tab_helper.h" #include "chrome/browser/ui/tab_contents/core_tab_helper.h" -#include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" +#include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/site_instance.h" -#include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_view.h" -#include "ipc/ipc_message.h" namespace { -int kUserDataKey; +const int kStalePageTimeoutMS = 3 * 3600 * 1000; // 3 hours -class InstantLoaderUserData : public base::SupportsUserData::Data { - public: - explicit InstantLoaderUserData(InstantLoader* loader) : loader_(loader) {} +// This HTTP header and value are set on loads that originate from Instant. +const char kInstantHeader[] = "X-Purpose: Instant"; - InstantLoader* loader() const { return loader_; } +} // namespace - private: - virtual ~InstantLoaderUserData() {} - - InstantLoader* const loader_; - - DISALLOW_COPY_AND_ASSIGN(InstantLoaderUserData); -}; - -} - -// WebContentsDelegateImpl ----------------------------------------------------- - -class InstantLoader::WebContentsDelegateImpl - : public CoreTabHelperDelegate, - public content::WebContentsDelegate { - public: - explicit WebContentsDelegateImpl(InstantLoader* loader); - - private: - // Overridden from CoreTabHelperDelegate: - virtual void SwapTabContents(content::WebContents* old_contents, - content::WebContents* new_contents) OVERRIDE; - - // Overridden from content::WebContentsDelegate: - virtual bool ShouldSuppressDialogs() OVERRIDE; - virtual bool ShouldFocusPageAfterCrash() OVERRIDE; - virtual void LostCapture() OVERRIDE; - virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE; - virtual bool CanDownload(content::RenderViewHost* render_view_host, - int request_id, - const std::string& request_method) OVERRIDE; - virtual void HandleMouseDown() OVERRIDE; - virtual void HandleMouseUp() OVERRIDE; - virtual void HandlePointerActivate() OVERRIDE; - virtual void HandleGestureEnd() OVERRIDE; - virtual void DragEnded() OVERRIDE; - virtual bool OnGoToEntryOffset(int offset) OVERRIDE; - virtual content::WebContents* OpenURLFromTab( - content::WebContents* source, - const content::OpenURLParams& params) OVERRIDE; - - void MaybeCommitFromPointerRelease(); - - InstantLoader* const loader_; - - DISALLOW_COPY_AND_ASSIGN(WebContentsDelegateImpl); -}; - -InstantLoader::WebContentsDelegateImpl::WebContentsDelegateImpl( - InstantLoader* loader) - : loader_(loader) { -} - -void InstantLoader::WebContentsDelegateImpl::SwapTabContents( - content::WebContents* old_contents, - content::WebContents* new_contents) { - // If this is being called, something is swapping in to loader's |contents_| - // before we've added it to the tab strip. - loader_->ReplacePreviewContents(old_contents, new_contents); -} - -bool InstantLoader::WebContentsDelegateImpl::ShouldSuppressDialogs() { - // Any message shown during Instant cancels Instant, so we suppress them. - return true; +InstantLoader::Delegate::~Delegate() { } -bool InstantLoader::WebContentsDelegateImpl::ShouldFocusPageAfterCrash() { - return false; -} - -void InstantLoader::WebContentsDelegateImpl::LostCapture() { - MaybeCommitFromPointerRelease(); -} - -void InstantLoader::WebContentsDelegateImpl::WebContentsFocused( - content::WebContents* /* contents */) { - // The preview is getting focus. Equivalent to it being clicked. - bool tmp = loader_->is_pointer_down_from_activate_; - loader_->is_pointer_down_from_activate_ = true; - loader_->controller_->InstantLoaderContentsFocused(); - loader_->is_pointer_down_from_activate_ = tmp; -} - -bool InstantLoader::WebContentsDelegateImpl::CanDownload( - content::RenderViewHost* /* render_view_host */, - int /* request_id */, - const std::string& /* request_method */) { - // Downloads are disabled. - return false; -} - -void InstantLoader::WebContentsDelegateImpl::HandleMouseDown() { - loader_->is_pointer_down_from_activate_ = true; -} - -void InstantLoader::WebContentsDelegateImpl::HandleMouseUp() { - MaybeCommitFromPointerRelease(); -} - -void InstantLoader::WebContentsDelegateImpl::HandlePointerActivate() { - loader_->is_pointer_down_from_activate_ = true; -} - -void InstantLoader::WebContentsDelegateImpl::HandleGestureEnd() { - MaybeCommitFromPointerRelease(); -} - -void InstantLoader::WebContentsDelegateImpl::DragEnded() { - // If the user drags, we won't get a mouse up (at least on Linux). Commit the - // Instant result when the drag ends, so that during the drag the page won't - // move around. - MaybeCommitFromPointerRelease(); -} - -bool InstantLoader::WebContentsDelegateImpl::OnGoToEntryOffset(int offset) { - return false; -} - -content::WebContents* InstantLoader::WebContentsDelegateImpl::OpenURLFromTab( - content::WebContents* source, - const content::OpenURLParams& params) { - content::WebContents* preview = loader_->contents_.get(); - if (loader_->controller_->CommitIfPossible(INSTANT_COMMIT_NAVIGATED)) - return preview->GetDelegate()->OpenURLFromTab(source, params); - return NULL; -} - -void InstantLoader::WebContentsDelegateImpl::MaybeCommitFromPointerRelease() { - if (loader_->is_pointer_down_from_activate_) { - loader_->is_pointer_down_from_activate_ = false; - loader_->controller_->CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST); - } -} - -// InstantLoader --------------------------------------------------------------- - -// static -InstantLoader* InstantLoader::FromWebContents( - const content::WebContents* web_contents) { - InstantLoaderUserData* data = static_cast<InstantLoaderUserData*>( - web_contents->GetUserData(&kUserDataKey)); - return data ? data->loader() : NULL; -} - -InstantLoader::InstantLoader(InstantController* controller, - const std::string& instant_url) - : client_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), - controller_(controller), - delegate_(new WebContentsDelegateImpl( - ALLOW_THIS_IN_INITIALIZER_LIST(this))), - instant_url_(instant_url), - supports_instant_(false), - is_pointer_down_from_activate_(false) { +InstantLoader::InstantLoader(Delegate* delegate) + : delegate_(delegate), + contents_(NULL), + stale_page_timer_(false, false) { } InstantLoader::~InstantLoader() { } -void InstantLoader::InitContents(const content::WebContents* active_tab) { - content::WebContents::CreateParams create_params( - active_tab->GetBrowserContext(), - active_tab->GetSiteInstance()->GetRelatedSiteInstance( - GURL(instant_url_))); - if (active_tab) +void InstantLoader::Init(const GURL& instant_url, + Profile* profile, + const content::WebContents* active_tab, + const base::Closure& on_stale_callback) { + content::WebContents::CreateParams create_params(profile); + if (active_tab) { create_params.initial_size = active_tab->GetView()->GetContainerSize(); - contents_.reset(content::WebContents::CreateWithSessionStorage( - create_params, - active_tab->GetController().GetSessionStorageNamespaceMap())); - SetupPreviewContents(); + create_params.site_instance = active_tab->GetSiteInstance()-> + GetRelatedSiteInstance(instant_url); + } else { + create_params.site_instance = content::SiteInstance::CreateForURL( + profile, instant_url); + } + SetContents(scoped_ptr<content::WebContents>( + content::WebContents::Create(create_params))); + instant_url_ = instant_url; + on_stale_callback_ = on_stale_callback; +} - // This HTTP header and value are set on loads that originate from Instant. - const char kInstantHeader[] = "X-Purpose: Instant"; +void InstantLoader::Load() { DVLOG(1) << "LoadURL: " << instant_url_; - contents_->GetController().LoadURL(GURL(instant_url_), content::Referrer(), + contents_->GetController().LoadURL( + instant_url_, content::Referrer(), content::PAGE_TRANSITION_GENERATED, kInstantHeader); contents_->WasHidden(); + stale_page_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kStalePageTimeoutMS), + on_stale_callback_); } -content::WebContents* InstantLoader::ReleaseContents() { - CleanupPreviewContents(); - return contents_.release(); -} - -void InstantLoader::DidNavigate( - const history::HistoryAddPageArgs& add_page_args) { - last_navigation_ = add_page_args; -} - -bool InstantLoader::IsUsingLocalPreview() const { - return instant_url_ == InstantController::kLocalOmniboxPopupURL; -} - -void InstantLoader::Update(const string16& text, - size_t selection_start, - size_t selection_end, - bool verbatim) { - last_navigation_ = history::HistoryAddPageArgs(); - client_.Update(text, selection_start, selection_end, verbatim); -} - -void InstantLoader::Submit(const string16& text) { - client_.Submit(text); -} - -void InstantLoader::Cancel(const string16& text) { - client_.Cancel(text); -} - -void InstantLoader::SetPopupBounds(const gfx::Rect& bounds) { - client_.SetPopupBounds(bounds); -} - -void InstantLoader::SetMarginSize(int start, int end) { - client_.SetMarginSize(start, end); -} - -void InstantLoader::InitializeFonts() { - client_.InitializeFonts(); -} - -void InstantLoader::SendAutocompleteResults( - const std::vector<InstantAutocompleteResult>& results) { - client_.SendAutocompleteResults(results); -} - -void InstantLoader::UpOrDownKeyPressed(int count) { - client_.UpOrDownKeyPressed(count); -} - -void InstantLoader::SearchModeChanged(const chrome::search::Mode& mode) { - client_.SearchModeChanged(mode); -} - -void InstantLoader::SendThemeBackgroundInfo( - const ThemeBackgroundInfo& theme_info) { - client_.SendThemeBackgroundInfo(theme_info); -} - -void InstantLoader::SendThemeAreaHeight(int height) { - client_.SendThemeAreaHeight(height); -} - -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); - controller_->SetSuggestions(contents(), suggestions); -} - -void InstantLoader::InstantSupportDetermined(bool supports_instant) { - // If we had already determined that the page supports Instant, nothing to do. - if (supports_instant_) - return; - - supports_instant_ = supports_instant; - controller_->InstantSupportDetermined(contents(), supports_instant); -} - -void InstantLoader::ShowInstantPreview(InstantShownReason reason, - int height, - InstantSizeUnits units) { - InstantSupportDetermined(true); - controller_->ShowInstantPreview(reason, height, units); -} - -void InstantLoader::StartCapturingKeyStrokes() { - InstantSupportDetermined(true); - controller_->StartCapturingKeyStrokes(); -} - -void InstantLoader::StopCapturingKeyStrokes() { - InstantSupportDetermined(true); - controller_->StopCapturingKeyStrokes(); -} - -void InstantLoader::RenderViewGone() { - controller_->InstantLoaderRenderViewGone(); -} - -void InstantLoader::AboutToNavigateMainFrame(const GURL& url) { - controller_->InstantLoaderAboutToNavigateMainFrame(url); -} - -void InstantLoader::NavigateToURL(const GURL& url, - content::PageTransition transition) { - InstantSupportDetermined(true); - controller_->NavigateToURL(url, transition); -} - -void InstantLoader::RenderViewCreated() { - controller_->InstantLoaderRenderViewCreated(); -} - -void InstantLoader::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { -#if defined(OS_MACOSX) - if (type == content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED) { - if (content::RenderWidgetHostView* rwhv = - contents_->GetRenderWidgetHostView()) - rwhv->SetTakesFocusOnlyOnMouseDown(true); - return; - } - NOTREACHED(); -#endif -} - -void InstantLoader::SetupPreviewContents() { - client_.SetContents(contents()); - contents_->SetUserData(&kUserDataKey, new InstantLoaderUserData(this)); - contents_->SetDelegate(delegate_.get()); +void InstantLoader::SetContents(scoped_ptr<content::WebContents> new_contents) { + contents_.reset(new_contents.release()); + contents_->SetDelegate(this); // Set up various tab helpers. The rest will get attached when (if) the // contents is added to the tab strip. @@ -364,7 +89,7 @@ void InstantLoader::SetupPreviewContents() { // A tab helper to catch prerender content swapping shenanigans. CoreTabHelper::CreateForWebContents(contents()); - CoreTabHelper::FromWebContents(contents())->set_delegate(delegate_.get()); + CoreTabHelper::FromWebContents(contents())->set_delegate(this); // Tab helpers used when committing a preview. chrome::search::SearchTabHelper::CreateForWebContents(contents()); @@ -391,12 +116,10 @@ void InstantLoader::SetupPreviewContents() { #endif } -void InstantLoader::CleanupPreviewContents() { - client_.SetContents(NULL); - contents_->RemoveUserData(&kUserDataKey); +scoped_ptr<content::WebContents> InstantLoader::ReleaseContents() { contents_->SetDelegate(NULL); - // Undo tab helper work done in SetupPreviewContents(). + // Undo tab helper work done in SetContents(). BlockedContentTabHelper::FromWebContents(contents())-> SetAllContentsBlocked(false); @@ -413,16 +136,87 @@ void InstantLoader::CleanupPreviewContents() { content::Source<content::NavigationController>( &contents_->GetController())); #endif + + return contents_.Pass(); } -void InstantLoader::ReplacePreviewContents(content::WebContents* old_contents, - content::WebContents* new_contents) { +void InstantLoader::Observe(int type, + const content::NotificationSource& /* source */, + const content::NotificationDetails& /* details */) { +#if defined(OS_MACOSX) + if (type == content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED) { + if (content::RenderWidgetHostView* rwhv = + contents_->GetRenderWidgetHostView()) + rwhv->SetTakesFocusOnlyOnMouseDown(true); + return; + } + NOTREACHED(); +#endif +} + +void InstantLoader::SwapTabContents(content::WebContents* old_contents, + content::WebContents* new_contents) { DCHECK_EQ(old_contents, contents()); - CleanupPreviewContents(); - // We release here without deleting so that the caller still has the - // responsibility for deleting the WebContents. - ignore_result(contents_.release()); - contents_.reset(new_contents); - SetupPreviewContents(); - controller_->SwappedWebContents(); + // We release here without deleting since the caller has the responsibility + // for deleting the old WebContents. + ignore_result(ReleaseContents().release()); + SetContents(scoped_ptr<content::WebContents>(new_contents)); + delegate_->OnSwappedContents(); +} + +bool InstantLoader::ShouldSuppressDialogs() { + // Messages shown during Instant cancel Instant, so we suppress them. + return true; +} + +bool InstantLoader::ShouldFocusPageAfterCrash() { + return false; +} + +void InstantLoader::LostCapture() { + delegate_->OnMouseUp(); +} + +void InstantLoader::WebContentsFocused(content::WebContents* /* contents */) { + delegate_->OnFocus(); +} + +bool InstantLoader::CanDownload(content::RenderViewHost* /* render_view_host */, + int /* request_id */, + const std::string& /* request_method */) { + // Downloads are disabled. + return false; +} + +void InstantLoader::HandleMouseDown() { + delegate_->OnMouseDown(); +} + +void InstantLoader::HandleMouseUp() { + delegate_->OnMouseUp(); +} + +void InstantLoader::HandlePointerActivate() { + delegate_->OnMouseDown(); +} + +void InstantLoader::HandleGestureEnd() { + delegate_->OnMouseUp(); +} + +void InstantLoader::DragEnded() { + // If the user drags, we won't get a mouse up (at least on Linux). Commit + // the Instant result when the drag ends, so that during the drag the page + // won't move around. + delegate_->OnMouseUp(); +} + +bool InstantLoader::OnGoToEntryOffset(int /* offset */) { + return false; +} + +content::WebContents* InstantLoader::OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) { + return delegate_->OpenURLFromTab(source, params); } diff --git a/chrome/browser/instant/instant_loader.h b/chrome/browser/instant/instant_loader.h index be244f5..b9f9fa9 100644 --- a/chrome/browser/instant/instant_loader.h +++ b/chrome/browser/instant/instant_loader.h @@ -5,152 +5,127 @@ #ifndef CHROME_BROWSER_INSTANT_INSTANT_LOADER_H_ #define CHROME_BROWSER_INSTANT_INSTANT_LOADER_H_ -#include <string> -#include <vector> - #include "base/basictypes.h" +#include "base/callback.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "base/string16.h" -#include "chrome/browser/history/history_types.h" -#include "chrome/browser/instant/instant_client.h" +#include "base/timer.h" +#include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" +#include "content/public/browser/web_contents_delegate.h" -struct InstantAutocompleteResult; -class InstantController; -struct ThemeBackgroundInfo; - -namespace chrome { -namespace search { -struct Mode; -} -} +class GURL; +class Profile; namespace content { +struct OpenURLParams; class WebContents; } -namespace gfx { -class Rect; -} - -// InstantLoader is used to communicate with a preview WebContents that it owns -// and loads the "Instant URL" into. This preview can appear and disappear at -// will as the user types in the omnibox (compare: InstantTab, which talks to a -// committed tab on the tab strip). -class InstantLoader : public InstantClient::Delegate, - public content::NotificationObserver { +// InstantLoader is used to create and maintain a WebContents where we can +// preload a page into. It is used by InstantOverlay and InstantNTP to +// preload an Instant page. +class InstantLoader : public content::NotificationObserver, + public content::WebContentsDelegate, + public CoreTabHelperDelegate { public: - // Returns the Instant loader for |contents| if it's used for Instant. - static InstantLoader* FromWebContents(const content::WebContents* contents); - - // Doesn't take ownership of |controller|. - InstantLoader(InstantController* controller, const std::string& instant_url); + // InstantLoader calls these methods on its delegate in response to certain + // changes in the underlying contents. + class Delegate { + public: + // Called after someone has swapped in a different WebContents for ours. + virtual void OnSwappedContents() = 0; + + // Called when the underlying contents receive focus. + virtual void OnFocus() = 0; + + // Called when the mouse pointer is down. + virtual void OnMouseDown() = 0; + + // Called when the mouse pointer is released (or a drag event ends). + virtual void OnMouseUp() = 0; + + // Called to open a URL using the underlying contents (see + // WebContentsDelegate::OpenURLFromTab). The Delegate should return the + // WebContents the URL is opened in, or NULL if the URL wasn't opened + // immediately. + virtual content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) = 0; + + protected: + ~Delegate(); + }; + + explicit InstantLoader(Delegate* delegate); virtual ~InstantLoader(); - // The preview WebContents. InstantLoader retains ownership. This will be - // non-NULL after InitFromContents(), and until ReleaseContents() is called. + // Creates a new WebContents in the context of |profile| that will be used to + // load |instant_url|. The page is not actually loaded until Load() is + // called. Uses |active_contents|, if non-NULL, to initialize the size of the + // new contents. |on_stale_callback| will be called after kStalePageTimeoutMS + // has elapsed after Load() being called. + void Init(const GURL& instant_url, + Profile* profile, + const content::WebContents* active_contents, + const base::Closure& on_stale_callback); + + // Loads |instant_url_| in |contents_|. + void Load(); + + // Returns the contents currently held. May be NULL. content::WebContents* contents() const { return contents_.get(); } - // Creates a new WebContents and loads |instant_url_| into it. |active_tab| is - // the page the preview will be shown on top of and potentially replace. - void InitContents(const content::WebContents* active_tab); - - // Releases the preview WebContents passing ownership to the caller. This - // should be called when the preview is committed. - content::WebContents* ReleaseContents() WARN_UNUSED_RESULT; - - // Returns the URL that we're loading. - const std::string& instant_url() const { return instant_url_; } - - // Returns true if the preview is known to support the Instant API. This - // starts out false, and becomes true whenever we get any message from the - // page. Once true, it never becomes false (the page isn't expected to drop - // Instant API support suddenly). - bool supports_instant() const { return supports_instant_; } - - // Returns true if the mouse or a touch pointer is down due to activating the - // preview contents. - bool is_pointer_down_from_activate() const { - return is_pointer_down_from_activate_; - } - - // Returns info about the last navigation by the Instant page. If the page - // hasn't navigated since the last Update(), the URL is empty. - const history::HistoryAddPageArgs& last_navigation() const { - return last_navigation_; - } - - // Called by the history tab helper with information that it would have added - // to the history service had this WebContents not been used for Instant. - void DidNavigate(const history::HistoryAddPageArgs& add_page_args); - - // Returns true if the loader is using - // InstantController::kLocalOmniboxPopupURL as the |instant_url_|. - bool IsUsingLocalPreview() const; - - // Calls through to methods of the same name on InstantClient. - void Update(const string16& text, - size_t selection_start, - size_t selection_end, - bool verbatim); - void Submit(const string16& text); - void Cancel(const string16& text); - void SetPopupBounds(const gfx::Rect& bounds); - void SetMarginSize(int start, int end); - void InitializeFonts(); - void SendAutocompleteResults( - const std::vector<InstantAutocompleteResult>& results); - void UpOrDownKeyPressed(int count); - void SearchModeChanged(const chrome::search::Mode& mode); - void SendThemeBackgroundInfo(const ThemeBackgroundInfo& theme_info); - void SendThemeAreaHeight(int height); - void SetDisplayInstantResults(bool display_instant_results); - void KeyCaptureChanged(bool is_key_capture_enabled); + // Replaces the contents held with |contents|. Any existing contents is + // deleted. The expiration timer is not restarted. + void SetContents(scoped_ptr<content::WebContents> contents); - private: - class WebContentsDelegateImpl; - - // Overridden from InstantClient::Delegate: - virtual void SetSuggestions( - const std::vector<InstantSuggestion>& suggestions) OVERRIDE; - virtual void InstantSupportDetermined(bool supports_instant) OVERRIDE; - virtual void ShowInstantPreview(InstantShownReason reason, - int height, - InstantSizeUnits units) OVERRIDE; - virtual void StartCapturingKeyStrokes() OVERRIDE; - virtual void StopCapturingKeyStrokes() OVERRIDE; - virtual void RenderViewGone() OVERRIDE; - virtual void AboutToNavigateMainFrame(const GURL& url) OVERRIDE; - virtual void NavigateToURL(const GURL& url, - content::PageTransition transition) OVERRIDE; - virtual void RenderViewCreated() OVERRIDE; + // Releases the contents currently held. Must only be called if contents() is + // not NULL. + scoped_ptr<content::WebContents> ReleaseContents() WARN_UNUSED_RESULT; + private: // Overridden from content::NotificationObserver: virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; - void SetupPreviewContents(); - void CleanupPreviewContents(); - void ReplacePreviewContents(content::WebContents* old_contents, - content::WebContents* new_contents); + // Overridden from CoreTabHelperDelegate: + virtual void SwapTabContents(content::WebContents* old_contents, + content::WebContents* new_contents) OVERRIDE; + + // Overridden from content::WebContentsDelegate: + virtual bool ShouldSuppressDialogs() OVERRIDE; + virtual bool ShouldFocusPageAfterCrash() OVERRIDE; + virtual void LostCapture() OVERRIDE; + virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE; + virtual bool CanDownload(content::RenderViewHost* render_view_host, + int request_id, + const std::string& request_method) OVERRIDE; + virtual void HandleMouseDown() OVERRIDE; + virtual void HandleMouseUp() OVERRIDE; + virtual void HandlePointerActivate() OVERRIDE; + virtual void HandleGestureEnd() OVERRIDE; + virtual void DragEnded() OVERRIDE; + virtual bool OnGoToEntryOffset(int offset) OVERRIDE; + virtual content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) OVERRIDE; + + Delegate* const delegate_; + scoped_ptr<content::WebContents> contents_; - InstantClient client_; - InstantController* const controller_; + // The URL we will be loading. + GURL instant_url_; - // Delegate of the preview WebContents. Used when the user does some gesture - // on the preview and it needs to be activated. - scoped_ptr<WebContentsDelegateImpl> delegate_; - scoped_ptr<content::WebContents> contents_; + // Called when |stale_page_timer_| fires. + base::Closure on_stale_callback_; - const std::string instant_url_; - bool supports_instant_; - bool is_pointer_down_from_activate_; - history::HistoryAddPageArgs last_navigation_; + // Used to mark when the page is stale. + base::Timer stale_page_timer_; - // Used to get notifications about renderers coming and going. + // Used to get notifications about renderers. content::NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(InstantLoader); diff --git a/chrome/browser/instant/instant_ntp.cc b/chrome/browser/instant/instant_ntp.cc new file mode 100644 index 0000000..f23a3bf --- /dev/null +++ b/chrome/browser/instant/instant_ntp.cc @@ -0,0 +1,66 @@ +// 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/instant/instant_ntp.h" + +#include "chrome/common/url_constants.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" + +InstantNTP::InstantNTP(InstantPage::Delegate* delegate, + const std::string& instant_url) + : InstantPage(delegate), + loader_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + instant_url_(instant_url) { +} + +InstantNTP::~InstantNTP() { +} + +void InstantNTP::InitContents(Profile* profile, + const content::WebContents* active_tab, + const base::Closure& on_stale_callback) { + loader_.Init(GURL(instant_url_), profile, active_tab, on_stale_callback); + SetContents(loader_.contents()); + loader_.Load(); + contents()->GetController().GetPendingEntry()->SetVirtualURL( + GURL(chrome::kChromeUINewTabURL)); +} + +scoped_ptr<content::WebContents> InstantNTP::ReleaseContents() { + SetContents(NULL); + return loader_.ReleaseContents(); +} + +void InstantNTP::OnSwappedContents() { + SetContents(loader_.contents()); + contents()->GetController().GetPendingEntry()->SetVirtualURL( + GURL(chrome::kChromeUINewTabURL)); +} + +void InstantNTP::OnFocus() { + NOTREACHED(); +} + +void InstantNTP::OnMouseDown() { + NOTREACHED(); +} + +void InstantNTP::OnMouseUp() { + NOTREACHED(); +} + +content::WebContents* InstantNTP::OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) { + return NULL; +} + +bool InstantNTP::ShouldProcessRenderViewCreated() { + return true; +} + +bool InstantNTP::ShouldProcessRenderViewGone() { + return true; +} diff --git a/chrome/browser/instant/instant_ntp.h b/chrome/browser/instant/instant_ntp.h new file mode 100644 index 0000000..fb72ec9 --- /dev/null +++ b/chrome/browser/instant/instant_ntp.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef CHROME_BROWSER_INSTANT_INSTANT_NTP_H_ +#define CHROME_BROWSER_INSTANT_INSTANT_NTP_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/instant/instant_loader.h" +#include "chrome/browser/instant/instant_page.h" + +class Profile; + +// InstantNTP is used to preload an Instant page that will be swapped in when a +// user navigates to a New Tab Page (NTP). The InstantNTP contents are never +// shown in an un-committed state. +class InstantNTP : public InstantPage, + public InstantLoader::Delegate { + public: + InstantNTP(InstantPage::Delegate* delegate, const std::string& instant_url); + virtual ~InstantNTP(); + + // Creates a new WebContents and loads |instant_url_| into it. Uses + // |active_tab|, if non-NULL, to initialize the size of the WebContents. + // |on_stale_callback| will be called when |loader_| determines the page to + // be stale. + void InitContents(Profile* profile, + const content::WebContents* active_tab, + const base::Closure& on_stale_callback); + + // Releases the WebContents for the instant page. This should be called when + // the page is about to be committed. + scoped_ptr<content::WebContents> ReleaseContents() WARN_UNUSED_RESULT; + + // Returns the URL that we're loading. + const std::string& instant_url() const { return instant_url_; } + + private: + // Overriden from InstantLoader::Delegate: + virtual void OnSwappedContents() OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnMouseDown() OVERRIDE; + virtual void OnMouseUp() OVERRIDE; + virtual content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) OVERRIDE; + + // Overridden from InstantPage: + virtual bool ShouldProcessRenderViewCreated() OVERRIDE; + virtual bool ShouldProcessRenderViewGone() OVERRIDE; + + InstantLoader loader_; + const std::string instant_url_; + + DISALLOW_COPY_AND_ASSIGN(InstantNTP); +}; + +#endif // CHROME_BROWSER_INSTANT_INSTANT_NTP_H_ diff --git a/chrome/browser/instant/instant_overlay.cc b/chrome/browser/instant/instant_overlay.cc new file mode 100644 index 0000000..ebe1a4a --- /dev/null +++ b/chrome/browser/instant/instant_overlay.cc @@ -0,0 +1,154 @@ +// 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/instant/instant_overlay.h" + +#include "base/auto_reset.h" +#include "base/supports_user_data.h" +#include "content/public/browser/web_contents.h" + +namespace { + +int kUserDataKey; + +class InstantOverlayUserData : public base::SupportsUserData::Data { + public: + explicit InstantOverlayUserData(InstantOverlay* overlay) + : overlay_(overlay) {} + + virtual InstantOverlay* overlay() const { return overlay_; } + + private: + ~InstantOverlayUserData() {} + + InstantOverlay* const overlay_; + + DISALLOW_COPY_AND_ASSIGN(InstantOverlayUserData); +}; + +} // namespace + +// static +InstantOverlay* InstantOverlay::FromWebContents( + const content::WebContents* web_contents) { + InstantOverlayUserData* data = static_cast<InstantOverlayUserData*>( + web_contents->GetUserData(&kUserDataKey)); + return data ? data->overlay() : NULL; +} + +InstantOverlay::InstantOverlay(InstantController* controller, + const std::string& instant_url) + : InstantPage(controller), + loader_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + instant_url_(instant_url), + is_stale_(false), + is_pointer_down_from_activate_(false) { +} + +InstantOverlay::~InstantOverlay() { +} + +void InstantOverlay::InitContents(Profile* profile, + const content::WebContents* active_tab) { + loader_.Init(GURL(instant_url_), profile, active_tab, + base::Bind(&InstantOverlay::HandleStalePage, + base::Unretained(this))); + SetContents(loader_.contents()); + contents()->SetUserData(&kUserDataKey, new InstantOverlayUserData(this)); + loader_.Load(); +} + +scoped_ptr<content::WebContents> InstantOverlay::ReleaseContents() { + contents()->RemoveUserData(&kUserDataKey); + SetContents(NULL); + return loader_.ReleaseContents(); +} + +void InstantOverlay::DidNavigate( + const history::HistoryAddPageArgs& add_page_args) { + last_navigation_ = add_page_args; +} + +bool InstantOverlay::IsUsingLocalPreview() const { + return instant_url_ == InstantController::kLocalOmniboxPopupURL; +} + +void InstantOverlay::Update(const string16& text, + size_t selection_start, + size_t selection_end, + bool verbatim) { + last_navigation_ = history::HistoryAddPageArgs(); + InstantPage::Update(text, selection_start, selection_end, verbatim); +} + +bool InstantOverlay::ShouldProcessRenderViewCreated() { + return true; +} + +bool InstantOverlay::ShouldProcessRenderViewGone() { + return true; +} + +bool InstantOverlay::ShouldProcessAboutToNavigateMainFrame() { + return true; +} + +bool InstantOverlay::ShouldProcessSetSuggestions() { + return true; +} + +bool InstantOverlay::ShouldProcessShowInstantPreview() { + return true; +} + +bool InstantOverlay::ShouldProcessNavigateToURL() { + return true; +} + +void InstantOverlay::OnSwappedContents() { + contents()->RemoveUserData(&kUserDataKey); + SetContents(loader_.contents()); + contents()->SetUserData(&kUserDataKey, new InstantOverlayUserData(this)); + instant_controller()->SwappedOverlayContents(); +} + +void InstantOverlay::OnFocus() { + // The preview is getting focus. Equivalent to it being clicked. + base::AutoReset<bool> reset(&is_pointer_down_from_activate_, true); + instant_controller()->FocusedOverlayContents(); +} + +void InstantOverlay::OnMouseDown() { + is_pointer_down_from_activate_ = true; +} + +void InstantOverlay::OnMouseUp() { + if (is_pointer_down_from_activate_) { + is_pointer_down_from_activate_ = false; + instant_controller()->CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST); + } +} + +content::WebContents* InstantOverlay::OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) { + // We will allow the navigate to continue if we are able to commit the + // overlay. + // + // First, cache the overlay contents since committing it will cause the + // contents to be released (and be set to NULL). + content::WebContents* overlay = contents(); + if (instant_controller()->CommitIfPossible(INSTANT_COMMIT_NAVIGATED)) { + // If the commit was successful, the overlay's delegate should be the tab + // strip, which will be able to handle the navigation. + DCHECK_NE(&loader_, overlay->GetDelegate()); + return overlay->GetDelegate()->OpenURLFromTab(source, params); + } + return NULL; +} + +void InstantOverlay::HandleStalePage() { + is_stale_ = true; + instant_controller()->ReloadOverlayIfStale(); +} diff --git a/chrome/browser/instant/instant_overlay.h b/chrome/browser/instant/instant_overlay.h new file mode 100644 index 0000000..99b7c2e --- /dev/null +++ b/chrome/browser/instant/instant_overlay.h @@ -0,0 +1,117 @@ +// 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. + +#ifndef CHROME_BROWSER_INSTANT_INSTANT_OVERLAY_H_ +#define CHROME_BROWSER_INSTANT_INSTANT_OVERLAY_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/instant/instant_controller.h" +#include "chrome/browser/instant/instant_loader.h" +#include "chrome/browser/instant/instant_page.h" + +class Profile; + +namespace content { +class WebContents; +} + +// InstantOverlay is used to communicate with a preview WebContents that it owns +// and loads the "Instant URL" into. This preview can appear and disappear at +// will as the user types in the omnibox. +class InstantOverlay : public InstantPage, + public InstantLoader::Delegate { + public: + // Returns the InstantOverlay for |contents| if it's used for Instant. + static InstantOverlay* FromWebContents(const content::WebContents* contents); + + InstantOverlay(InstantController* controller, + const std::string& instant_url); + virtual ~InstantOverlay(); + + // Creates a new WebContents and loads |instant_url_| into it. Uses + // |active_tab|, if non-NULL, to initialize the size of the WebContents. + void InitContents(Profile* profile, + const content::WebContents* active_tab); + + // Releases the overlay WebContents. This should be called when the overlay + // is committed. + scoped_ptr<content::WebContents> ReleaseContents() WARN_UNUSED_RESULT; + + // Returns the URL that we're loading. + const std::string& instant_url() const { return instant_url_; } + + // Returns whether the underlying contents is stale (i.e. was loaded too long + // ago). + bool is_stale() const { return is_stale_; } + + // Returns true if the mouse or a touch pointer is down due to activating the + // preview contents. + bool is_pointer_down_from_activate() const { + return is_pointer_down_from_activate_; + } + + // Returns info about the last navigation by the Instant page. If the page + // hasn't navigated since the last Update(), the URL is empty. + const history::HistoryAddPageArgs& last_navigation() const { + return last_navigation_; + } + + // Called by the history tab helper with information that it would have added + // to the history service had this WebContents not been used for Instant. + void DidNavigate(const history::HistoryAddPageArgs& add_page_args); + + // Returns true if the overlay is using + // InstantController::kLocalOmniboxPopupURL as the |instant_url_|. + bool IsUsingLocalPreview() const; + + // Overridden from InstantPage: + virtual void Update(const string16& text, + size_t selection_start, + size_t selection_end, + bool verbatim) OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(InstantTest, InstantOverlayRefresh); + + // Helper to access delegate() as an InstantController object. + InstantController* instant_controller() const { + return static_cast<InstantController*>(delegate()); + } + + // Overridden from InstantPage: + virtual bool ShouldProcessRenderViewCreated() OVERRIDE; + virtual bool ShouldProcessRenderViewGone() OVERRIDE; + virtual bool ShouldProcessAboutToNavigateMainFrame() OVERRIDE; + virtual bool ShouldProcessSetSuggestions() OVERRIDE; + virtual bool ShouldProcessShowInstantPreview() OVERRIDE; + virtual bool ShouldProcessNavigateToURL() OVERRIDE; + + // Overriden from InstantLoader::Delegate: + virtual void OnSwappedContents() OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnMouseDown() OVERRIDE; + virtual void OnMouseUp() OVERRIDE; + virtual content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) OVERRIDE; + + // Called when the underlying page becomes stale. + void HandleStalePage(); + + InstantLoader loader_; + const std::string instant_url_; + bool is_stale_; + bool is_pointer_down_from_activate_; + history::HistoryAddPageArgs last_navigation_; + + DISALLOW_COPY_AND_ASSIGN(InstantOverlay); +}; + +#endif // CHROME_BROWSER_INSTANT_INSTANT_OVERLAY_H_ diff --git a/chrome/browser/instant/instant_page.cc b/chrome/browser/instant/instant_page.cc new file mode 100644 index 0000000..8343975 --- /dev/null +++ b/chrome/browser/instant/instant_page.cc @@ -0,0 +1,238 @@ +// 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/instant/instant_page.h" + +#include "base/utf_string_conversions.h" +#include "chrome/common/render_messages.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/font.h" + +InstantPage::Delegate::~Delegate() { +} + +InstantPage::~InstantPage() { +} + +void InstantPage::Update(const string16& text, + size_t selection_start, + size_t selection_end, + bool verbatim) { + Send(new ChromeViewMsg_SearchBoxChange(routing_id(), text, verbatim, + selection_start, selection_end)); +} + +void InstantPage::Submit(const string16& text) { + Send(new ChromeViewMsg_SearchBoxSubmit(routing_id(), text)); +} + +void InstantPage::Cancel(const string16& text) { + Send(new ChromeViewMsg_SearchBoxCancel(routing_id(), text)); +} + +void InstantPage::SetPopupBounds(const gfx::Rect& bounds) { + Send(new ChromeViewMsg_SearchBoxPopupResize(routing_id(), bounds)); +} + +void InstantPage::SetMarginSize(const int start, const int end) { + Send(new ChromeViewMsg_SearchBoxMarginChange(routing_id(), start, end)); +} + +void InstantPage::InitializeFonts() { + const gfx::Font& omnibox_font = + ui::ResourceBundle::GetSharedInstance().GetFont( + ui::ResourceBundle::MediumFont); + string16 omnibox_font_name = UTF8ToUTF16(omnibox_font.GetFontName()); + size_t omnibox_font_size = omnibox_font.GetFontSize(); + Send(new ChromeViewMsg_SearchBoxFontInformation( + routing_id(), omnibox_font_name, omnibox_font_size)); +} + +void InstantPage::DetermineIfPageSupportsInstant() { + Send(new ChromeViewMsg_DetermineIfPageSupportsInstant(routing_id())); +} + +void InstantPage::SendAutocompleteResults( + const std::vector<InstantAutocompleteResult>& results) { + Send(new ChromeViewMsg_SearchBoxAutocompleteResults(routing_id(), results)); +} + +void InstantPage::UpOrDownKeyPressed(int count) { + Send(new ChromeViewMsg_SearchBoxUpOrDownKeyPressed(routing_id(), count)); +} + +void InstantPage::SearchModeChanged(const chrome::search::Mode& mode) { + Send(new ChromeViewMsg_SearchBoxModeChanged(routing_id(), mode)); +} + +void InstantPage::SendThemeBackgroundInfo( + const ThemeBackgroundInfo& theme_info) { + Send(new ChromeViewMsg_SearchBoxThemeChanged(routing_id(), theme_info)); +} + +void InstantPage::SendThemeAreaHeight(int height) { + Send(new ChromeViewMsg_SearchBoxThemeAreaHeightChanged(routing_id(), height)); +} + +void InstantPage::SetDisplayInstantResults(bool display_instant_results) { + Send(new ChromeViewMsg_SearchBoxSetDisplayInstantResults( + routing_id(), display_instant_results)); +} + +void InstantPage::KeyCaptureChanged(bool is_key_capture_enabled) { + Send(new ChromeViewMsg_SearchBoxKeyCaptureChanged( + routing_id(), is_key_capture_enabled)); +} + +InstantPage::InstantPage(Delegate* delegate) + : delegate_(delegate), + supports_instant_(false) { +} + +void InstantPage::SetContents(content::WebContents* contents) { + Observe(contents); +} + +bool InstantPage::ShouldProcessRenderViewCreated() { + return false; +} + +bool InstantPage::ShouldProcessRenderViewGone() { + return false; +} + +bool InstantPage::ShouldProcessAboutToNavigateMainFrame() { + return false; +} + +bool InstantPage::ShouldProcessSetSuggestions() { + return false; +} + +bool InstantPage::ShouldProcessShowInstantPreview() { + return false; +} + +bool InstantPage::ShouldProcessStartCapturingKeyStrokes() { + return false; +} + +bool InstantPage::ShouldProcessStopCapturingKeyStrokes() { + return false; +} + +bool InstantPage::ShouldProcessNavigateToURL() { + return false; +} + +void InstantPage::RenderViewCreated(content::RenderViewHost* render_view_host) { + if (ShouldProcessRenderViewCreated()) + 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_ShowInstantPreview, + OnShowInstantPreview) + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_StartCapturingKeyStrokes, + OnStartCapturingKeyStrokes); + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_StopCapturingKeyStrokes, + OnStopCapturingKeyStrokes); + IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxNavigate, + OnSearchBoxNavigate); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void InstantPage::RenderViewGone(base::TerminationStatus /* status */) { + if (ShouldProcessRenderViewGone()) + delegate_->InstantPageRenderViewGone(contents()); +} + +void InstantPage::DidCommitProvisionalLoadForFrame( + int64 /* frame_id */, + bool is_main_frame, + const GURL& url, + content::PageTransition /* transition_type */, + content::RenderViewHost* /* render_view_host */) { + if (is_main_frame && ShouldProcessAboutToNavigateMainFrame()) + delegate_->InstantPageAboutToNavigateMainFrame(contents(), url); +} + +void InstantPage::OnSetSuggestions( + int page_id, + const std::vector<InstantSuggestion>& suggestions) { + if (contents()->IsActiveEntry(page_id)) { + OnInstantSupportDetermined(page_id, true); + if (ShouldProcessSetSuggestions()) + delegate_->SetSuggestions(contents(), suggestions); + } +} + +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. + return; + } + + 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); +} + +void InstantPage::OnShowInstantPreview(int page_id, + InstantShownReason reason, + int height, + InstantSizeUnits units) { + if (contents()->IsActiveEntry(page_id)) { + OnInstantSupportDetermined(page_id, true); + if (ShouldProcessShowInstantPreview()) + delegate_->ShowInstantPreview(contents(), reason, height, units); + } +} + +void InstantPage::OnStartCapturingKeyStrokes(int page_id) { + if (contents()->IsActiveEntry(page_id)) { + OnInstantSupportDetermined(page_id, true); + if (ShouldProcessStartCapturingKeyStrokes()) + delegate_->StartCapturingKeyStrokes(contents()); + } +} + +void InstantPage::OnStopCapturingKeyStrokes(int page_id) { + if (contents()->IsActiveEntry(page_id)) { + OnInstantSupportDetermined(page_id, true); + if (ShouldProcessStopCapturingKeyStrokes()) + delegate_->StopCapturingKeyStrokes(contents()); + } +} + +void InstantPage::OnSearchBoxNavigate(int page_id, + const GURL& url, + content::PageTransition transition) { + if (contents()->IsActiveEntry(page_id)) { + OnInstantSupportDetermined(page_id, true); + if (ShouldProcessNavigateToURL()) + delegate_->NavigateToURL(contents(), url, transition); + } +} diff --git a/chrome/browser/instant/instant_page.h b/chrome/browser/instant/instant_page.h new file mode 100644 index 0000000..1051b22 --- /dev/null +++ b/chrome/browser/instant/instant_page.h @@ -0,0 +1,223 @@ +// 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. + +#ifndef CHROME_BROWSER_INSTANT_INSTANT_PAGE_H_ +#define CHROME_BROWSER_INSTANT_INSTANT_PAGE_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/string16.h" +#include "chrome/common/instant_types.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/common/page_transition_types.h" + +class GURL; + +namespace chrome { +namespace search { +struct Mode; +} +} + +namespace content { +class WebContents; +} + +namespace gfx { +class Rect; +} + +// InstantPage is used to exchange messages with a page that implements the +// 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 { + public: + // InstantPage calls its delegate in response to messages received from the + // page. Each method is called with the |contents| corresponding to the page + // we are observing. + class Delegate { + public: + // Called when a RenderView is created, so that state can be initialized. + virtual void InstantPageRenderViewCreated( + const content::WebContents* contents) = 0; + + // Called upon determination of Instant API support. Either in response to + // the page loading or because we received some other message. + virtual void InstantSupportDetermined(const content::WebContents* contents, + bool supports_instant) = 0; + + // Called when the underlying RenderView crashed. + virtual void InstantPageRenderViewGone( + const content::WebContents* contents) = 0; + + // Called when the page is about to navigate to |url|. + virtual void InstantPageAboutToNavigateMainFrame( + const content::WebContents* contents, + const GURL& url) = 0; + + // Called when the page has suggestions. Usually in response to Update(), + // SendAutocompleteResults() or UpOrDownKeyPressed(). + virtual void SetSuggestions( + const content::WebContents* contents, + const std::vector<InstantSuggestion>& suggestions) = 0; + + // Called when the page wants to be shown. Usually in response to Update(), + // SendAutocompleteResults() or SearchModeChanged(). + virtual void ShowInstantPreview(const content::WebContents* contents, + InstantShownReason reason, + int height, + InstantSizeUnits units) = 0; + + // Called when the page wants the omnibox to start capturing user key + // strokes. If this call is processed successfully, the omnibox will not + // look focused visibly but any user key strokes will go to the omnibox. + // Currently, this is implemented by focusing the omnibox invisibly. + virtual void StartCapturingKeyStrokes( + const content::WebContents* contents) = 0; + + // Called when the page wants the omnibox to stop capturing user key + // strokes. + virtual void StopCapturingKeyStrokes(content::WebContents* contents) = 0; + + // Called when the page wants to navigate to the specified URL. Usually + // used by the page to navigate to privileged destinations (e.g. chrome:// + // URLs) or to navigate to URLs that are hidden from the page using + // Restricted IDs (rid in the API). + virtual void NavigateToURL(const content::WebContents* contents, + const GURL& url, + content::PageTransition transition) = 0; + + protected: + virtual ~Delegate(); + }; + + virtual ~InstantPage(); + + // The WebContents corresponding to the page we're talking to. May be NULL. + content::WebContents* contents() const { return web_contents(); } + + // Returns true if the page is known to support the Instant API. This starts + // out false, and is set to true whenever we get any message from the page. + // Once true, it never becomes false (the page isn't expected to drop API + // support suddenly). + bool supports_instant() const { return supports_instant_; } + + // Tells the page that the user typed |text| into the omnibox. If |verbatim| + // is false, the page predicts the query the user means to type and fetches + // results for the prediction. If |verbatim| is true, |text| is taken as the + // exact query (no prediction is made). + virtual void Update(const string16& text, + size_t selection_start, + size_t selection_end, + bool verbatim); + + // Tells the page that the user pressed Enter in the omnibox. + void Submit(const string16& text); + + // Tells the page that the user clicked on it. Nothing is being cancelled; the + // poor choice of name merely reflects the IPC of the same (poor) name. + void Cancel(const string16& text); + + // Tells the page the bounds of the omnibox dropdown (in screen coordinates). + // This is used by the page to offset the results to avoid them being covered + // by the omnibox dropdown. + void SetPopupBounds(const gfx::Rect& bounds); + + // Tells the page the start and end margins of the omnibox (in screen + // coordinates). This is used by the page to align text or assets properly + // with the omnibox. + void SetMarginSize(int start, int end); + + // 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); + + // Tells the page that the user pressed Up or Down in the omnibox. |count| is + // a repeat count, negative for moving up, positive for moving down. + void UpOrDownKeyPressed(int count); + + // Tells the page that the active tab's search mode has changed. + void SearchModeChanged(const chrome::search::Mode& mode); + + // Tells the page about the current theme background. + void SendThemeBackgroundInfo(const ThemeBackgroundInfo& theme_info); + + // Tells the page about the current theme area height. + void SendThemeAreaHeight(int height); + + // 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); + + protected: + explicit InstantPage(Delegate* delegate); + + // Sets |contents| as the page to communicate with. |contents| may be NULL, + // which effectively stops all communication. + void SetContents(content::WebContents* contents); + + Delegate* delegate() const { return delegate_; } + + // These functions are called before processing messages received from the + // page. By default, all messages are handled, but any derived classes may + // choose to ingore some or all of the received messages by overriding these + // methods. + virtual bool ShouldProcessRenderViewCreated(); + virtual bool ShouldProcessRenderViewGone(); + virtual bool ShouldProcessAboutToNavigateMainFrame(); + virtual bool ShouldProcessSetSuggestions(); + virtual bool ShouldProcessShowInstantPreview(); + virtual bool ShouldProcessStartCapturingKeyStrokes(); + virtual bool ShouldProcessStopCapturingKeyStrokes(); + virtual bool ShouldProcessNavigateToURL(); + + private: + // 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( + int64 frame_id, + bool is_main_frame, + const GURL& url, + content::PageTransition transition_type, + content::RenderViewHost* render_view_host) OVERRIDE; + + void OnSetSuggestions(int page_id, + const std::vector<InstantSuggestion>& suggestions); + void OnInstantSupportDetermined(int page_id, bool supports_instant); + void OnShowInstantPreview(int page_id, + InstantShownReason reason, + int height, + InstantSizeUnits units); + void OnStartCapturingKeyStrokes(int page_id); + void OnStopCapturingKeyStrokes(int page_id); + void OnSearchBoxNavigate(int page_id, const GURL& url, + content::PageTransition transition); + + Delegate* const delegate_; + bool supports_instant_; + + DISALLOW_COPY_AND_ASSIGN(InstantPage); +}; + +#endif // CHROME_BROWSER_INSTANT_INSTANT_PAGE_H_ diff --git a/chrome/browser/instant/instant_tab.cc b/chrome/browser/instant/instant_tab.cc index 733c6f0..0a4b51a 100644 --- a/chrome/browser/instant/instant_tab.cc +++ b/chrome/browser/instant/instant_tab.cc @@ -4,102 +4,30 @@ #include "chrome/browser/instant/instant_tab.h" -#include "chrome/browser/instant/instant_controller.h" - -InstantTab::InstantTab(InstantController* controller, - content::WebContents* contents) - : client_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), - controller_(controller), - contents_(contents), - supports_instant_(false) { +InstantTab::InstantTab(InstantPage::Delegate* delegate) + : InstantPage(delegate) { } InstantTab::~InstantTab() { } -void InstantTab::Init() { - client_.SetContents(contents_); - client_.DetermineIfPageSupportsInstant(); -} - -void InstantTab::Update(const string16& text, - size_t selection_start, - size_t selection_end, - bool verbatim) { - client_.Update(text, selection_start, selection_end, verbatim); -} - -void InstantTab::Submit(const string16& text) { - client_.Submit(text); -} - -void InstantTab::SendAutocompleteResults( - const std::vector<InstantAutocompleteResult>& results) { - client_.SendAutocompleteResults(results); -} - -void InstantTab::SetDisplayInstantResults(bool display_instant_results) { - client_.SetDisplayInstantResults(display_instant_results); -} - -void InstantTab::UpOrDownKeyPressed(int count) { - client_.UpOrDownKeyPressed(count); -} - -void InstantTab::SetMarginSize(int start, int end) { - client_.SetMarginSize(start, end); -} - -void InstantTab::InitializeFonts() { - client_.InitializeFonts(); -} - -void InstantTab::SetSuggestions( - const std::vector<InstantSuggestion>& suggestions) { - InstantSupportDetermined(true); - controller_->SetSuggestions(contents_, suggestions); -} - -void InstantTab::InstantSupportDetermined(bool supports_instant) { - // If we had already determined that the page supports Instant, nothing to do. - if (supports_instant_) - return; - - supports_instant_ = supports_instant; - - // If the page doesn't support Instant, stop communicating with it. - if (!supports_instant) - client_.SetContents(NULL); - - controller_->InstantSupportDetermined(contents_, supports_instant); -} - -void InstantTab::ShowInstantPreview(InstantShownReason /* reason */, - int /* height */, - InstantSizeUnits /* units */) { - // The page is a committed tab (i.e., always showing), so nothing to do. -} - -void InstantTab::StartCapturingKeyStrokes() { - // We don't honor this call from committed tabs. -} - -void InstantTab::StopCapturingKeyStrokes() { - // We don't honor this call from committed tabs. +void InstantTab::Init(content::WebContents* contents) { + SetContents(contents); + DetermineIfPageSupportsInstant(); } -void InstantTab::RenderViewGone() { - // For a commit page, a crash should not be handled differently. +bool InstantTab::ShouldProcessSetSuggestions() { + return true; } -void InstantTab::AboutToNavigateMainFrame(const GURL& url) { - // The client is a committed tab, navigations will happen as expected. +bool InstantTab::ShouldProcessStartCapturingKeyStrokes() { + return true; } -void InstantTab::NavigateToURL(const GURL& url, - content::PageTransition transition) { - controller_->NavigateToURL(url, transition); +bool InstantTab::ShouldProcessStopCapturingKeyStrokes() { + return true; } -void InstantTab::RenderViewCreated() { +bool InstantTab::ShouldProcessNavigateToURL() { + return true; } diff --git a/chrome/browser/instant/instant_tab.h b/chrome/browser/instant/instant_tab.h index 8546c08..bbc675d 100644 --- a/chrome/browser/instant/instant_tab.h +++ b/chrome/browser/instant/instant_tab.h @@ -5,68 +5,27 @@ #ifndef CHROME_BROWSER_INSTANT_INSTANT_TAB_H_ #define CHROME_BROWSER_INSTANT_INSTANT_TAB_H_ -#include <vector> - #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/string16.h" -#include "chrome/browser/instant/instant_client.h" - -struct InstantAutocompleteResult; -class InstantController; - -namespace content { -class WebContents; -} +#include "chrome/browser/instant/instant_page.h" -// InstantTab is used to communicate with a committed search results page, i.e., -// an actual tab on the tab strip (compare: InstantLoader, which has a preview -// page that appears and disappears as the user types in the omnibox). -class InstantTab : public InstantClient::Delegate { +// InstantTab represents a committed page (i.e. an actual tab on the tab strip) +// that supports the Instant API. +class InstantTab : public InstantPage { public: - // Doesn't take ownership of either |controller| or |contents|. - InstantTab(InstantController* controller, content::WebContents* contents); + explicit InstantTab(InstantPage::Delegate* delegate); virtual ~InstantTab(); - content::WebContents* contents() const { return contents_; } - - // Start observing |contents_| for messages. Sends a message to determine if + // Start observing |contents| for messages. Sends a message to determine if // the page supports the Instant API. - void Init(); - - // Calls through to methods of the same name on InstantClient. - void Update(const string16& text, - size_t selection_start, - size_t selection_end, - bool verbatim); - void Submit(const string16& text); - void SendAutocompleteResults( - const std::vector<InstantAutocompleteResult>& results); - void SetDisplayInstantResults(bool display_instant_results); - void UpOrDownKeyPressed(int count); - void SetMarginSize(int start, int end); - void InitializeFonts(); + void Init(content::WebContents* contents); private: - // Overridden from InstantClient::Delegate: - virtual void SetSuggestions( - const std::vector<InstantSuggestion>& suggestions) OVERRIDE; - virtual void InstantSupportDetermined(bool supports_instant) OVERRIDE; - virtual void ShowInstantPreview(InstantShownReason reason, - int height, - InstantSizeUnits units) OVERRIDE; - virtual void StartCapturingKeyStrokes() OVERRIDE; - virtual void StopCapturingKeyStrokes() OVERRIDE; - virtual void RenderViewGone() OVERRIDE; - virtual void AboutToNavigateMainFrame(const GURL& url) OVERRIDE; - virtual void NavigateToURL(const GURL& url, - content::PageTransition transition) OVERRIDE; - virtual void RenderViewCreated() OVERRIDE; - - InstantClient client_; - InstantController* const controller_; - content::WebContents* const contents_; - bool supports_instant_; + // Overridden from InstantPage: + virtual bool ShouldProcessSetSuggestions() OVERRIDE; + virtual bool ShouldProcessStartCapturingKeyStrokes() OVERRIDE; + virtual bool ShouldProcessStopCapturingKeyStrokes() OVERRIDE; + virtual bool ShouldProcessNavigateToURL() OVERRIDE; DISALLOW_COPY_AND_ASSIGN(InstantTab); }; diff --git a/chrome/browser/instant/instant_test_utils.cc b/chrome/browser/instant/instant_test_utils.cc index 65451db..efd35a3 100644 --- a/chrome/browser/instant/instant_test_utils.cc +++ b/chrome/browser/instant/instant_test_utils.cc @@ -9,12 +9,10 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service.h" #include "chrome/browser/search_engines/template_url_service_factory.h" -#include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" -#include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/result_codes.h" @@ -99,14 +97,6 @@ void InstantTestBase::FocusOmnibox() { } } -void InstantTestBase::FocusOmniboxAndWaitForInstantSupport() { - content::WindowedNotificationObserver observer( - chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, - content::NotificationService::AllSources()); - FocusOmnibox(); - observer.Wait(); -} - void InstantTestBase::SetOmniboxText(const std::string& text) { FocusOmnibox(); omnibox()->SetUserText(UTF8ToUTF16(text)); diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index e81eebc..d969bfa 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -1389,6 +1389,10 @@ void Browser::BeforeUnloadFired(WebContents* web_contents, unload_controller_->BeforeUnloadFired(web_contents, proceed); } +bool Browser::ShouldFocusLocationBarByDefault(WebContents* source) { + return source->GetURL() == GURL(chrome::kChromeUINewTabURL); +} + void Browser::SetFocusToLocationBar(bool select_all) { // Two differences between this and FocusLocationBar(): // (1) This doesn't get recorded in user metrics, since it's called diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 8d0ce58..e029a39 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h @@ -556,6 +556,8 @@ class Browser : public TabStripModelObserver, virtual void BeforeUnloadFired(content::WebContents* source, bool proceed, bool* proceed_to_fire_unload) OVERRIDE; + virtual bool ShouldFocusLocationBarByDefault( + content::WebContents* source) OVERRIDE; virtual void SetFocusToLocationBar(bool select_all) OVERRIDE; virtual void RenderWidgetShowing() OVERRIDE; virtual int GetExtraRenderViewHeight() const OVERRIDE; diff --git a/chrome/browser/ui/browser_instant_controller.cc b/chrome/browser/ui/browser_instant_controller.cc index 49d93da..ae5c0b0 100644 --- a/chrome/browser/ui/browser_instant_controller.cc +++ b/chrome/browser/ui/browser_instant_controller.cc @@ -19,6 +19,7 @@ #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" #include "content/public/browser/notification_service.h" #include "grit/theme_resources.h" #include "ui/gfx/color_utils.h" @@ -39,13 +40,13 @@ namespace chrome { BrowserInstantController::BrowserInstantController(Browser* browser) : browser_(browser), instant_(ALLOW_THIS_IN_INITIALIZER_LIST(this), - chrome::search::IsInstantExtendedAPIEnabled(browser->profile())), + chrome::search::IsInstantExtendedAPIEnabled(profile())), instant_unload_handler_(browser), initialized_theme_info_(false), theme_area_height_(0) { - profile_pref_registrar_.Init(browser_->profile()->GetPrefs()); + profile_pref_registrar_.Init(profile()->GetPrefs()); profile_pref_registrar_.Add( - GetInstantPrefName(browser_->profile()), + GetInstantPrefName(profile()), base::Bind(&BrowserInstantController::ResetInstant, base::Unretained(this))); profile_pref_registrar_.Add( @@ -59,7 +60,7 @@ BrowserInstantController::BrowserInstantController(Browser* browser) // Listen for theme installation. registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, content::Source<ThemeService>( - ThemeServiceFactory::GetForProfile(browser_->profile()))); + ThemeServiceFactory::GetForProfile(profile()))); #endif // defined(ENABLE_THEMES) } @@ -98,6 +99,33 @@ void BrowserInstantController::RegisterUserPrefs(PrefServiceSyncable* prefs) { PrefServiceSyncable::SYNCABLE_PREF); } +bool BrowserInstantController::MaybeSwapInInstantNTPContents( + const GURL& url, + content::WebContents* source_contents, + content::WebContents** target_contents) { + if (url != GURL(chrome::kChromeUINewTabURL)) + return false; + + scoped_ptr<content::WebContents> instant_ntp = instant_.ReleaseNTPContents(); + if (!instant_ntp) + return false; + + *target_contents = instant_ntp.get(); + instant_ntp->GetController().PruneAllButActive(); + if (source_contents) { + instant_ntp->GetController().CopyStateFromAndPrune( + &source_contents->GetController()); + ReplaceWebContentsAt( + browser_->tab_strip_model()->GetIndexOfWebContents(source_contents), + instant_ntp.Pass()); + } else { + // If |source_contents| is NULL, then the caller is responsible for + // inserting instant_ntp into the tabstrip and will take ownership. + ignore_result(instant_ntp.release()); + } + return true; +} + bool BrowserInstantController::OpenInstant(WindowOpenDisposition disposition) { // Unsupported dispositions. if (disposition == NEW_BACKGROUND_TAB || disposition == NEW_WINDOW) @@ -113,31 +141,43 @@ bool BrowserInstantController::OpenInstant(WindowOpenDisposition disposition) { INSTANT_COMMIT_PRESSED_ENTER : INSTANT_COMMIT_PRESSED_ALT_ENTER); } -void BrowserInstantController::CommitInstant(content::WebContents* preview, - bool in_new_tab) { +Profile* BrowserInstantController::profile() const { + return browser_->profile(); +} + +void BrowserInstantController::CommitInstant( + scoped_ptr<content::WebContents> preview, + bool in_new_tab) { + if (profile()->GetExtensionService()->IsInstalledApp(preview->GetURL())) { + AppLauncherHandler::RecordAppLaunchType( + extension_misc::APP_LAUNCH_OMNIBOX_INSTANT); + } if (in_new_tab) { // TabStripModel takes ownership of |preview|. - browser_->tab_strip_model()->AddWebContents(preview, -1, + browser_->tab_strip_model()->AddWebContents(preview.release(), -1, instant_.last_transition_type(), TabStripModel::ADD_ACTIVE); } else { - int index = browser_->tab_strip_model()->active_index(); - DCHECK_NE(TabStripModel::kNoTab, index); - content::WebContents* active_tab = - browser_->tab_strip_model()->GetWebContentsAt(index); - // TabStripModel takes ownership of |preview|. - browser_->tab_strip_model()->ReplaceWebContentsAt(index, preview); - // InstantUnloadHandler takes ownership of |active_tab|. - instant_unload_handler_.RunUnloadListenersOrDestroy(active_tab, index); - - GURL url = preview->GetURL(); - DCHECK(browser_->profile()->GetExtensionService()); - if (browser_->profile()->GetExtensionService()->IsInstalledApp(url)) { - AppLauncherHandler::RecordAppLaunchType( - extension_misc::APP_LAUNCH_OMNIBOX_INSTANT); - } + ReplaceWebContentsAt( + browser_->tab_strip_model()->active_index(), + preview.Pass()); } } +void BrowserInstantController::ReplaceWebContentsAt( + int index, + scoped_ptr<content::WebContents> new_contents) { + DCHECK_NE(TabStripModel::kNoTab, index); + content::WebContents* old_contents = + browser_->tab_strip_model()->GetWebContentsAt(index); + // TabStripModel takes ownership of |new_contents|. + browser_->tab_strip_model()->ReplaceWebContentsAt( + index, new_contents.release()); + // TODO(samarth): use scoped_ptr instead of comments to document ownership + // transfer. + // InstantUnloadHandler takes ownership of |old_contents|. + instant_unload_handler_.RunUnloadListenersOrDestroy(old_contents, index); +} + void BrowserInstantController::SetInstantSuggestion( const InstantSuggestion& suggestion) { browser_->window()->GetLocationBar()->SetInstantSuggestion(suggestion); @@ -180,7 +220,7 @@ void BrowserInstantController::UpdateThemeInfoForPreview() { // Initialize |theme_info| if necessary. // |OnThemeChanged| also updates theme area height if necessary. if (!initialized_theme_info_) - OnThemeChanged(ThemeServiceFactory::GetForProfile(browser_->profile())); + OnThemeChanged(ThemeServiceFactory::GetForProfile(profile())); else OnThemeChanged(NULL); } @@ -200,12 +240,10 @@ void BrowserInstantController::SetMarginSize(int start, int end) { } void BrowserInstantController::ResetInstant() { - Profile* profile = browser_->profile(); - - bool instant_enabled = IsInstantEnabled(profile); - bool use_local_preview_only = profile->IsOffTheRecord() || + bool instant_enabled = IsInstantEnabled(profile()); + bool use_local_preview_only = profile()->IsOffTheRecord() || (!instant_enabled && - !profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled)); + !profile()->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled)); instant_.SetInstantEnabled(instant_enabled, use_local_preview_only); } diff --git a/chrome/browser/ui/browser_instant_controller.h b/chrome/browser/ui/browser_instant_controller.h index f154992..1063988 100644 --- a/chrome/browser/ui/browser_instant_controller.h +++ b/chrome/browser/ui/browser_instant_controller.h @@ -43,23 +43,38 @@ class BrowserInstantController : public content::NotificationObserver, // Registers Instant related preferences. static void RegisterUserPrefs(PrefServiceSyncable* prefs); + // If |url| is the new tab page URL, set |target_contents| to the preloaded + // NTP contents from InstantController. If |source_contents| is not NULL, we + // replace it with the new |target_contents| in the tabstrip and delete + // |source_contents|. Otherwise, the caller owns |target_contents| and is + // responsible for inserting it into the tabstrip. + // + // Returns true if and only if we update |target_contents|. + bool MaybeSwapInInstantNTPContents( + const GURL& url, + content::WebContents* source_contents, + content::WebContents** target_contents); + // Commits the current Instant, returning true on success. This is intended // for use from OpenCurrentURL. bool OpenInstant(WindowOpenDisposition disposition); + // Returns the Profile associated with the Browser that owns this object. + Profile* profile() const; + // Returns the InstantController or NULL if there is no InstantController for // this BrowserInstantController. InstantController* instant() { return &instant_; } // Invoked by |instant_| to commit the |preview| by merging it into the active - // tab or adding it as a new tab. We take ownership of |preview|. - void CommitInstant(content::WebContents* preview, bool in_new_tab); + // tab or adding it as a new tab. + void CommitInstant(scoped_ptr<content::WebContents> preview, bool in_new_tab); // Invoked by |instant_| to autocomplete the |suggestion| into the omnibox. void SetInstantSuggestion(const InstantSuggestion& suggestion); // Invoked by |instant_| to get the bounds that the preview is placed at, - // in screen coordinated. + // in screen coordinates. gfx::Rect GetInstantBounds(); // Invoked by |instant_| to notify that the preview gained focus, usually due @@ -113,6 +128,11 @@ class BrowserInstantController : public content::NotificationObserver, // Helper for handling theme area height change. void OnThemeAreaHeightChanged(int height); + // Replaces the contents at tab |index| with |new_contents| and deletes the + // existing contents. + void ReplaceWebContentsAt(int index, + scoped_ptr<content::WebContents> new_contents); + Browser* const browser_; InstantController instant_; diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc index a3ad3eb..5b290b0 100644 --- a/chrome/browser/ui/browser_navigator.cc +++ b/chrome/browser/ui/browser_navigator.cc @@ -21,6 +21,7 @@ #include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_instant_controller.h" #include "chrome/browser/ui/browser_tab_contents.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/host_desktop.h" @@ -309,6 +310,41 @@ class ScopedTargetContentsOwner { DISALLOW_COPY_AND_ASSIGN(ScopedTargetContentsOwner); }; +content::WebContents* CreateTargetContents(const chrome::NavigateParams& params, + const GURL& url) { + WebContents::CreateParams create_params( + params.browser->profile(), + tab_util::GetSiteInstanceForNewTab(params.browser->profile(), url)); + if (params.source_contents) { + create_params.initial_size = + params.source_contents->GetView()->GetContainerSize(); + } +#if defined(USE_AURA) + if (params.browser->window() && + params.browser->window()->GetNativeWindow()) { + create_params.context = + params.browser->window()->GetNativeWindow(); + } +#endif + + content::WebContents* target_contents = WebContents::Create(create_params); + // New tabs can have WebUI URLs that will make calls back to arbitrary + // tab helpers, so the entire set of tab helpers needs to be set up + // immediately. + BrowserNavigatorWebContentsAdoption::AttachTabHelpers(target_contents); + extensions::TabHelper::FromWebContents(target_contents)-> + SetExtensionAppById(params.extension_app_id); + // TODO(sky): Figure out why this is needed. Without it we seem to get + // failures in startup tests. + // By default, content believes it is not hidden. When adding contents + // in the background, tell it that it's hidden. + if ((params.tabstrip_add_types & TabStripModel::ADD_ACTIVE) == 0) { + // TabStripModel::AddWebContents invokes WasHidden if not foreground. + target_contents->WasHidden(); + } + return target_contents; +} + // If a prerendered page exists for |url|, replace the page at |target_contents| // with it. bool SwapInPrerender(WebContents* target_contents, const GURL& url) { @@ -319,6 +355,15 @@ bool SwapInPrerender(WebContents* target_contents, const GURL& url) { prerender_manager->MaybeUsePrerenderedPage(target_contents, url); } +bool SwapInInstantNTP(chrome::NavigateParams* params, + const GURL& url, + content::WebContents* source_contents) { + chrome::BrowserInstantController* instant = + params->browser->instant_controller(); + return instant && instant->MaybeSwapInInstantNTPContents( + url, source_contents, ¶ms->target_contents); +} + } // namespace namespace chrome { @@ -466,6 +511,9 @@ void Navigate(NavigateParams* params) { // Check if this is a singleton tab that already exists int singleton_index = chrome::GetIndexOfSingletonTab(params); + // Did we use Instant's NTP contents? + bool swapped_in_instant = false; + // If no target WebContents was specified, we need to construct one if // we are supposed to target a new tab; unless it's a singleton that already // exists. @@ -480,59 +528,39 @@ void Navigate(NavigateParams* params) { } if (params->disposition != CURRENT_TAB) { - WebContents::CreateParams create_params( - params->browser->profile(), - tab_util::GetSiteInstanceForNewTab(params->browser->profile(), url)); - if (params->source_contents) { - create_params.initial_size = - params->source_contents->GetView()->GetContainerSize(); - } -#if defined(USE_AURA) - if (params->browser->window() && - params->browser->window()->GetNativeWindow()) { - create_params.context = - params->browser->window()->GetNativeWindow(); - } -#endif - params->target_contents = WebContents::Create(create_params); - // New tabs can have WebUI URLs that will make calls back to arbitrary - // tab helpers, so the entire set of tab helpers needs to be set up - // immediately. - BrowserNavigatorWebContentsAdoption::AttachTabHelpers( - params->target_contents); + swapped_in_instant = SwapInInstantNTP(params, url, NULL); + if (!swapped_in_instant) + params->target_contents = CreateTargetContents(*params, url); + // This function takes ownership of |params->target_contents| until it // is added to a TabStripModel. target_contents_owner.TakeOwnership(); - extensions::TabHelper::FromWebContents(params->target_contents)-> - SetExtensionAppById(params->extension_app_id); - // TODO(sky): Figure out why this is needed. Without it we seem to get - // failures in startup tests. - // By default, content believes it is not hidden. When adding contents - // in the background, tell it that it's hidden. - if ((params->tabstrip_add_types & TabStripModel::ADD_ACTIVE) == 0) { - // TabStripModel::AddWebContents invokes WasHidden if not foreground. - params->target_contents->WasHidden(); - } } else { // ... otherwise if we're loading in the current tab, the target is the // same as the source. - params->target_contents = params->source_contents; + DCHECK(params->source_contents); + swapped_in_instant = SwapInInstantNTP(params, url, + params->source_contents); + if (!swapped_in_instant) + params->target_contents = params->source_contents; DCHECK(params->target_contents); } if (user_initiated) params->target_contents->UserGestureDone(); - if (SwapInPrerender(params->target_contents, url)) - return; + if (!swapped_in_instant) { + if (SwapInPrerender(params->target_contents, url)) + return; - // Try to handle non-navigational URLs that popup dialogs and such, these - // should not actually navigate. - if (!HandleNonNavigationAboutURL(url)) { - // Perform the actual navigation, tracking whether it came from the - // renderer. + // Try to handle non-navigational URLs that popup dialogs and such, these + // should not actually navigate. + if (!HandleNonNavigationAboutURL(url)) { + // Perform the actual navigation, tracking whether it came from the + // renderer. - LoadURLInContents(params->target_contents, url, params); + LoadURLInContents(params->target_contents, url, params); + } } } else { // |target_contents| was specified non-NULL, and so we assume it has already @@ -549,7 +577,8 @@ void Navigate(NavigateParams* params) { (params->tabstrip_add_types & TabStripModel::ADD_INHERIT_OPENER)) params->source_contents->Focus(); - if (params->source_contents == params->target_contents) { + if (params->source_contents == params->target_contents || + (swapped_in_instant && params->disposition == CURRENT_TAB)) { // The navigation occurred in the source tab. params->browser->UpdateUIForNavigationInTab(params->target_contents, params->transition, diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc index 9bc6c73..186834fa 100644 --- a/chrome/browser/ui/search/search_tab_helper.cc +++ b/chrome/browser/ui/search/search_tab_helper.cc @@ -61,8 +61,7 @@ namespace chrome { namespace search { SearchTabHelper::SearchTabHelper(content::WebContents* web_contents) - : WebContentsObserver(web_contents), - is_search_enabled_(IsSearchEnabled(ProfileFromWebContents(web_contents))), + : is_search_enabled_(IsSearchEnabled(ProfileFromWebContents(web_contents))), user_input_in_progress_(false), model_(web_contents) { if (!is_search_enabled_) @@ -96,19 +95,6 @@ void SearchTabHelper::NavigationEntryUpdated() { UpdateModelBasedOnURL(web_contents()->GetURL()); } -void SearchTabHelper::NavigateToPendingEntry( - const GURL& url, - content::NavigationController::ReloadType reload_type) { - if (!is_search_enabled_) - return; - - // NTP mode changes are initiated at "pending", all others are initiated - // when "committed". This is because NTP is rendered natively so is faster - // to render than the web contents and we need to coordinate the animations. - if (IsNTP(url)) - UpdateModelBasedOnURL(url); -} - void SearchTabHelper::Observe( int type, const content::NotificationSource& source, @@ -116,10 +102,7 @@ void SearchTabHelper::Observe( DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type); content::LoadCommittedDetails* committed_details = content::Details<content::LoadCommittedDetails>(details).ptr(); - // See comment in |NavigateToPendingEntry()| about why |!IsNTP()| is used. - if (!IsNTP(committed_details->entry->GetURL())) { - UpdateModelBasedOnURL(committed_details->entry->GetURL()); - } + UpdateModelBasedOnURL(committed_details->entry->GetVirtualURL()); } void SearchTabHelper::UpdateModelBasedOnURL(const GURL& url) { diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h index db57c4e..114cc66 100644 --- a/chrome/browser/ui/search/search_tab_helper.h +++ b/chrome/browser/ui/search/search_tab_helper.h @@ -9,7 +9,6 @@ #include "chrome/browser/ui/search/search_model.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" -#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" class OmniboxEditModel; @@ -23,8 +22,7 @@ namespace search { // Per-tab search "helper". Acts as the owner and controller of the tab's // search UI model. -class SearchTabHelper : public content::WebContentsObserver, - public content::NotificationObserver, +class SearchTabHelper : public content::NotificationObserver, public content::WebContentsUserData<SearchTabHelper> { public: virtual ~SearchTabHelper(); @@ -43,11 +41,6 @@ class SearchTabHelper : public content::WebContentsObserver, // the notification system and shouldn't call this method. void NavigationEntryUpdated(); - // Overridden from contents::WebContentsObserver: - virtual void NavigateToPendingEntry( - const GURL& url, - content::NavigationController::ReloadType reload_type) OVERRIDE; - // Overridden from content::NotificationObserver: virtual void Observe(int type, const content::NotificationSource& source, diff --git a/chrome/browser/ui/toolbar/toolbar_model_impl.cc b/chrome/browser/ui/toolbar/toolbar_model_impl.cc index 1d20fa0..5c4c475 100644 --- a/chrome/browser/ui/toolbar/toolbar_model_impl.cc +++ b/chrome/browser/ui/toolbar/toolbar_model_impl.cc @@ -136,6 +136,9 @@ bool ToolbarModelImpl::ShouldDisplayURL() const { return false; #endif + if (entry && entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL)) + return false; + return true; } |