diff options
Diffstat (limited to 'chrome/browser/instant/instant_loader.cc')
| -rw-r--r-- | chrome/browser/instant/instant_loader.cc | 287 |
1 files changed, 100 insertions, 187 deletions
diff --git a/chrome/browser/instant/instant_loader.cc b/chrome/browser/instant/instant_loader.cc index 3c561b3..7147b7c 100644 --- a/chrome/browser/instant/instant_loader.cc +++ b/chrome/browser/instant/instant_loader.cc @@ -7,8 +7,10 @@ #include <algorithm> #include <utility> +#include "app/l10n_util.h" #include "base/command_line.h" #include "base/string_number_conversions.h" +#include "base/timer.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/favicon_service.h" @@ -24,6 +26,7 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" #include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" @@ -34,104 +37,19 @@ #include "ipc/ipc_message.h" namespace { - -// Script sent as the user is typing and the provider supports instant. -// Params: -// . the text the user typed. -// TODO: add support for the 2nd and 3rd params. -const char kUserInputScript[] = - "if (window.chrome.userInput) window.chrome.userInput(\"$1\", 0, 0);"; - -// Script sent when the page is committed and the provider supports instant. -// Params: -// . the text the user typed. -// . boolean indicating if the user pressed enter to accept the text. -const char kUserDoneScript[] = - "if (window.chrome.userWantsQuery) " - "window.chrome.userWantsQuery(\"$1\", $2);"; - -// Script sent when the bounds of the omnibox changes and the provider supports -// instant. The params are the bounds relative to the origin of the preview -// (x, y, width, height). -const char kSetOmniboxBoundsScript[] = - "if (window.chrome.setDropdownDimensions) " - "window.chrome.setDropdownDimensions($1, $2, $3, $4);"; - -// Script sent to see if the page really supports instant. -const char kSupportsInstantScript[] = - "if (window.chrome.setDropdownDimensions) true; else false;"; - // Number of ms to delay before updating the omnibox bounds. This is a bit long // as updating the bounds ends up being quite expensive. const int kUpdateBoundsDelayMS = 500; - -// Escapes quotes in the |text| so that it be passed to JavaScript as a quoted -// string. -string16 EscapeUserText(const string16& text) { - string16 escaped_text(text); - ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""), - ASCIIToUTF16("\\\"")); - return escaped_text; -} - -// Sends the script for when the user commits the preview. |pressed_enter| is -// true if the user pressed enter to commit. -void SendDoneScript(TabContents* tab_contents, - const string16& text, - bool pressed_enter) { - std::vector<string16> params; - params.push_back(EscapeUserText(text)); - params.push_back(pressed_enter ? ASCIIToUTF16("true") : - ASCIIToUTF16("false")); - string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserDoneScript), - params, - NULL); - tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( - std::wstring(), - UTF16ToWide(script)); -} - -// Sends the user input script to |tab_contents|. |text| is the text the user -// input into the omnibox. -void SendUserInputScript(TabContents* tab_contents, const string16& text) { - string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript), - EscapeUserText(text), - NULL); - tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( - std::wstring(), - UTF16ToWide(script)); -} - -// Sends the script for setting the bounds of the omnibox to |tab_contents|. -void SendOmniboxBoundsScript(TabContents* tab_contents, - const gfx::Rect& bounds) { - std::vector<string16> bounds_vector; - bounds_vector.push_back(base::IntToString16(bounds.x())); - bounds_vector.push_back(base::IntToString16(bounds.y())); - bounds_vector.push_back(base::IntToString16(bounds.width())); - bounds_vector.push_back(base::IntToString16(bounds.height())); - string16 script = ReplaceStringPlaceholders( - ASCIIToUTF16(kSetOmniboxBoundsScript), - bounds_vector, - NULL); - tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( - std::wstring(), - UTF16ToWide(script)); -} - } // namespace -// FrameLoadObserver is responsible for waiting for the TabContents to finish -// loading and when done sending the necessary script down to the page. +// FrameLoadObserver is responsible for determining if the page supports +// instant after it has loaded. class InstantLoader::FrameLoadObserver : public NotificationObserver { public: - FrameLoadObserver(InstantLoader* loader, const string16& text) - : loader_(loader), - tab_contents_(loader->preview_contents()), - unique_id_(tab_contents_->controller().pending_entry()->unique_id()), + FrameLoadObserver(TabContents* tab_contents, const string16& text) + : tab_contents_(tab_contents), text_(text), - initial_text_(text), - execute_js_id_(0) { + unique_id_(tab_contents_->controller().pending_entry()->unique_id()) { registrar_.Add(this, NotificationType::LOAD_COMPLETED_MAIN_FRAME, Source<TabContents>(tab_contents_)); } @@ -152,29 +70,10 @@ class InstantLoader::FrameLoadObserver : public NotificationObserver { active_entry->unique_id() != unique_id_) { return; } - - DetermineIfPageSupportsInstant(); + tab_contents_->render_view_host()->DetermineIfPageSupportsInstant( + text_); break; } - - case NotificationType::EXECUTE_JAVASCRIPT_RESULT: { - typedef std::pair<int, Value*> ExecuteDetailType; - ExecuteDetailType* result = - (static_cast<Details<ExecuteDetailType > >(details)).ptr(); - if (result->first != execute_js_id_) - return; - - DCHECK(loader_); - bool bool_result; - if (!result->second || !result->second->IsType(Value::TYPE_BOOLEAN) || - !result->second->GetAsBoolean(&bool_result) || !bool_result) { - DoesntSupportInstant(); - return; - } - SupportsInstant(); - return; - } - default: NOTREACHED(); break; @@ -182,66 +81,18 @@ class InstantLoader::FrameLoadObserver : public NotificationObserver { } private: - // Executes the javascript to determine if the page supports script. The - // results are passed back to us by way of NotificationObserver. - void DetermineIfPageSupportsInstant() { - DCHECK_EQ(0, execute_js_id_); - - RenderViewHost* rvh = tab_contents_->render_view_host(); - registrar_.Add(this, NotificationType::EXECUTE_JAVASCRIPT_RESULT, - Source<RenderViewHost>(rvh)); - execute_js_id_ = rvh->ExecuteJavascriptInWebFrameNotifyResult( - string16(), - ASCIIToUTF16(kSupportsInstantScript)); - } - - // Invoked when we determine the page doesn't really support instant. - void DoesntSupportInstant() { - DCHECK(loader_); - - loader_->PageDoesntSupportInstant(text_ != initial_text_); - - // WARNING: we've been deleted. - } - - // Invoked when we determine the page really supports instant. - void SupportsInstant() { - DCHECK(loader_); - - gfx::Rect bounds = loader_->GetOmniboxBoundsInTermsOfPreview(); - loader_->last_omnibox_bounds_ = loader_->omnibox_bounds_; - if (!bounds.IsEmpty()) - SendOmniboxBoundsScript(tab_contents_, bounds); - - SendUserInputScript(tab_contents_, text_); - - loader_->PageFinishedLoading(); - - // WARNING: we've been deleted. - } - - // InstantLoader that created us. - InstantLoader* loader_; - // The TabContents we're listening for changes on. TabContents* tab_contents_; - // unique_id of the NavigationEntry we're waiting on. - const int unique_id_; - // Text to send down to the page. string16 text_; - // Initial text supplied to constructor. - const string16 initial_text_; + // unique_id of the NavigationEntry we're waiting on. + const int unique_id_; // Registers and unregisters us for notifications. NotificationRegistrar registrar_; - // ID of the javascript that was executed to determine if the page supports - // instant. - int execute_js_id_; - DISALLOW_COPY_AND_ASSIGN(FrameLoadObserver); }; @@ -274,11 +125,13 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { : loader_(loader), installed_paint_observer_(false), waiting_for_new_page_(true), - is_mouse_down_from_activate_(false) { + is_mouse_down_from_activate_(false), + user_typed_before_load_(false) { } // Invoked prior to loading a new URL. void PrepareForNewLoad() { + user_typed_before_load_ = false; waiting_for_new_page_ = true; add_page_vector_.clear(); } @@ -294,6 +147,8 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { return is_mouse_down_from_activate_; } + void set_user_typed_before_load() { user_typed_before_load_ = true; } + // Commits the currently buffered history. void CommitHistory() { TabContents* tab = loader_->preview_contents(); @@ -353,7 +208,7 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { virtual bool IsPopup(const TabContents* source) const { return false; } - virtual bool ShouldFocusConstrainedWindow(TabContents* source) { + virtual bool ShouldFocusConstrainedWindow() { // Return false so that constrained windows are not initially focused. If // we did otherwise the preview would prematurely get committed when focus // goes to the constrained window. @@ -460,12 +315,33 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { virtual void UpdatePreferredSize(const gfx::Size& pref_size) {} virtual void ContentTypeChanged(TabContents* source) {} - virtual void OnSetSuggestResult(int32 page_id, const std::string& result) { + virtual void OnSetSuggestions(int32 page_id, + const std::vector<std::string>& suggestions) { TabContents* source = loader_->preview_contents(); + if (!source->controller().GetActiveEntry() || + page_id != source->controller().GetActiveEntry()->page_id()) + return; + // TODO: only allow for default search provider. - if (source->controller().GetActiveEntry() && - page_id == source->controller().GetActiveEntry()->page_id()) { - loader_->SetCompleteSuggestedText(UTF8ToUTF16(result)); + // TODO(sky): Handle multiple suggestions. + if (suggestions.empty()) + loader_->SetCompleteSuggestedText(string16()); + else + loader_->SetCompleteSuggestedText(UTF8ToUTF16(suggestions[0])); + } + + virtual void OnInstantSupportDetermined(int32 page_id, bool result) { + TabContents* source = loader_->preview_contents(); + if (!source->controller().GetActiveEntry() || + page_id != source->controller().GetActiveEntry()->page_id()) + return; + + if (result) { + gfx::Rect bounds = loader_->GetOmniboxBoundsInTermsOfPreview(); + loader_->last_omnibox_bounds_ = loader_->omnibox_bounds_; + loader_->PageFinishedLoading(); + } else { + loader_->PageDoesntSupportInstant(user_typed_before_load_); } } @@ -496,9 +372,12 @@ class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { // NEW_PAGE navigation we don't add history items to add_page_vector_. bool waiting_for_new_page_; - // Returns true if the mouse is down from an activate. + // True if the mouse is down from an activate. bool is_mouse_down_from_activate_; + // True if the user typed in the search box before the page loaded. + bool user_typed_before_load_; + DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); }; @@ -563,23 +442,40 @@ void InstantLoader::Update(TabContents* tab_contents, if (is_waiting_for_load()) { // The page hasn't loaded yet. We'll send the script down when it does. frame_load_observer_->set_text(user_text_); + preview_tab_contents_delegate_->set_user_typed_before_load(); return; } - SendUserInputScript(preview_contents_.get(), user_text_); - if (complete_suggested_text_.size() > user_text_.size() && - !complete_suggested_text_.compare(0, user_text_.size(), user_text_)) { + preview_contents_->render_view_host()->SearchBoxChange( + user_text_, false, 0, 0); + + string16 complete_suggested_text_lower = l10n_util::ToLower( + complete_suggested_text_); + string16 user_text_lower = l10n_util::ToLower(user_text_); + if (complete_suggested_text_lower.size() > user_text_lower.size() && + !complete_suggested_text_lower.compare(0, user_text_lower.size(), + user_text_lower)) { *suggested_text = complete_suggested_text_.substr(user_text_.size()); } } else { // Load the instant URL. We don't reflect the url we load in url() as // callers expect that we're loading the URL they tell us to. + // + // This uses an empty string for the replacement text as the url doesn't + // really have the search params, but we need to use the replace + // functionality so that embeded tags (like {google:baseURL}) are escaped + // correctly. + // TODO(sky): having to use a replaceable url is a bit of a hack here. GURL instant_url( template_url->instant_url()->ReplaceSearchTerms( - *template_url, UTF16ToWideHack(user_text), -1, std::wstring())); + *template_url, std::wstring(), -1, std::wstring())); + CommandLine* cl = CommandLine::ForCurrentProcess(); + if (cl->HasSwitch(switches::kInstantURL)) + instant_url = GURL(cl->GetSwitchValueASCII(switches::kInstantURL)); initial_instant_url_ = url; preview_contents_->controller().LoadURL( instant_url, GURL(), transition_type); - frame_load_observer_.reset(new FrameLoadObserver(this, user_text_)); + frame_load_observer_.reset( + new FrameLoadObserver(preview_contents(), user_text_)); } } else { DCHECK(template_url_id_ == 0); @@ -619,9 +515,11 @@ TabContents* InstantLoader::ReleasePreviewContents(InstantCommitType type) { DCHECK(type == INSTANT_COMMIT_DESTROY || !frame_load_observer_.get()); if (type != INSTANT_COMMIT_DESTROY && is_showing_instant()) { - SendDoneScript(preview_contents_.get(), - user_text_, - type == INSTANT_COMMIT_PRESSED_ENTER); + if (type == INSTANT_COMMIT_FOCUS_LOST) + preview_contents_->render_view_host()->SearchBoxCancel(); + else + preview_contents_->render_view_host()->SearchBoxSubmit( + user_text_, type == INSTANT_COMMIT_PRESSED_ENTER); } omnibox_bounds_ = gfx::Rect(); last_omnibox_bounds_ = gfx::Rect(); @@ -678,8 +576,12 @@ void InstantLoader::SetCompleteSuggestedText( if (complete_suggested_text == complete_suggested_text_) return; - if (user_text_.compare(0, user_text_.size(), complete_suggested_text, - 0, user_text_.size())) { + string16 user_text_lower = l10n_util::ToLower(user_text_); + string16 complete_suggested_text_lower = l10n_util::ToLower( + complete_suggested_text); + if (user_text_lower.compare(0, user_text_lower.size(), + complete_suggested_text_lower, + 0, user_text_lower.size())) { // The user text no longer contains the suggested text, ignore it. complete_suggested_text_.clear(); delegate_->SetSuggestedTextFor(this, string16()); @@ -718,15 +620,26 @@ void InstantLoader::PageFinishedLoading() { // date by the time we show it. } +// 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). gfx::Rect InstantLoader::GetOmniboxBoundsInTermsOfPreview() { - if (omnibox_bounds_.IsEmpty()) - return omnibox_bounds_; - gfx::Rect preview_bounds(delegate_->GetInstantBounds()); - return gfx::Rect(omnibox_bounds_.x() - preview_bounds.x(), - omnibox_bounds_.y() - preview_bounds.y(), - omnibox_bounds_.width(), - omnibox_bounds_.height()); + gfx::Rect intersection(omnibox_bounds_.Intersect(preview_bounds)); + + // Translate into window's coordinates. + if (!intersection.IsEmpty()) { + intersection.Offset(-preview_bounds.origin().x(), + -preview_bounds.origin().y()); + } + + // In the current Chrome UI, these must always be true so they sanity check + // the above operations. In a future UI, these may be removed or adjusted. + DCHECK_EQ(0, intersection.y()); + DCHECK_LE(0, intersection.x()); + DCHECK_LE(0, intersection.width()); + DCHECK_LE(0, intersection.height()); + + return intersection; } void InstantLoader::PageDoesntSupportInstant(bool needs_reload) { @@ -749,7 +662,7 @@ void InstantLoader::ProcessBoundsChange() { last_omnibox_bounds_ = omnibox_bounds_; if (preview_contents_.get() && is_showing_instant() && !is_waiting_for_load()) { - SendOmniboxBoundsScript(preview_contents_.get(), - GetOmniboxBoundsInTermsOfPreview()); + preview_contents_->render_view_host()->SearchBoxResize( + GetOmniboxBoundsInTermsOfPreview()); } } |
