instant_loader.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/instant/instant_loader.h" 6 7#include <algorithm> 8#include <utility> 9 10#include "base/command_line.h" 11#include "base/string_number_conversions.h" 12#include "base/utf_string_conversions.h" 13#include "base/values.h" 14#include "chrome/browser/favicon_service.h" 15#include "chrome/browser/history/history_marshaling.h" 16#include "chrome/browser/instant/instant_loader_delegate.h" 17#include "chrome/browser/profile.h" 18#include "chrome/browser/renderer_host/render_view_host.h" 19#include "chrome/browser/renderer_host/render_widget_host.h" 20#include "chrome/browser/renderer_host/render_widget_host_view.h" 21#include "chrome/browser/search_engines/template_url.h" 22#include "chrome/browser/tab_contents/navigation_controller.h" 23#include "chrome/browser/tab_contents/navigation_entry.h" 24#include "chrome/browser/tab_contents/tab_contents.h" 25#include "chrome/browser/tab_contents/tab_contents_delegate.h" 26#include "chrome/browser/tab_contents/tab_contents_view.h" 27#include "chrome/common/notification_observer.h" 28#include "chrome/common/notification_registrar.h" 29#include "chrome/common/notification_service.h" 30#include "chrome/common/page_transition_types.h" 31#include "chrome/common/render_messages.h" 32#include "chrome/common/renderer_preferences.h" 33#include "gfx/codec/png_codec.h" 34#include "ipc/ipc_message.h" 35 36namespace { 37 38// Script sent as the user is typing and the provider supports instant. 39// Params: 40// . the text the user typed. 41// TODO: add support for the 2nd and 3rd params. 42const char kUserInputScript[] = 43 "if (window.chrome.userInput) window.chrome.userInput(\"$1\", 0, 0);"; 44 45// Script sent when the page is committed and the provider supports instant. 46// Params: 47// . the text the user typed. 48// . boolean indicating if the user pressed enter to accept the text. 49const char kUserDoneScript[] = 50 "if (window.chrome.userWantsQuery) " 51 "window.chrome.userWantsQuery(\"$1\", $2);"; 52 53// Script sent when the bounds of the omnibox changes and the provider supports 54// instant. The params are the bounds relative to the origin of the preview 55// (x, y, width, height). 56const char kSetOmniboxBoundsScript[] = 57 "if (window.chrome.setDropdownDimensions) " 58 "window.chrome.setDropdownDimensions($1, $2, $3, $4);"; 59 60// Script sent to see if the page really supports instant. 61const char kSupportsInstantScript[] = 62 "if (window.chrome.setDropdownDimensions) true; else false;"; 63 64// Number of ms to delay before updating the omnibox bounds. This is a bit long 65// as updating the bounds ends up being quite expensive. 66const int kUpdateBoundsDelayMS = 500; 67 68// Escapes quotes in the |text| so that it be passed to JavaScript as a quoted 69// string. 70string16 EscapeUserText(const string16& text) { 71 string16 escaped_text(text); 72 ReplaceSubstringsAfterOffset(&escaped_text, 0L, ASCIIToUTF16("\""), 73 ASCIIToUTF16("\\\"")); 74 return escaped_text; 75} 76 77// Sends the script for when the user commits the preview. |pressed_enter| is 78// true if the user pressed enter to commit. 79void SendDoneScript(TabContents* tab_contents, 80 const string16& text, 81 bool pressed_enter) { 82 std::vector<string16> params; 83 params.push_back(EscapeUserText(text)); 84 params.push_back(pressed_enter ? ASCIIToUTF16("true") : 85 ASCIIToUTF16("false")); 86 string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserDoneScript), 87 params, 88 NULL); 89 tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( 90 std::wstring(), 91 UTF16ToWide(script)); 92} 93 94// Sends the user input script to |tab_contents|. |text| is the text the user 95// input into the omnibox. 96void SendUserInputScript(TabContents* tab_contents, const string16& text) { 97 string16 script = ReplaceStringPlaceholders(ASCIIToUTF16(kUserInputScript), 98 EscapeUserText(text), 99 NULL); 100 tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( 101 std::wstring(), 102 UTF16ToWide(script)); 103} 104 105// Sends the script for setting the bounds of the omnibox to |tab_contents|. 106void SendOmniboxBoundsScript(TabContents* tab_contents, 107 const gfx::Rect& bounds) { 108 std::vector<string16> bounds_vector; 109 bounds_vector.push_back(base::IntToString16(bounds.x())); 110 bounds_vector.push_back(base::IntToString16(bounds.y())); 111 bounds_vector.push_back(base::IntToString16(bounds.width())); 112 bounds_vector.push_back(base::IntToString16(bounds.height())); 113 string16 script = ReplaceStringPlaceholders( 114 ASCIIToUTF16(kSetOmniboxBoundsScript), 115 bounds_vector, 116 NULL); 117 tab_contents->render_view_host()->ExecuteJavascriptInWebFrame( 118 std::wstring(), 119 UTF16ToWide(script)); 120} 121 122} // namespace 123 124// FrameLoadObserver is responsible for waiting for the TabContents to finish 125// loading and when done sending the necessary script down to the page. 126class InstantLoader::FrameLoadObserver : public NotificationObserver { 127 public: 128 FrameLoadObserver(InstantLoader* loader, const string16& text) 129 : loader_(loader), 130 tab_contents_(loader->preview_contents()), 131 unique_id_(tab_contents_->controller().pending_entry()->unique_id()), 132 text_(text), 133 initial_text_(text), 134 execute_js_id_(0) { 135 registrar_.Add(this, NotificationType::LOAD_COMPLETED_MAIN_FRAME, 136 Source<TabContents>(tab_contents_)); 137 } 138 139 // Sets the text to send to the page. 140 void set_text(const string16& text) { text_ = text; } 141 142 // NotificationObserver: 143 virtual void Observe(NotificationType type, 144 const NotificationSource& source, 145 const NotificationDetails& details) { 146 switch (type.value) { 147 case NotificationType::LOAD_COMPLETED_MAIN_FRAME: { 148 int page_id = *(Details<int>(details).ptr()); 149 NavigationEntry* active_entry = 150 tab_contents_->controller().GetActiveEntry(); 151 if (!active_entry || active_entry->page_id() != page_id || 152 active_entry->unique_id() != unique_id_) { 153 return; 154 } 155 156 DetermineIfPageSupportsInstant(); 157 break; 158 } 159 160 case NotificationType::EXECUTE_JAVASCRIPT_RESULT: { 161 typedef std::pair<int, Value*> ExecuteDetailType; 162 ExecuteDetailType* result = 163 (static_cast<Details<ExecuteDetailType > >(details)).ptr(); 164 if (result->first != execute_js_id_) 165 return; 166 167 DCHECK(loader_); 168 bool bool_result; 169 if (!result->second || !result->second->IsType(Value::TYPE_BOOLEAN) || 170 !result->second->GetAsBoolean(&bool_result) || !bool_result) { 171 DoesntSupportInstant(); 172 return; 173 } 174 SupportsInstant(); 175 return; 176 } 177 178 default: 179 NOTREACHED(); 180 break; 181 } 182 } 183 184 private: 185 // Executes the javascript to determine if the page supports script. The 186 // results are passed back to us by way of NotificationObserver. 187 void DetermineIfPageSupportsInstant() { 188 DCHECK_EQ(0, execute_js_id_); 189 190 RenderViewHost* rvh = tab_contents_->render_view_host(); 191 registrar_.Add(this, NotificationType::EXECUTE_JAVASCRIPT_RESULT, 192 Source<RenderViewHost>(rvh)); 193 execute_js_id_ = rvh->ExecuteJavascriptInWebFrameNotifyResult( 194 string16(), 195 ASCIIToUTF16(kSupportsInstantScript)); 196 } 197 198 // Invoked when we determine the page doesn't really support instant. 199 void DoesntSupportInstant() { 200 DCHECK(loader_); 201 202 loader_->PageDoesntSupportInstant(text_ != initial_text_); 203 204 // WARNING: we've been deleted. 205 } 206 207 // Invoked when we determine the page really supports instant. 208 void SupportsInstant() { 209 DCHECK(loader_); 210 211 gfx::Rect bounds = loader_->GetOmniboxBoundsInTermsOfPreview(); 212 loader_->last_omnibox_bounds_ = loader_->omnibox_bounds_; 213 if (!bounds.IsEmpty()) 214 SendOmniboxBoundsScript(tab_contents_, bounds); 215 216 SendUserInputScript(tab_contents_, text_); 217 218 loader_->PageFinishedLoading(); 219 220 // WARNING: we've been deleted. 221 } 222 223 // InstantLoader that created us. 224 InstantLoader* loader_; 225 226 // The TabContents we're listening for changes on. 227 TabContents* tab_contents_; 228 229 // unique_id of the NavigationEntry we're waiting on. 230 const int unique_id_; 231 232 // Text to send down to the page. 233 string16 text_; 234 235 // Initial text supplied to constructor. 236 const string16 initial_text_; 237 238 // Registers and unregisters us for notifications. 239 NotificationRegistrar registrar_; 240 241 // ID of the javascript that was executed to determine if the page supports 242 // instant. 243 int execute_js_id_; 244 245 DISALLOW_COPY_AND_ASSIGN(FrameLoadObserver); 246}; 247 248// PaintObserver implementation. When the RenderWidgetHost paints itself this 249// notifies InstantLoader, which makes the TabContents active. 250class InstantLoader::PaintObserverImpl 251 : public RenderWidgetHost::PaintObserver { 252 public: 253 explicit PaintObserverImpl(InstantLoader* loader) : loader_(loader) { 254 } 255 256 virtual void RenderWidgetHostWillPaint(RenderWidgetHost* rwh) { 257 } 258 259 virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) { 260 loader_->PreviewPainted(); 261 rwh->set_paint_observer(NULL); 262 // WARNING: we've been deleted. 263 } 264 265 private: 266 InstantLoader* loader_; 267 268 DISALLOW_COPY_AND_ASSIGN(PaintObserverImpl); 269}; 270 271class InstantLoader::TabContentsDelegateImpl : public TabContentsDelegate { 272 public: 273 explicit TabContentsDelegateImpl(InstantLoader* loader) 274 : loader_(loader), 275 installed_paint_observer_(false), 276 waiting_for_new_page_(true), 277 is_mouse_down_from_activate_(false) { 278 } 279 280 // Invoked prior to loading a new URL. 281 void PrepareForNewLoad() { 282 waiting_for_new_page_ = true; 283 add_page_vector_.clear(); 284 } 285 286 // Invoked when removed as the delegate. Gives a chance to do any necessary 287 // cleanup. 288 void Reset() { 289 installed_paint_observer_ = false; 290 is_mouse_down_from_activate_ = false; 291 } 292 293 bool is_mouse_down_from_activate() const { 294 return is_mouse_down_from_activate_; 295 } 296 297 // Commits the currently buffered history. 298 void CommitHistory() { 299 TabContents* tab = loader_->preview_contents(); 300 if (tab->profile()->IsOffTheRecord()) 301 return; 302 303 for (size_t i = 0; i < add_page_vector_.size(); ++i) 304 tab->UpdateHistoryForNavigation(add_page_vector_[i].get()); 305 306 NavigationEntry* active_entry = tab->controller().GetActiveEntry(); 307 DCHECK(active_entry); 308 tab->UpdateHistoryPageTitle(*active_entry); 309 310 FaviconService* favicon_service = 311 tab->profile()->GetFaviconService(Profile::EXPLICIT_ACCESS); 312 313 if (favicon_service && active_entry->favicon().is_valid() && 314 !active_entry->favicon().bitmap().empty()) { 315 std::vector<unsigned char> image_data; 316 gfx::PNGCodec::EncodeBGRASkBitmap(active_entry->favicon().bitmap(), false, 317 &image_data); 318 favicon_service->SetFavicon(active_entry->url(), 319 active_entry->favicon().url(), 320 image_data); 321 } 322 } 323 324 virtual void OpenURLFromTab(TabContents* source, 325 const GURL& url, const GURL& referrer, 326 WindowOpenDisposition disposition, 327 PageTransition::Type transition) {} 328 virtual void NavigationStateChanged(const TabContents* source, 329 unsigned changed_flags) { 330 if (!installed_paint_observer_ && source->controller().entry_count()) { 331 // The load has been committed. Install an observer that waits for the 332 // first paint then makes the preview active. We wait for the load to be 333 // committed before waiting on paint as there is always an initial paint 334 // when a new renderer is created from the resize so that if we showed the 335 // preview after the first paint we would end up with a white rect. 336 installed_paint_observer_ = true; 337 source->GetRenderWidgetHostView()->GetRenderWidgetHost()-> 338 set_paint_observer(new PaintObserverImpl(loader_)); 339 } 340 } 341 virtual void AddNewContents(TabContents* source, 342 TabContents* new_contents, 343 WindowOpenDisposition disposition, 344 const gfx::Rect& initial_pos, 345 bool user_gesture) {} 346 virtual void ActivateContents(TabContents* contents) { 347 } 348 virtual void DeactivateContents(TabContents* contents) {} 349 virtual void LoadingStateChanged(TabContents* source) {} 350 virtual void CloseContents(TabContents* source) {} 351 virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {} 352 virtual void DetachContents(TabContents* source) {} 353 virtual bool IsPopup(const TabContents* source) const { 354 return false; 355 } 356 virtual bool ShouldFocusConstrainedWindow(TabContents* source) { 357 // Return false so that constrained windows are not initially focused. If 358 // we did otherwise the preview would prematurely get committed when focus 359 // goes to the constrained window. 360 return false; 361 } 362 virtual void WillShowConstrainedWindow(TabContents* source) { 363 if (!loader_->ready()) { 364 // A constrained window shown for an auth may not paint. Show the preview 365 // contents. 366 if (installed_paint_observer_) { 367 source->GetRenderWidgetHostView()->GetRenderWidgetHost()-> 368 set_paint_observer(NULL); 369 } 370 installed_paint_observer_ = true; 371 loader_->ShowPreview(); 372 } 373 } 374 virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} 375 virtual void URLStarredChanged(TabContents* source, bool starred) {} 376 virtual void UpdateTargetURL(TabContents* source, const GURL& url) {} 377 virtual void ContentsMouseEvent( 378 TabContents* source, const gfx::Point& location, bool motion) {} 379 virtual void ContentsZoomChange(bool zoom_in) {} 380 virtual void OnContentSettingsChange(TabContents* source) {} 381 virtual bool IsApplication() const { return false; } 382 virtual void ConvertContentsToApplication(TabContents* source) {} 383 virtual bool CanReloadContents(TabContents* source) const { return true; } 384 virtual gfx::Rect GetRootWindowResizerRect() const { 385 return gfx::Rect(); 386 } 387 virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, 388 gfx::NativeWindow parent_window) {} 389 virtual void BeforeUnloadFired(TabContents* tab, 390 bool proceed, 391 bool* proceed_to_fire_unload) {} 392 virtual void ForwardMessageToExternalHost(const std::string& message, 393 const std::string& origin, 394 const std::string& target) {} 395 virtual bool IsExternalTabContainer() const { return false; } 396 virtual void SetFocusToLocationBar(bool select_all) {} 397 virtual bool ShouldFocusPageAfterCrash() { return false; } 398 virtual void RenderWidgetShowing() {} 399 virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( 400 RenderViewHost* render_view_host, 401 const std::string& extension_id) { 402 return NULL; 403 } 404 virtual bool TakeFocus(bool reverse) { return false; } 405 virtual void LostCapture() { 406 CommitFromMouseReleaseIfNecessary(); 407 } 408 virtual void SetTabContentBlocked(TabContents* contents, bool blocked) {} 409 virtual void TabContentsFocused(TabContents* tab_content) { 410 } 411 virtual int GetExtraRenderViewHeight() const { return 0; } 412 virtual bool CanDownload(int request_id) { return false; } 413 virtual void OnStartDownload(DownloadItem* download, TabContents* tab) {} 414 virtual bool HandleContextMenu(const ContextMenuParams& params) { 415 return false; 416 } 417 virtual bool ExecuteContextMenuCommand(int command) { 418 return false; 419 } 420 virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, 421 Profile* profile) {} 422 virtual void ShowPageInfo(Profile* profile, 423 const GURL& url, 424 const NavigationEntry::SSLStatus& ssl, 425 bool show_history) {} 426 virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, 427 bool* is_keyboard_shortcut) { 428 return false; 429 } 430 virtual void HandleMouseUp() { 431 CommitFromMouseReleaseIfNecessary(); 432 } 433 virtual void HandleMouseActivate() { 434 is_mouse_down_from_activate_ = true; 435 } 436 virtual void ShowRepostFormWarningDialog(TabContents* tab_contents) {} 437 virtual void ShowContentSettingsWindow(ContentSettingsType content_type) {} 438 virtual void ShowCollectedCookiesDialog(TabContents* tab_contents) {} 439 virtual bool OnGoToEntryOffset(int offset) { return false; } 440 virtual bool ShouldAddNavigationToHistory( 441 const history::HistoryAddPageArgs& add_page_args, 442 NavigationType::Type navigation_type) { 443 if (waiting_for_new_page_ && navigation_type == NavigationType::NEW_PAGE) 444 waiting_for_new_page_ = false; 445 446 if (!waiting_for_new_page_) { 447 add_page_vector_.push_back( 448 scoped_refptr<history::HistoryAddPageArgs>(add_page_args.Clone())); 449 } 450 return false; 451 } 452 virtual void OnDidGetApplicationInfo(TabContents* tab_contents, 453 int32 page_id) {} 454 virtual gfx::NativeWindow GetFrameNativeWindow() { 455 return NULL; 456 } 457 virtual void TabContentsCreated(TabContents* new_contents) {} 458 virtual bool infobars_enabled() { return false; } 459 virtual bool ShouldEnablePreferredSizeNotifications() { return false; } 460 virtual void UpdatePreferredSize(const gfx::Size& pref_size) {} 461 virtual void ContentTypeChanged(TabContents* source) {} 462 463 virtual void OnSetSuggestResult(int32 page_id, const std::string& result) { 464 TabContents* source = loader_->preview_contents(); 465 // TODO: only allow for default search provider. 466 if (source->controller().GetActiveEntry() && 467 page_id == source->controller().GetActiveEntry()->page_id()) { 468 loader_->SetCompleteSuggestedText(UTF8ToUTF16(result)); 469 } 470 } 471 472 private: 473 typedef std::vector<scoped_refptr<history::HistoryAddPageArgs> > 474 AddPageVector; 475 476 void CommitFromMouseReleaseIfNecessary() { 477 bool was_down = is_mouse_down_from_activate_; 478 is_mouse_down_from_activate_ = false; 479 if (was_down && loader_->ShouldCommitInstantOnMouseUp()) 480 loader_->CommitInstantLoader(); 481 } 482 483 InstantLoader* loader_; 484 485 // Has the paint observer been installed? See comment in 486 // NavigationStateChanged for details on this. 487 bool installed_paint_observer_; 488 489 // Used to cache data that needs to be added to history. Normally entries are 490 // added to history as the user types, but for instant we only want to add the 491 // items to history if the user commits instant. So, we cache them here and if 492 // committed then add the items to history. 493 AddPageVector add_page_vector_; 494 495 // Are we we waiting for a NavigationType of NEW_PAGE? If we're waiting for 496 // NEW_PAGE navigation we don't add history items to add_page_vector_. 497 bool waiting_for_new_page_; 498 499 // Returns true if the mouse is down from an activate. 500 bool is_mouse_down_from_activate_; 501 502 DISALLOW_COPY_AND_ASSIGN(TabContentsDelegateImpl); 503}; 504 505InstantLoader::InstantLoader(InstantLoaderDelegate* delegate, TemplateURLID id) 506 : delegate_(delegate), 507 template_url_id_(id), 508 ready_(false), 509 last_transition_type_(PageTransition::LINK) { 510 preview_tab_contents_delegate_.reset(new TabContentsDelegateImpl(this)); 511} 512 513InstantLoader::~InstantLoader() { 514 // Delete the TabContents before the delegate as the TabContents holds a 515 // reference to the delegate. 516 preview_contents_.reset(NULL); 517} 518 519void InstantLoader::Update(TabContents* tab_contents, 520 const TemplateURL* template_url, 521 const GURL& url, 522 PageTransition::Type transition_type, 523 const string16& user_text, 524 string16* suggested_text) { 525 if (url_ == url) 526 return; 527 528 DCHECK(!url.is_empty() && url.is_valid()); 529 530 last_transition_type_ = transition_type; 531 url_ = url; 532 user_text_ = user_text; 533 534 bool created_preview_contents; 535 if (preview_contents_.get() == NULL) { 536 preview_contents_.reset( 537 new TabContents(tab_contents->profile(), NULL, MSG_ROUTING_NONE, 538 NULL, NULL)); 539 preview_contents_->SetAllContentsBlocked(true); 540 // Propagate the max page id. That way if we end up merging the two 541 // NavigationControllers (which happens if we commit) none of the page ids 542 // will overlap. 543 int32 max_page_id = tab_contents->GetMaxPageID(); 544 if (max_page_id != -1) 545 preview_contents_->controller().set_max_restored_page_id(max_page_id + 1); 546 547 preview_contents_->set_delegate(preview_tab_contents_delegate_.get()); 548 549 gfx::Rect tab_bounds; 550 tab_contents->view()->GetContainerBounds(&tab_bounds); 551 preview_contents_->view()->SizeContents(tab_bounds.size()); 552 553 preview_contents_->ShowContents(); 554 created_preview_contents = true; 555 } else { 556 created_preview_contents = false; 557 } 558 preview_tab_contents_delegate_->PrepareForNewLoad(); 559 560 if (template_url) { 561 DCHECK(template_url_id_ == template_url->id()); 562 if (!created_preview_contents) { 563 if (is_waiting_for_load()) { 564 // The page hasn't loaded yet. We'll send the script down when it does. 565 frame_load_observer_->set_text(user_text_); 566 return; 567 } 568 SendUserInputScript(preview_contents_.get(), user_text_); 569 if (complete_suggested_text_.size() > user_text_.size() && 570 !complete_suggested_text_.compare(0, user_text_.size(), user_text_)) { 571 *suggested_text = complete_suggested_text_.substr(user_text_.size()); 572 } 573 } else { 574 // Load the instant URL. We don't reflect the url we load in url() as 575 // callers expect that we're loading the URL they tell us to. 576 GURL instant_url( 577 template_url->instant_url()->ReplaceSearchTerms( 578 *template_url, UTF16ToWideHack(user_text), -1, std::wstring())); 579 initial_instant_url_ = url; 580 preview_contents_->controller().LoadURL( 581 instant_url, GURL(), transition_type); 582 frame_load_observer_.reset(new FrameLoadObserver(this, user_text_)); 583 } 584 } else { 585 DCHECK(template_url_id_ == 0); 586 frame_load_observer_.reset(NULL); 587 preview_contents_->controller().LoadURL(url_, GURL(), transition_type); 588 } 589} 590 591void InstantLoader::SetOmniboxBounds(const gfx::Rect& bounds) { 592 if (omnibox_bounds_ == bounds) 593 return; 594 595 omnibox_bounds_ = bounds; 596 if (preview_contents_.get() && is_showing_instant() && 597 !is_waiting_for_load()) { 598 // Updating the bounds is rather expensive, and because of the async nature 599 // of the omnibox the bounds can dance around a bit. Delay the update in 600 // hopes of things settling down. 601 if (update_bounds_timer_.IsRunning()) 602 update_bounds_timer_.Stop(); 603 update_bounds_timer_.Start( 604 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), 605 this, &InstantLoader::ProcessBoundsChange); 606 } 607} 608 609bool InstantLoader::IsMouseDownFromActivate() { 610 return preview_tab_contents_delegate_->is_mouse_down_from_activate(); 611} 612 613TabContents* InstantLoader::ReleasePreviewContents(InstantCommitType type) { 614 if (!preview_contents_.get()) 615 return NULL; 616 617 // FrameLoadObserver is only used for instant results, and instant results are 618 // only committed if active (when the FrameLoadObserver isn't installed). 619 DCHECK(type == INSTANT_COMMIT_DESTROY || !frame_load_observer_.get()); 620 621 if (type != INSTANT_COMMIT_DESTROY && is_showing_instant()) { 622 SendDoneScript(preview_contents_.get(), 623 user_text_, 624 type == INSTANT_COMMIT_PRESSED_ENTER); 625 } 626 omnibox_bounds_ = gfx::Rect(); 627 last_omnibox_bounds_ = gfx::Rect(); 628 url_ = GURL(); 629 user_text_.clear(); 630 complete_suggested_text_.clear(); 631 if (preview_contents_.get()) { 632 if (type != INSTANT_COMMIT_DESTROY) 633 preview_tab_contents_delegate_->CommitHistory(); 634 // Destroy the paint observer. 635 // RenderWidgetHostView may be null during shutdown. 636 if (preview_contents_->GetRenderWidgetHostView()) { 637 preview_contents_->GetRenderWidgetHostView()->GetRenderWidgetHost()-> 638 set_paint_observer(NULL); 639#if defined(OS_MACOSX) 640 preview_contents_->GetRenderWidgetHostView()-> 641 SetTakesFocusOnlyOnMouseDown(false); 642#endif 643 } 644 preview_contents_->set_delegate(NULL); 645 preview_tab_contents_delegate_->Reset(); 646 ready_ = false; 647 } 648 update_bounds_timer_.Stop(); 649 return preview_contents_.release(); 650} 651 652bool InstantLoader::ShouldCommitInstantOnMouseUp() { 653 return delegate_->ShouldCommitInstantOnMouseUp(); 654} 655 656void InstantLoader::CommitInstantLoader() { 657 delegate_->CommitInstantLoader(this); 658} 659 660void InstantLoader::ClearTemplateURLID() { 661 // This should only be invoked for sites we thought supported instant. 662 DCHECK(template_url_id_); 663 664 // The frame load observer should have completed. 665 DCHECK(!frame_load_observer_.get()); 666 667 // We shouldn't be ready. 668 DCHECK(!ready()); 669 670 template_url_id_ = 0; 671 ShowPreview(); 672} 673 674void InstantLoader::SetCompleteSuggestedText( 675 const string16& complete_suggested_text) { 676 ShowPreview(); 677 678 if (complete_suggested_text == complete_suggested_text_) 679 return; 680 681 if (user_text_.compare(0, user_text_.size(), complete_suggested_text, 682 0, user_text_.size())) { 683 // The user text no longer contains the suggested text, ignore it. 684 complete_suggested_text_.clear(); 685 delegate_->SetSuggestedTextFor(this, string16()); 686 return; 687 } 688 689 complete_suggested_text_ = complete_suggested_text; 690 delegate_->SetSuggestedTextFor( 691 this, 692 complete_suggested_text_.substr(user_text_.size())); 693} 694 695void InstantLoader::PreviewPainted() { 696 // If instant is supported then we wait for the first suggest result before 697 // showing the page. 698 if (!template_url_id_) 699 ShowPreview(); 700} 701 702void InstantLoader::ShowPreview() { 703 if (!ready_) { 704 ready_ = true; 705#if defined(OS_MACOSX) 706 if (preview_contents_->GetRenderWidgetHostView()) { 707 preview_contents_->GetRenderWidgetHostView()-> 708 SetTakesFocusOnlyOnMouseDown(true); 709 } 710#endif 711 delegate_->ShowInstantLoader(this); 712 } 713} 714 715void InstantLoader::PageFinishedLoading() { 716 frame_load_observer_.reset(); 717 // Wait for the user input before showing, this way the page should be up to 718 // date by the time we show it. 719} 720 721gfx::Rect InstantLoader::GetOmniboxBoundsInTermsOfPreview() { 722 if (omnibox_bounds_.IsEmpty()) 723 return omnibox_bounds_; 724 725 gfx::Rect preview_bounds(delegate_->GetInstantBounds()); 726 return gfx::Rect(omnibox_bounds_.x() - preview_bounds.x(), 727 omnibox_bounds_.y() - preview_bounds.y(), 728 omnibox_bounds_.width(), 729 omnibox_bounds_.height()); 730} 731 732void InstantLoader::PageDoesntSupportInstant(bool needs_reload) { 733 GURL url_to_load = url_; 734 735 // Because we didn't process any of the requests to load in Update we're 736 // actually at initial_instant_url_. We need to reset url_ so that callers see 737 // the correct state. 738 url_ = initial_instant_url_; 739 740 frame_load_observer_.reset(NULL); 741 742 delegate_->InstantLoaderDoesntSupportInstant(this, needs_reload, url_to_load); 743} 744 745void InstantLoader::ProcessBoundsChange() { 746 if (last_omnibox_bounds_ == omnibox_bounds_) 747 return; 748 749 last_omnibox_bounds_ = omnibox_bounds_; 750 if (preview_contents_.get() && is_showing_instant() && 751 !is_waiting_for_load()) { 752 SendOmniboxBoundsScript(preview_contents_.get(), 753 GetOmniboxBoundsInTermsOfPreview()); 754 } 755} 756