diff options
| author | gspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-28 17:26:49 +0000 | 
|---|---|---|
| committer | gspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-28 17:26:49 +0000 | 
| commit | d65adb173f09a837c96f96fc24a258f439e46e58 (patch) | |
| tree | 43605ab367e97389930c518c4e94bd3da5f20080 | |
| parent | 7cea56d943924d0c2196bdf4049f592b6182992c (diff) | |
| download | chromium_src-d65adb173f09a837c96f96fc24a258f439e46e58.zip chromium_src-d65adb173f09a837c96f96fc24a258f439e46e58.tar.gz chromium_src-d65adb173f09a837c96f96fc24a258f439e46e58.tar.bz2 | |
This adds in the ability for Chrome to generate windows with snapshots
of all currently open tabs in all browsers.
This is needed for overview mode on ChromeOS.
BUG=http://code.google.com/p/chromium-os/issues/detail?id=1170
TEST=Ran Chrome under ChromeOS with updated window manager.
Review URL: http://codereview.chromium.org/661237
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45824 0039d316-1c4b-4281-b951-d872f2087c98
28 files changed, 1297 insertions, 102 deletions
| diff --git a/app/surface/transport_dib.h b/app/surface/transport_dib.h index 7a60f08..44fb6a9 100644 --- a/app/surface/transport_dib.h +++ b/app/surface/transport_dib.h @@ -73,6 +73,13 @@ class TransportDIB {    // Returns a default, invalid handle, that is meant to indicate a missing    // Transport DIB.    static Handle DefaultHandleValue() { return NULL; } + +  // Returns a value that is ONLY USEFUL FOR TESTS WHERE IT WON'T BE +  // ACTUALLY USED AS A REAL HANDLE. +  static Handle GetFakeHandleForTest() { +    static int fake_handle = 10; +    return reinterpret_cast<Handle>(fake_handle++); +  }  #elif defined(OS_MACOSX)    typedef base::SharedMemoryHandle Handle;    // On Mac, the inode number of the backing file is used as an id. @@ -81,6 +88,13 @@ class TransportDIB {    // Returns a default, invalid handle, that is meant to indicate a missing    // Transport DIB.    static Handle DefaultHandleValue() { return Handle(); } + +  // Returns a value that is ONLY USEFUL FOR TESTS WHERE IT WON'T BE +  // ACTUALLY USED AS A REAL HANDLE. +  static Handle GetFakeHandleForTest() { +    static int fake_handle = 10; +    return Handle(fake_handle++, false); +  }  #elif defined(USE_X11)    typedef int Handle;  // These two ints are SysV IPC shared memory keys    typedef int Id; @@ -88,6 +102,13 @@ class TransportDIB {    // Returns a default, invalid handle, that is meant to indicate a missing    // Transport DIB.    static Handle DefaultHandleValue() { return -1; } + +  // Returns a value that is ONLY USEFUL FOR TESTS WHERE IT WON'T BE +  // ACTUALLY USED AS A REAL HANDLE. +  static Handle GetFakeHandleForTest() { +    static int fake_handle = 10; +    return fake_handle++; +  }  #endif    // Create a new TransportDIB, returning NULL on failure. diff --git a/app/x11_util.cc b/app/x11_util.cc index 38799ad..b6b63b3 100644 --- a/app/x11_util.cc +++ b/app/x11_util.cc @@ -229,6 +229,44 @@ bool GetIntProperty(XID window, const std::string& property_name, int* value) {    return true;  } +bool GetIntArrayProperty(XID window, +                         const std::string& property_name, +                         std::vector<int>* value) { +  Atom property_atom = gdk_x11_get_xatom_by_name_for_display( +      gdk_display_get_default(), property_name.c_str()); + +  Atom type = None; +  int format = 0;  // size in bits of each item in 'property' +  long unsigned int num_items = 0, remaining_bytes = 0; +  unsigned char* properties = NULL; + +  int result = XGetWindowProperty(GetXDisplay(), +                                  window, +                                  property_atom, +                                  0,      // offset into property data to read +                                  (~0L),  // max length to get (all of them) +                                  False,  // deleted +                                  AnyPropertyType, +                                  &type, +                                  &format, +                                  &num_items, +                                  &remaining_bytes, +                                  &properties); +  if (result != Success) +    return false; + +  if (format != 32) { +    XFree(properties); +    return false; +  } + +  int* int_properties = reinterpret_cast<int*>(properties); +  value->clear(); +  value->insert(value->begin(), int_properties, int_properties + num_items); +  XFree(properties); +  return true; +} +  bool GetStringProperty(      XID window, const std::string& property_name, std::string* value) {    Atom property_atom = gdk_x11_get_xatom_by_name_for_display( diff --git a/app/x11_util.h b/app/x11_util.h index 1c8685f1..5c202f3 100644 --- a/app/x11_util.h +++ b/app/x11_util.h @@ -78,9 +78,11 @@ int BitsPerPixelForPixmapDepth(Display* display, int depth);  bool IsWindowVisible(XID window);  // Returns the bounds of |window|.  bool GetWindowRect(XID window, gfx::Rect* rect); -// Get the value of an int or string property.  On success, true is returned and -// the value is stored in |value|. +// Get the value of an int, int array, or string property.  On +// success, true is returned and the value is stored in |value|.  bool GetIntProperty(XID window, const std::string& property_name, int* value); +bool GetIntArrayProperty(XID window, const std::string& property_name, +                         std::vector<int>* value);  bool GetStringProperty(      XID window, const std::string& property_name, std::string* value); diff --git a/base/file_descriptor_posix.h b/base/file_descriptor_posix.h index e9199eda..abc0789 100644 --- a/base/file_descriptor_posix.h +++ b/base/file_descriptor_posix.h @@ -24,6 +24,15 @@ struct FileDescriptor {        : fd(ifd),          auto_close(iauto_close) { } +  bool operator==(const FileDescriptor& other) const { +    return (fd == other.fd && auto_close == other.auto_close); +  } + +  // A comparison operator so that we can use these as keys in a std::map. +  bool operator<(const FileDescriptor& other) const { +    return other.fd < fd; +  } +    int fd;    // If true, this file descriptor should be closed after it has been used. For    // example an IPC system might interpret this flag as indicating that the diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc index a5e1706..8da58f6 100644 --- a/chrome/browser/browser_init.cc +++ b/chrome/browser/browser_init.cc @@ -21,6 +21,7 @@  #include "chrome/browser/extensions/extensions_service.h"  #include "chrome/browser/first_run.h"  #include "chrome/browser/net/dns_global.h" +#include "chrome/browser/net/url_fixer_upper.h"  #include "chrome/browser/notifications/desktop_notification_service.h"  #include "chrome/browser/pref_service.h"  #include "chrome/browser/profile.h" @@ -30,13 +31,12 @@  #include "chrome/browser/sessions/session_restore.h"  #include "chrome/browser/sessions/session_service.h"  #include "chrome/browser/shell_integration.h" -#include "chrome/browser/tabs/pinned_tab_codec.h" +#include "chrome/browser/status_icons/status_tray_manager.h"  #include "chrome/browser/tab_contents/infobar_delegate.h"  #include "chrome/browser/tab_contents/navigation_controller.h"  #include "chrome/browser/tab_contents/tab_contents.h"  #include "chrome/browser/tab_contents/tab_contents_view.h" -#include "chrome/browser/net/url_fixer_upper.h" -#include "chrome/browser/status_icons/status_tray_manager.h" +#include "chrome/browser/tabs/pinned_tab_codec.h"  #include "chrome/browser/user_data_manager.h"  #include "chrome/common/chrome_constants.h"  #include "chrome/common/chrome_paths.h" @@ -68,7 +68,6 @@  #if defined(OS_CHROMEOS)  #include "chrome/browser/chromeos/browser_notification_observers.h" -#include "chrome/browser/dom_ui/mediaplayer_ui.h"  #include "chrome/browser/chromeos/cros/cros_library.h"  #include "chrome/browser/chromeos/cros/mount_library.h"  #include "chrome/browser/chromeos/cros/power_library.h" @@ -76,6 +75,8 @@  #include "chrome/browser/chromeos/low_battery_observer.h"  #include "chrome/browser/chromeos/usb_mount_observer.h"  #include "chrome/browser/chromeos/wm_message_listener.h" +#include "chrome/browser/chromeos/wm_overview_controller.h" +#include "chrome/browser/dom_ui/mediaplayer_ui.h"  #endif  namespace { @@ -398,6 +399,9 @@ bool BrowserInit::LaunchBrowser(    // of what window has focus.    chromeos::WmMessageListener::instance(); +  // Create the WmOverviewController so it can register with the listener. +  chromeos::WmOverviewController::instance(); +    // Install the GView request interceptor that will redirect requests    // of compatible documents (PDF, etc) to the GView document viewer.    const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc index d6aff2f..b3925c1 100644 --- a/chrome/browser/chromeos/frame/browser_view.cc +++ b/chrome/browser/chromeos/frame/browser_view.cc @@ -6,6 +6,7 @@  #include <algorithm>  #include <string> +#include <vector>  #include "app/menus/simple_menu_model.h"  #include "app/theme_provider.h" @@ -392,16 +393,29 @@ void BrowserView::Init() {    otr_avatar_icon_->SetImage(GetOTRAvatarIcon());    otr_avatar_icon_->SetID(VIEW_ID_OTR_AVATAR);    AddChildView(otr_avatar_icon_); + +  // Make sure the window is set to the right type. +  std::vector<int> params; +  params.push_back(browser()->tab_count()); +  params.push_back(browser()->selected_index()); +  WmIpc::instance()->SetWindowType( +      GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()), +      WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL, +      ¶ms);  }  void BrowserView::Show() {    bool was_visible = frame()->GetWindow()->IsVisible();    ::BrowserView::Show();    if (!was_visible) { +    // Have to update the tab count and selected index to reflect reality. +    std::vector<int> params; +    params.push_back(browser()->tab_count()); +    params.push_back(browser()->selected_index());      WmIpc::instance()->SetWindowType(          GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()),          WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL, -        NULL); +        ¶ms);    }  } diff --git a/chrome/browser/chromeos/panels/panel_browsertest.cc b/chrome/browser/chromeos/panels/panel_browsertest.cc index 1ec09b2..4b01924 100644 --- a/chrome/browser/chromeos/panels/panel_browsertest.cc +++ b/chrome/browser/chromeos/panels/panel_browsertest.cc @@ -64,7 +64,7 @@ IN_PROC_BROWSER_TEST_F(PanelTest, PanelOpenSmall) {    EXPECT_EQ(        WmIpc::WINDOW_TYPE_CHROME_PANEL_CONTENT,        WmIpc::instance()->GetWindowType( -          GTK_WIDGET(new_browser->window()->GetNativeHandle()))); +          GTK_WIDGET(new_browser->window()->GetNativeHandle()), NULL));  }  // Large popups should open as new tab. diff --git a/chrome/browser/chromeos/wm_ipc.cc b/chrome/browser/chromeos/wm_ipc.cc index da1ed06..f3e298b 100644 --- a/chrome/browser/chromeos/wm_ipc.cc +++ b/chrome/browser/chromeos/wm_ipc.cc @@ -84,12 +84,18 @@ bool WmIpc::SetWindowType(GtkWidget* widget,                          type_to_atom_[ATOM_CHROME_WINDOW_TYPE], values);  } -WmIpc::WindowType WmIpc::GetWindowType(GtkWidget* widget) { -  int type; -  if (x11_util::GetIntProperty( +WmIpc::WindowType WmIpc::GetWindowType(GtkWidget* widget, +                                       std::vector<int>* params) { +  std::vector<int> properties; +  if (x11_util::GetIntArrayProperty(            x11_util::GetX11WindowFromGtkWidget(widget),            atom_to_string_[type_to_atom_[ATOM_CHROME_WINDOW_TYPE]], -          &type)) { +          &properties)) { +    int type = properties.front(); +    if (params) { +      params->clear(); +      params->insert(params->begin(), properties.begin() + 1, properties.end()); +    }      return static_cast<WindowType>(type);    } else {      return WINDOW_TYPE_UNKNOWN; diff --git a/chrome/browser/chromeos/wm_ipc.h b/chrome/browser/chromeos/wm_ipc.h index 2d35c0b..7df76a29 100644 --- a/chrome/browser/chromeos/wm_ipc.h +++ b/chrome/browser/chromeos/wm_ipc.h @@ -41,20 +41,14 @@ class WmIpc {      WINDOW_TYPE_UNKNOWN = 0,      // A top-level Chrome window. +    //   param[0]: The number of tabs currently in this Chrome window. +    //   param[1]: The index of the currently selected tab in this +    //             Chrome window.      WINDOW_TYPE_CHROME_TOPLEVEL, -    // A window showing scaled-down views of all of the tabs within a -    // Chrome window. -    WINDOW_TYPE_CHROME_TAB_SUMMARY, - -    // A tab that's been detached from a Chrome window and is currently -    // being dragged. -    //   param[0]: Cursor's initial X position at the start of the drag -    //   param[1]: Cursor's initial Y position -    //   param[2]: X component of cursor's offset from upper-left corner of -    //             tab at start of drag -    //   param[3]: Y component of cursor's offset -    WINDOW_TYPE_CHROME_FLOATING_TAB, +    // Vestiges of the old windows-across-the-bottom overview mode. +    DEPRECATED_WINDOW_TYPE_CHROME_TAB_SUMMARY, +    DEPRECATED_WINDOW_TYPE_CHROME_FLOATING_TAB,      // The contents of a popup window.      //   param[0]: X ID of associated titlebar, which must be mapped before @@ -66,8 +60,8 @@ class WmIpc {      // drawn above the panel when it's expanded.      WINDOW_TYPE_CHROME_PANEL_TITLEBAR, -    // A small window that when clicked creates a new browser window. -    WINDOW_TYPE_CREATE_BROWSER_WINDOW, +    // Vestiges of an earlier UI design. +    DEPRECATED_WINDOW_TYPE_CREATE_BROWSER_WINDOW,      // A Chrome info bubble (e.g. the bookmark bubble).  These are      // transient RGBA windows; we skip the usual transient behavior of @@ -76,8 +70,7 @@ class WmIpc {      // A window showing a view of a tab within a Chrome window.      //   param[0]: X ID of toplevel window that owns it. -    //   param[1]: index of this tab in the tab order (range is 0 to -    //             sum of all tabs in all browsers). +    //   param[1]: index of this tab in the toplevel window that owns it.      WINDOW_TYPE_CHROME_TAB_SNAPSHOT,      // The following types are used for the windows that represent a user that @@ -118,27 +111,10 @@ class WmIpc {      enum Type {        UNKNOWN = 0, -      // Notify Chrome when a floating tab has entered or left a tab -      // summary window.  Sent to the summary window. -      //   param[0]: X ID of the floating tab window -      //   param[1]: state (0 means left, 1 means entered or currently in) -      //   param[2]: X coordinate relative to summary window -      //   param[3]: Y coordinate -      CHROME_NOTIFY_FLOATING_TAB_OVER_TAB_SUMMARY, - -      // Notify Chrome when a floating tab has entered or left a top-level -      // window.  Sent to the window being entered/left. -      //   param[0]: X ID of the floating tab window -      //   param[1]: state (0 means left, 1 means entered) -      CHROME_NOTIFY_FLOATING_TAB_OVER_TOPLEVEL, - -      // Instruct a top-level Chrome window to change the visibility of its -      // tab summary window. -      //   param[0]: desired visibility (0 means hide, 1 means show) -      //   param[1]: X position (relative to the left edge of the root -      //             window) of the center of the top-level window.  Only -      //             relevant for "show" messages -      CHROME_SET_TAB_SUMMARY_VISIBILITY, +      // Vestiges of the old windows-across-the-bottom overview mode. +      DEPRECATED_CHROME_NOTIFY_FLOATING_TAB_OVER_TAB_SUMMARY, +      DEPRECATED_CHROME_NOTIFY_FLOATING_TAB_OVER_TOPLEVEL, +      DEPRECATED_CHROME_SET_TAB_SUMMARY_VISIBILITY,        // Tell the WM to collapse or expand a panel.        //   param[0]: X ID of the panel window @@ -148,16 +124,12 @@ class WmIpc {        // Notify Chrome that the panel state has changed.  Sent to the panel        // window.        //   param[0]: new state (0 means collapsed, 1 means expanded) +      // TODO: Deprecate this; Chrome can just watch for changes to the +      // _CHROME_STATE property to get the same information.        CHROME_NOTIFY_PANEL_STATE, -      // Instruct the WM to move a floating tab.  The passed-in position is -      // that of the cursor; the tab's composited window is displaced based -      // on the cursor's offset from the upper-left corner of the tab at -      // the start of the drag. -      //   param[0]: X ID of the floating tab window -      //   param[1]: X coordinate to which the tab should be moved -      //   param[2]: Y coordinate -      WM_MOVE_FLOATING_TAB, +      // From the old windows-across-the-bottom overview mode. +      DEPRECATED_WM_MOVE_FLOATING_TAB,        // Notify the WM that a panel has been dragged.        //   param[0]: X ID of the panel's content window @@ -180,18 +152,21 @@ class WmIpc {        //   param[0]: X ID of the panel's content window        WM_NOTIFY_PANEL_DRAG_COMPLETE, -      // Deprecated.  Send a _NET_ACTIVE_WINDOW client message to focus a window -      // instead (e.g. using gtk_window_present()). +      // Deprecated.  Send a _NET_ACTIVE_WINDOW client message to focus a +      // window instead (e.g. using gtk_window_present()).        DEPRECATED_WM_FOCUS_WINDOW,        // Notify Chrome that the layout mode (for example, overview or -      // focused) has changed. -      //   param[0]: new mode (0 means focused, 1 means overview) +      // active) has changed.  Since overview mode can be "cancelled" +      // (user hits escape to revert), we have an extra parameter to +      // indicate this. +      //   param[0]: new mode (0 means active mode, 1 means overview mode) +      //   param[1]: was mode cancelled? (0 = no, 1 = yes)        CHROME_NOTIFY_LAYOUT_MODE, -      // Instruct the WM to enter overview mode. +      // Deprecated. Instruct the WM to enter overview mode.        //   param[0]: X ID of the window to show the tab overview for. -      WM_SWITCH_TO_OVERVIEW_MODE, +      DEPRECATED_WM_SWITCH_TO_OVERVIEW_MODE,        // Let the WM know which version of this file Chrome is using.  It's        // difficult to make changes synchronously to Chrome and the WM (our @@ -218,11 +193,11 @@ class WmIpc {        //   param[0]: version of this protocol currently supported        WM_NOTIFY_IPC_VERSION, -      // Notify Chrome when a tab snapshot has been 'magnified' in the -      // overview.  Sent to the top level window. -      //   param[0]: X ID of the tab snapshot window -      //   param[1]: state (0 means end magnify, 1 means begin magnify) -      CHROME_NOTIFY_TAB_SNAPSHOT_MAGNIFY, +      // Notify Chrome when a tab has been selected in the overview. +      // Sent to the toplevel window associated with the magnified +      // tab. +      //   param[0]: tab index of newly selected tab. +      CHROME_NOTIFY_TAB_SELECT,        // Forces the window manager to hide the login windows.        WM_HIDE_LOGIN, @@ -295,9 +270,11 @@ class WmIpc {                       WindowType type,                       const std::vector<int>* params); -  // Gets the type of the window. The caller is responsible for trapping -  // errors from the X server. -  WmIpc::WindowType GetWindowType(GtkWidget* widget); +  // Gets the type of the window, and any associated parameters. The +  // caller is responsible for trapping errors from the X server.  If +  // the parameters are not interesting to the caller, NULL may be +  // passed for |params|. +  WmIpc::WindowType GetWindowType(GtkWidget* widget, std::vector<int>* params);    // Sends a message to the WM.    void SendMessage(const Message& msg); diff --git a/chrome/browser/chromeos/wm_overview_controller.cc b/chrome/browser/chromeos/wm_overview_controller.cc new file mode 100644 index 0000000..01a0888 --- /dev/null +++ b/chrome/browser/chromeos/wm_overview_controller.cc @@ -0,0 +1,517 @@ +// Copyright (c) 2010 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/chromeos/wm_overview_controller.h" + +#include <algorithm> +#include <vector> + +#include "base/linked_ptr.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/wm_ipc.h" +#include "chrome/browser/chromeos/wm_overview_snapshot.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/tab_contents/thumbnail_generator.h" +#include "chrome/browser/views/frame/browser_extender.h" +#include "chrome/browser/views/frame/browser_view.h" +#include "chrome/common/notification_service.h" +#include "views/widget/root_view.h" +#include "views/widget/widget_gtk.h" +#include "views/window/window.h" + +using std::vector; + +#if !defined(OS_CHROMEOS) +#error This file is only meant to be compiled for ChromeOS +#endif + +namespace chromeos { + +class BrowserListener : public TabStripModelObserver { + public: +  BrowserListener(Browser* browser, WmOverviewController* parent); +  ~BrowserListener(); + +  // Begin TabStripModelObserver methods +  virtual void TabInsertedAt(TabContents* contents, +                             int index, +                             bool foreground); +  virtual void TabClosingAt(TabContents* contents, int index) {} +  virtual void TabDetachedAt(TabContents* contents, int index); +  virtual void TabMoved(TabContents* contents, +                        int from_index, +                        int to_index); +  virtual void TabChangedAt(TabContents* contents, int index, +                            TabStripModelObserver::TabChangeType change_type); +  virtual void TabStripEmpty(); +  virtual void TabDeselectedAt(TabContents* contents, int index) {} +  virtual void TabSelectedAt(TabContents* old_contents, +                             TabContents* new_contents, +                             int index, +                             bool user_gesture); +  // End TabStripModelObserver methods + +  // Returns the number of tabs in this child. +  int count() const { return browser_->tabstrip_model()->count(); } + +  // Returns the browser that this child gets its data from. +  Browser* browser() const { return browser_; } + +  // Removes all the snapshots and re-populates them from the browser. +  void RecreateSnapshots(); + +  // Updates the selected index and tab count on the toplevel window. +  void UpdateSelectedIndex(int index); + +  // Finds the first cell with no snapshot and invokes ConfigureCell +  // for it.  Returns false if there are no more cells to configure on +  // this listener. +  bool ConfigureNextUnconfiguredSnapshot(); + +  // Saves the currently selected tab. +  void SaveCurrentTab() { original_selected_tab_ = browser_->selected_index(); } + +  // Reverts the selected browser tab to the tab that was selected +  // when This BrowserListener was created, or the last time +  // SaveCurrentTab was called. +  void RestoreOriginalSelectedTab(); + +  // Selects the tab at the given index. +  void SelectTab(int index); + +  // Shows any snapshots that are not visible, and updates their +  // bitmaps. +  void ShowSnapshots(); + + private: +  // Returns the tab contents from the tab model for this child at index. +  TabContents* GetTabContentsAt(int index) const  { +    return browser_->tabstrip_model()->GetTabContentsAt(index); +  } + +  // Configures a cell from the tab contents. +  void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents); + +  // Configures a cell from the model. +  void ConfigureCell(WmOverviewSnapshot* cell, int index) { +    ConfigureCell(cell, GetTabContentsAt(index)); +  } + +  // Inserts a new snapshot, initialized from the model, at the given +  // index, and renumbers any following snapshots. +  void InsertSnapshot(int index); + +  // Removes the snapshot at index. +  void ClearSnapshot(int index); + +  // Renumbers the index atom in the snapshots starting at the given +  // index. +  void RenumberSnapshots(int start_index); + +  Browser* browser_;  // Not owned +  WmOverviewController* controller_;  // Not owned + +  // Widgets containing snapshot images for this browser. +  typedef std::vector<WmOverviewSnapshot* > SnapshotVector; +  SnapshotVector snapshots_; + +  // True if the snapshots are showing. +  bool snapshots_showing_; + +  // The tab selected the last time SaveCurrentTab is called. +  int original_selected_tab_; + +  DISALLOW_COPY_AND_ASSIGN(BrowserListener); +}; + +BrowserListener::BrowserListener(Browser* browser, +                                 WmOverviewController* controller) +    : browser_(browser), +      controller_(controller), +      snapshots_showing_(false), +      original_selected_tab_(-1) { +  CHECK(browser_); +  CHECK(controller_); +  browser_->tabstrip_model()->AddObserver(this); +} + +BrowserListener::~BrowserListener() { +  browser_->tabstrip_model()->RemoveObserver(this); +} + +void BrowserListener::TabInsertedAt(TabContents* contents, +                                    int index, +                                    bool foreground) { +  InsertSnapshot(index); +  RenumberSnapshots(index); +  UpdateSelectedIndex(browser_->selected_index()); +} + +void BrowserListener::TabDetachedAt(TabContents* contents, int index) { +  ClearSnapshot(index); +  UpdateSelectedIndex(browser_->selected_index()); +  RenumberSnapshots(index); +} + +void BrowserListener::TabMoved(TabContents* contents, +                               int from_index, +                               int to_index) { +  // Need to reorder tab in the snapshots list, and reset the window +  // type atom on the affected snapshots (the one moved, and all the +  // ones after it), so that their indices are correct. +  WmOverviewSnapshot* snapshot = snapshots_[from_index]; +  snapshots_.erase(snapshots_.begin() + from_index); +  snapshots_.insert(snapshots_.begin() + to_index, snapshot); + +  RenumberSnapshots(std::min(to_index, from_index)); +} + +void BrowserListener::TabChangedAt( +    TabContents* contents, +    int index, +    TabStripModelObserver::TabChangeType change_type) { +  snapshots_[index]->reload_snapshot(); +  controller_->StartDelayTimer(); +} + +void BrowserListener::TabStripEmpty() { +  snapshots_.clear(); +} + +void BrowserListener::TabSelectedAt(TabContents* old_contents, +                                    TabContents* new_contents, +                                    int index, +                                    bool user_gesture) { +  UpdateSelectedIndex(index); +} + +void BrowserListener::RecreateSnapshots() { +  snapshots_.clear(); + +  for (int i = 0; i < count(); ++i) { +    InsertSnapshot(i); +  } + +  RenumberSnapshots(0); +} + +void BrowserListener::UpdateSelectedIndex(int index) { +  // Get the window params and check to make sure that they are +  // different from what we know before we set them, to avoid extra +  // notifications. +  std::vector<int> params; +  WmIpc::WindowType type = WmIpc::instance()->GetWindowType( +      GTK_WIDGET(browser_->window()->GetNativeHandle()), +      ¶ms); +  DCHECK(type == WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL); +  if (params.size() > 1) { +    if (params[0] == browser_->tab_count() && +        params[0] == index) +      return; +  } + +  params.clear(); +  params.push_back(browser_->tab_count()); +  params.push_back(index); +  WmIpc::instance()->SetWindowType( +      GTK_WIDGET(browser_->window()->GetNativeHandle()), +      WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL, +      ¶ms); +} + +bool BrowserListener::ConfigureNextUnconfiguredSnapshot() { +  for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) { +    WmOverviewSnapshot* cell = snapshots_[i]; +    if (!cell->configured_snapshot()) { +      ConfigureCell(cell, GetTabContentsAt(i)); +      return true; +    } +  } +  return false; +} + +void BrowserListener::RestoreOriginalSelectedTab() { +  if (original_selected_tab_ >= 0) { +    browser_->SelectTabContentsAt(original_selected_tab_, false); +  } +} + +void BrowserListener::ShowSnapshots() { +  for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) { +    WmOverviewSnapshot* snapshot = snapshots_[i]; +    snapshot->reload_snapshot(); +    if (!snapshot->IsVisible()) { +      snapshot->Show(); +    } +  } +} + +void BrowserListener::SelectTab(int index) { +  browser_->SelectTabContentsAt(index, true); +} + +void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell, +                                    TabContents* contents) { +  if (contents) { +    if (controller_->allow_show_snapshots()) { +      ThumbnailGenerator* generator = +          g_browser_process->GetThumbnailGenerator(); +      // TODO: Make sure that if the cell gets deleted before the +      // callback is called that it sticks around until it gets +      // called.  (some kind of "in flight" list that uses linked_ptr +      // to make sure they don't actually get deleted?)  Also, make +      // sure that any request for a thumbnail eventually returns +      // (even if it has bogus data), so we don't leak orphaned cells. +      ThumbnailGenerator::ThumbnailReadyCallback* callback = +          NewCallback(cell, &WmOverviewSnapshot::SetImage); +      generator->AskForThumbnail(contents->render_view_host(), +                                 callback, cell->size()); +    } +  } else { +    // This happens because the contents haven't been loaded yet. + +    // Make sure we set the snapshot image to something, otherwise +    // configured_snapshot remains false and ConfigureNextUnconfiguredSnapshot +    // would get stuck. +    if (controller_->allow_show_snapshots()) { +      cell->SetImage(SkBitmap()); +    } +  } +} + +void BrowserListener::InsertSnapshot(int index) { +  WmOverviewSnapshot* snapshot = new WmOverviewSnapshot; +  gfx::Rect bounds = +      static_cast<BrowserView*>(browser_->window())->GetClientAreaBounds(); +  gfx::Size size(bounds.width()/2, bounds.height()/2); +  snapshot->Init(size, browser_, index); +  snapshots_.insert(snapshots_.begin() + index, snapshot); +  snapshot->reload_snapshot(); +  controller_->StartDelayTimer(); +} + +// Removes the snapshot at index. +void BrowserListener::ClearSnapshot(int index) { +  snapshots_[index]->CloseNow(); +  snapshots_.erase(snapshots_.begin() + index); +} + +void BrowserListener::RenumberSnapshots(int start_index) { +  int changes = 0; +  for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) { +    if (snapshots_[i]->index() != static_cast<int>(i)) { +      snapshots_[i]->UpdateIndex(browser_, i); +      changes++; +    } +  } +} + +/////////////////////////////////// +// WmOverviewController methods + +// static +WmOverviewController* WmOverviewController::instance() { +  static WmOverviewController* instance = NULL; +  if (!instance) { +    instance = Singleton<WmOverviewController>::get(); +  } +  return instance; +} + +WmOverviewController::WmOverviewController() +    : allow_show_snapshots_(false) { +  AddAllBrowsers(); +  BrowserList::AddObserver(this); +  WmMessageListener::instance()->AddObserver(this); +} + +WmOverviewController::~WmOverviewController() { +  WmMessageListener::instance()->RemoveObserver(this); +  BrowserList::RemoveObserver(this); +  listeners_.clear(); +} + +void WmOverviewController::Observe(NotificationType type, +                                   const NotificationSource& source, +                                   const NotificationDetails& details) { +  // Now that the browser window is ready, we create the snapshots. +  if (type == NotificationType::BROWSER_WINDOW_READY) { +    const Browser* browser = Source<const Browser>(source).ptr(); +    BrowserListenerVector::value_type new_listener( +        new BrowserListener(const_cast<Browser*>(browser), this)); +    listeners_.push_back(new_listener); +    new_listener->RecreateSnapshots(); + +    // This makes sure that the new listener is in the right order (to +    // match the order in the browser list). +    AddAllBrowsers(); +  } +} + +// Called immediately before a browser is removed from the list. +void WmOverviewController::OnBrowserRemoving(const Browser* browser) { +  for (BrowserListenerVector::iterator i = listeners_.begin(); +       i != listeners_.end(); ++i) { +    if ((*i)->browser() == browser) { +      listeners_.erase(i); +      return; +    } +  } +} + +BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) { +  gpointer data = NULL; +  gdk_window_get_user_data(gdk_window, &data); +  GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); +  if (widget) { +    GtkWindow* gtk_window = GTK_WINDOW(widget); +    return BrowserView::GetBrowserViewForNativeWindow(gtk_window); +  } else { +    return NULL; +  } +} + +void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message, +                                            GdkWindow* window) { +  switch (message.type()) { +    case WmIpc::Message::CHROME_NOTIFY_LAYOUT_MODE: { +      if (message.param(0) == 0 || BrowserList::size() == 0) { +        Hide(message.param(1) != 0); +      } else { +        Show(); +      } +      break; +    } +    case WmIpc::Message::CHROME_NOTIFY_TAB_SELECT: { +      BrowserView* browser_window = GetBrowserViewForGdkWindow(window); +      // Find out which listener this goes to, and send it there. +      for (BrowserListenerVector::iterator i = listeners_.begin(); +           i != listeners_.end(); ++i) { +        if ((*i)->browser()->window() == browser_window) { +          (*i)->SelectTab(message.param(0)); +          break; +        } +      } +      break; +    } +    default: +      break; +  } +} + +void WmOverviewController::StartDelayTimer() { +  // We leave the delay timer running if it already is -- this means +  // we're rate limiting the number of times we can reconfigure the +  // snapshots (at most once every 350ms).  If we were to restart the +  // delay timer, it could result in a very long delay until they get +  // configured if tabs keep changing. +  if (!delay_timer_.IsRunning()) { +    configure_timer_.Stop(); +    // Note that this pause is kind of a hack: it's just long enough +    // that the overview-mode transitions will have finished happening +    // before we start refreshing snapshots, so we don't bog the CPU +    // while it's trying to animate the overview transitions. +    delay_timer_.Start( +        base::TimeDelta::FromMilliseconds(350), this, +        &WmOverviewController::StartConfiguring); +  } +} + +void WmOverviewController::RestoreTabSelections() { +  for (BrowserListenerVector::iterator i = listeners_.begin(); +       i != listeners_.end(); ++i) { +    (*i)->RestoreOriginalSelectedTab(); +  } +} + +void WmOverviewController::SaveTabSelections() { +  for (BrowserListenerVector::iterator i = listeners_.begin(); +       i != listeners_.end(); ++i) { +    (*i)->SaveCurrentTab(); +  } +} + +void WmOverviewController::Show() { +  SaveTabSelections(); +  for (BrowserListenerVector::iterator i = listeners_.begin(); +       i != listeners_.end(); ++i) { +    (*i)->ShowSnapshots(); +  } +  allow_show_snapshots_ = false; +  StartDelayTimer(); +} + +void WmOverviewController::Hide(bool cancelled) { +  configure_timer_.Stop(); +  delay_timer_.Stop(); +  if (cancelled) { +    RestoreTabSelections(); +  } +} + +void WmOverviewController::StartConfiguring() { +  allow_show_snapshots_ = true; +  configure_timer_.Stop(); +  configure_timer_.Start( +      base::TimeDelta::FromMilliseconds(10), this, +      &WmOverviewController::ConfigureNextUnconfiguredSnapshot); +} + +// Just configure one unconfigured cell.  If there aren't any left, +// then stop the timer. +void WmOverviewController::ConfigureNextUnconfiguredSnapshot() { +  for (BrowserListenerVector::size_type i = 0; i < listeners_.size(); ++i) { +    BrowserListener* listener = listeners_[i].get(); +    if (listener->ConfigureNextUnconfiguredSnapshot()) +      return; +  } +  configure_timer_.Stop(); +} + +void WmOverviewController::AddAllBrowsers() { +  // Make a copy so the old ones aren't deleted yet. +  BrowserListenerVector old_listeners; + +  listeners_.swap(old_listeners); + +  // Iterator through the browser list, adding all the browsers in the +  // new order.  If they were in the old list of listeners, just copy +  // that linked pointer, instead of making a new listener, so that we +  // can avoid lots of spurious destroy/create messages. +  BrowserList::const_iterator iterator = BrowserList::begin(); +  while (iterator != BrowserList::end()) { +    BrowserListenerVector::value_type item( +        BrowserListenerVector::value_type(NULL)); +    for (BrowserListenerVector::iterator old_iter = old_listeners.begin(); +         old_iter != old_listeners.end(); ++old_iter) { +      if ((*old_iter)->browser() == *iterator) { +        item = *old_iter; +        break; +      } +    } + +    // This browser isn't owned by any listener, so create it. +    if (item.get() == NULL) { +      item = BrowserListenerVector::value_type( +          new BrowserListener(*iterator, this)); + +      // This browser didn't already exist, and so we haven't been +      // watching it for tab insertions, so we need to create the +      // snapshots associated with it. +      item->RecreateSnapshots(); +    } +    listeners_.push_back(item); +    ++iterator; +  } + +  // Make sure we get notifications for when browser windows are ready. +  if (registrar_.IsEmpty()) +    registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY, +                   NotificationService::AllSources()); +} + +}  // namespace chromeos diff --git a/chrome/browser/chromeos/wm_overview_controller.h b/chrome/browser/chromeos/wm_overview_controller.h new file mode 100644 index 0000000..b37f965 --- /dev/null +++ b/chrome/browser/chromeos/wm_overview_controller.h @@ -0,0 +1,147 @@ +// Copyright (c) 2010 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_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_ +#define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_ + +#include <vector> + +#include "base/linked_ptr.h" +#include "base/singleton.h" +#include "base/timer.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/chromeos/wm_message_listener.h" +#include "chrome/common/notification_registrar.h" +#include "gfx/rect.h" + +namespace views { +class Widget; +} + +class Animation; +class Browser; + +namespace chromeos { + +class BrowserListener; +class WmOverviewSnapshot; + +// WmOverviewController is responsible for managing a list of objects +// that listen to the browsers (BrowserListeners, defined in the +// source file for this class) for changes, and keep a list of +// snapshot images in sync with the browser tab contents. +// +// As tabs are added/removed from the browsers, the number of snapshot +// windows changes to match. +// +// As obtaining and setting snapshots is expensive we delay setting +// the snapshot. The delay is controlled by delay_timer_. Once the +// timer fires another timer is started (configure_timer_). This timer +// invokes ConfigureNextUnconfiguredCell on the BrowserListener, which +// obtains and sets the snapshot of the next uncofigured +// cell. ConfigureNextUnconfiguredCell only configures one cell at a +// time until they are all configured. + +class WmOverviewController : public BrowserList::Observer, +                             public WmMessageListener::Observer, +                             public NotificationObserver { + public: +  // This class is a singleton. +  static WmOverviewController* instance(); + +  // BrowserList::Observer methods +  // This is called immediately after a browser is added to the list. +  void OnBrowserAdded(const Browser* browser) {} + +  // This is called immediately before a browser is removed from the list. +  void OnBrowserRemoving(const Browser* browser); +  // End BrowserList::Observer methods + +  // WmMessageListener::Observer methods +  // This is called immediately after a browser is added to the list. +  void ProcessWmMessage(const WmIpc::Message& message, +                        GdkWindow* window); +  // End WmMessageListener::Observer methods + +  // NotificationObserver methods +  void Observe(NotificationType type, +               const NotificationSource& source, +               const NotificationDetails& details); +  // End NotificationObserver methods + +  // Used by the BrowserListeners to configure their snapshots. +  const gfx::Rect& monitor_bounds() const { return monitor_bounds_; } + +  // Tells the listeners whether or not they're allowed to show +  // snapshots yet. +  bool allow_show_snapshots() const { return allow_show_snapshots_; } + +  // Starts the delay timer, and once the delay is over, configures +  // any unconfigured snapshots one at a time until none are left to +  // be configured. +  void StartDelayTimer(); + + private: +  friend struct DefaultSingletonTraits<WmOverviewController>; + +  // This class is a singleton. +  WmOverviewController(); +  ~WmOverviewController(); + +  // Restores tab selections on all browsers to what they were when +  // Show was last called.  Used when cancelling overview mode. +  void RestoreTabSelections(); + +  // Saves the currently selected tabs in the snapshots so that they +  // can be restored later with RestoreTabSelections. +  void SaveTabSelections(); + +  // Show the snapshot windows, saving current tab selections. +  void Show(); + +  // Hide the snapshot windows.  When |cancelled| is true, then the +  // tab selections that were saved when the snapshot windows were +  // shown are restored. +  void Hide(bool cancelled); + +  // Invoked by delay_timer_. Sets allow_show_snapshots_ to true and starts +  // configure_timer_. +  void StartConfiguring(); + +  // Configure the next unconfigured snapshot window owned by any of +  // the listeners. +  void ConfigureNextUnconfiguredSnapshot(); + +  // Add browser listeners for all existing browsers, reusing any that +  // were already there. +  void AddAllBrowsers(); + +  // This is so we can register for notifications. +  NotificationRegistrar registrar_; + +  // This is a vector of listeners that listen to all the browsers. +  typedef std::vector<linked_ptr<BrowserListener> > BrowserListenerVector; +  BrowserListenerVector listeners_; + +  // This is the bounds of the monitor we're being displayed on. This +  // is used to adjust the size of snapshots so they'll fit. +  gfx::Rect monitor_bounds_; + +  // This indicates whether we should actually set the snapshots so we +  // don't do it when we don't need to. It is initially false, then +  // set to true by StartConfiguring. +  bool allow_show_snapshots_; + +  // See description above class for details. +  base::OneShotTimer<WmOverviewController> delay_timer_; + +  // See description above class for details. +  base::RepeatingTimer<WmOverviewController> configure_timer_; + +  DISALLOW_COPY_AND_ASSIGN(WmOverviewController); +}; + +}  // namespace chromeos + +#endif  // CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_CONTROLLER_H_ diff --git a/chrome/browser/chromeos/wm_overview_snapshot.cc b/chrome/browser/chromeos/wm_overview_snapshot.cc new file mode 100644 index 0000000..97c2a8e --- /dev/null +++ b/chrome/browser/chromeos/wm_overview_snapshot.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2010 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/chromeos/wm_overview_snapshot.h" + +#include <vector> + +#include "app/x11_util.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/chromeos/wm_ipc.h" +#include "views/border.h" +#include "views/controls/image_view.h" +#include "views/controls/label.h" +#include "views/grid_layout.h" + +using views::ColumnSet; +using views::GridLayout; +using std::vector; + +#if !defined(OS_CHROMEOS) +#error This file is only meant to be compiled for ChromeOS +#endif + +namespace chromeos { + +WmOverviewSnapshot::WmOverviewSnapshot() +  : WidgetGtk(TYPE_WINDOW), +    snapshot_view_(NULL), +    index_(-1), +    configured_snapshot_(false) { +} + +void WmOverviewSnapshot::Init(const gfx::Size& size, +                              Browser* browser, +                              int index) { +  snapshot_view_ = new views::ImageView(); +  MakeTransparent(); + +  snapshot_view_->set_background( +      views::Background::CreateSolidBackground(SK_ColorWHITE)); +  snapshot_view_->set_border( +      views::Border::CreateSolidBorder(1, SkColorSetRGB(176, 176, 176))); + +  WidgetGtk::Init(NULL, gfx::Rect(gfx::Point(0,0), size)); + +  SetContentsView(snapshot_view_); + +  UpdateIndex(browser, index); +} + + +void WmOverviewSnapshot::UpdateIndex(Browser* browser, int index) { +  vector<int> params; +  params.push_back(x11_util::GetX11WindowFromGtkWidget( +      GTK_WIDGET(browser->window()->GetNativeHandle()))); +  params.push_back(index); +  WmIpc::instance()->SetWindowType( +      GetNativeView(), +      WmIpc::WINDOW_TYPE_CHROME_TAB_SNAPSHOT, +      ¶ms); +  index_ = index; +} + +void WmOverviewSnapshot::SetImage(const SkBitmap& image) { +  CHECK(snapshot_view_) << "Init not called before setting image."; +  snapshot_view_->SetImage(image); + +  // Reset the bounds to the size of the image. +  gfx::Rect bounds; +  GetBounds(&bounds, false); +  bounds.set_width(image.width()); +  bounds.set_height(image.height()); +  SetBounds(bounds); + +  configured_snapshot_ = true; +} + +}  // namespace chromeos diff --git a/chrome/browser/chromeos/wm_overview_snapshot.h b/chrome/browser/chromeos/wm_overview_snapshot.h new file mode 100644 index 0000000..7bc3699 --- /dev/null +++ b/chrome/browser/chromeos/wm_overview_snapshot.h @@ -0,0 +1,59 @@ +// Copyright (c) 2010 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_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_ +#define CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_ + +#include "third_party/skia/include/core/SkBitmap.h" +#include "views/view.h" +#include "views/widget/widget_gtk.h" +#include "views/controls/image_view.h" + +class Browser; + +namespace chromeos { + +// A single snapshot displayed by WmOverviewController. +// WmOverviewSnapshot contains a label, favicon and snapshot. +class WmOverviewSnapshot : public views::WidgetGtk { + public: +  WmOverviewSnapshot(); +  void Init(const gfx::Size& size, Browser* browser, int index); + +  void SetImage(const SkBitmap& image); + +  void UpdateIndex(Browser* browser, int index); +  int index() const { return index_; } + +  // Returns the size of the snapshot widget. +  gfx::Size size() const { +    gfx::Rect rect; +    GetBounds(&rect, false); +    return rect.size(); +  } + +  // Has the snapshot been configured? This is true after SetSnapshot +  // is invoked. +  bool configured_snapshot() const { return configured_snapshot_; } + +  // This resets the configured_snapshot flag for this snapshot so it will +  // get reloaded the next time we check. +  void reload_snapshot() { configured_snapshot_ = false; } + + private: +  // This control is the contents view for this widget. +  views::ImageView* snapshot_view_; + +  // This is the tab index of the snapshot in the associated browser. +  int index_; + +  // This indicates whether or not the snapshot has been configured. +  bool configured_snapshot_; + +  DISALLOW_COPY_AND_ASSIGN(WmOverviewSnapshot); +}; + +}  // namespace chromeos + +#endif  // CHROME_BROWSER_CHROMEOS_WM_OVERVIEW_SNAPSHOT_H_ diff --git a/chrome/browser/renderer_host/backing_store_x.cc b/chrome/browser/renderer_host/backing_store_x.cc index d88dca7..f10e589 100644 --- a/chrome/browser/renderer_host/backing_store_x.cc +++ b/chrome/browser/renderer_host/backing_store_x.cc @@ -326,6 +326,7 @@ bool BackingStoreX::CopyFromBackingStore(const gfx::Rect& rect,    // TODO(jhawkins): Need to convert the image data if the image bits per pixel    // is not 32. +  // Note that this also initializes the output bitmap as opaque.    if (!output->initialize(width, height, true) ||        image->bits_per_pixel != 32) {      if (shared_memory_support_ != x11_util::SHARED_MEMORY_NONE) @@ -335,15 +336,19 @@ bool BackingStoreX::CopyFromBackingStore(const gfx::Rect& rect,      return false;    } -  // The X image might have a different row stride, so iterate through it and -  // copy each row out, only up to the pixels we're actually using. -  // This code assumes a visual mode where a pixel is represented using -  // a byte for each component. +  // The X image might have a different row stride, so iterate through +  // it and copy each row out, only up to the pixels we're actually +  // using.  This code assumes a visual mode where a pixel is +  // represented using a 32-bit unsigned int, with a byte per component.    SkBitmap bitmap = output->getTopPlatformDevice().accessBitmap(true);    for (int y = 0; y < height; y++) { +    const uint32* src_row = reinterpret_cast<uint32*>( +        &image->data[image->bytes_per_line * y]);      uint32* dest_row = bitmap.getAddr32(0, y); -    const char* src_row = &image->data[image->bytes_per_line * y]; -    memcpy(dest_row, src_row, width * 4); +    for (int x = 0; x < width; ++x, ++dest_row) { +      // Force alpha to be 0xff, because otherwise it causes rendering problems. +      *dest_row = src_row[x] | 0xff000000; +    }    }    if (shared_memory_support_ != x11_util::SHARED_MEMORY_NONE) diff --git a/chrome/browser/renderer_host/render_widget_helper.h b/chrome/browser/renderer_host/render_widget_helper.h index 53b336c..33ca744 100644 --- a/chrome/browser/renderer_host/render_widget_helper.h +++ b/chrome/browser/renderer_host/render_widget_helper.h @@ -188,7 +188,7 @@ class RenderWidgetHelper  #endif    // A map of live paint messages.  Must hold pending_paints_lock_ to access. -  // The PaintMsgProxy objects are not owned by this map.  (See PaintMsgProxy +  // The UpdateMsgProxy objects are not owned by this map.  (See UpdateMsgProxy    // for details about how the lifetime of instances are managed.)    UpdateMsgProxyMap pending_paints_;    Lock pending_paints_lock_; diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc index 6b20935..59b2231 100644 --- a/chrome/browser/renderer_host/render_widget_host.cc +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -127,6 +127,7 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {      IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewGone, OnMsgRenderViewGone)      IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose)      IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove) +    IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnMsgPaintAtSizeAck)      IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnMsgUpdateRect)      IPC_MESSAGE_HANDLER(ViewHostMsg_CreateVideo, OnMsgCreateVideo)      IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateVideo, OnMsgUpdateVideo) @@ -280,6 +281,14 @@ void RenderWidgetHost::SetIsLoading(bool is_loading) {    view_->SetIsLoading(is_loading);  } +void RenderWidgetHost::PaintAtSize(TransportDIB::Handle dib_handle, +                                   const gfx::Size& size) { +  // Ask the renderer to create a bitmap regardless of whether it's +  // hidden, being resized, redrawn, etc., and to scale it by the +  // scale factor given. +  Send(new ViewMsg_PaintAtSize(routing_id_, dib_handle, size)); +} +  BackingStore* RenderWidgetHost::GetBackingStore(bool force_create) {    // We should not be asked to paint while we are hidden.  If we are hidden,    // then it means that our consumer failed to call WasRestored. If we're not @@ -676,6 +685,13 @@ void RenderWidgetHost::OnMsgRequestMove(const gfx::Rect& pos) {    }  } +void RenderWidgetHost::OnMsgPaintAtSizeAck( +    const TransportDIB::Handle& dib_handle, const gfx::Size& size) { +  if (painting_observer_) { +    painting_observer_->WidgetDidReceivePaintAtSizeAck(this, dib_handle, size); +  } +} +  void RenderWidgetHost::OnMsgUpdateRect(      const ViewHostMsg_UpdateRect_Params& params) {    TimeTicks paint_start = TimeTicks::Now(); diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h index d57d8bc..7b87d13 100644 --- a/chrome/browser/renderer_host/render_widget_host.h +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -205,6 +205,16 @@ class RenderWidgetHost : public IPC::Channel::Listener,    // Indicates if the page has finished loading.    void SetIsLoading(bool is_loading); +  // This tells the renderer to paint into a bitmap and return it, +  // regardless of whether the tab is hidden or not.  It returns the +  // bitmap scaled so it matches the requested size, so that the +  // scaling happens on the rendering thread.  When the bitmap is +  // ready, the renderer sends a PaintAtSizeACK to this host, and the +  // painting observer is notified.  Note that this bypasses most of +  // the update logic that is normally invoked, and doesn't put the +  // results into the backing store. +  void PaintAtSize(TransportDIB::Handle dib_handle, const gfx::Size& size); +    // Get access to the widget's backing store.  If a resize is in progress,    // then the current size of the backing store may be less than the size of    // the widget's view.  If you pass |force_create| as true, then the backing @@ -413,6 +423,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,    FRIEND_TEST(RenderWidgetHostTest, Resize);    FRIEND_TEST(RenderWidgetHostTest, ResizeThenCrash);    FRIEND_TEST(RenderWidgetHostTest, HiddenPaint); +  FRIEND_TEST(RenderWidgetHostTest, PaintAtSize);    // Tell this object to destroy itself.    void Destroy(); @@ -421,7 +432,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,    // if it is.    void CheckRendererIsUnresponsive(); -  // Called if we know the renderer is responsive. When we currently thing the +  // Called if we know the renderer is responsive. When we currently think the    // renderer is unresponsive, this will clear that state and call    // NotifyRendererResponsive.    void RendererIsResponsive(); @@ -431,6 +442,8 @@ class RenderWidgetHost : public IPC::Channel::Listener,    void OnMsgRenderViewGone();    void OnMsgClose();    void OnMsgRequestMove(const gfx::Rect& pos); +  void OnMsgPaintAtSizeAck(const TransportDIB::Handle& dib_handle, +                           const gfx::Size& size);    void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params);    void OnMsgCreateVideo(const gfx::Size& size);    void OnMsgUpdateVideo(TransportDIB::Id bitmap, const gfx::Rect& bitmap_rect); diff --git a/chrome/browser/renderer_host/render_widget_host_painting_observer.h b/chrome/browser/renderer_host/render_widget_host_painting_observer.h index d24b61a..332e446 100644 --- a/chrome/browser/renderer_host/render_widget_host_painting_observer.h +++ b/chrome/browser/renderer_host/render_widget_host_painting_observer.h @@ -5,8 +5,15 @@  #ifndef CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_PAINTING_OBSERVER_H_  #define CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_PAINTING_OBSERVER_H_ +#include "app/surface/transport_dib.h" +  class BackingStore;  class RenderWidgetHost; +class SkBitmap; + +namespace gfx { +class Size; +}  // This class can be used to observe painting events for a RenderWidgetHost.  // Its primary goal in Chrome is to allow thumbnails to be generated. @@ -19,6 +26,13 @@ class RenderWidgetHostPaintingObserver {    // Indicates that the RenderWidgetHost just updated the backing store.    virtual void WidgetDidUpdateBackingStore(RenderWidgetHost* widget) = 0; + +  // This notifies the painting observer that a PaintAtSizeACK was +  // received. +  virtual void WidgetDidReceivePaintAtSizeAck( +      RenderWidgetHost* widget, +      const TransportDIB::Handle& dib_handle, +      const gfx::Size& size) = 0;  };  #endif  // CHROME_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_PAINTING_OBSERVER_H_ diff --git a/chrome/browser/renderer_host/render_widget_host_unittest.cc b/chrome/browser/renderer_host/render_widget_host_unittest.cc index e0ffe1a..28be917 100644 --- a/chrome/browser/renderer_host/render_widget_host_unittest.cc +++ b/chrome/browser/renderer_host/render_widget_host_unittest.cc @@ -9,6 +9,7 @@  #include "base/timer.h"  #include "build/build_config.h"  #include "chrome/browser/renderer_host/backing_store.h" +#include "chrome/browser/renderer_host/render_widget_host_painting_observer.h"  #include "chrome/browser/renderer_host/test/test_render_view_host.h"  #include "chrome/common/render_messages.h"  #include "gfx/canvas.h" @@ -19,6 +20,10 @@ using base::TimeDelta;  using WebKit::WebInputEvent;  using WebKit::WebMouseWheelEvent; +namespace gfx { +class Size; +} +  // RenderWidgetHostProcess -----------------------------------------------------  class RenderWidgetHostProcess : public MockRenderProcessHost { @@ -191,6 +196,32 @@ class MockRenderWidgetHost : public RenderWidgetHost {    bool unresponsive_timer_fired_;  }; +// MockPaintingObserver -------------------------------------------------------- + +class MockPaintingObserver : public RenderWidgetHostPaintingObserver { + public: +  void WidgetWillDestroyBackingStore(RenderWidgetHost* widget, +                                     BackingStore* backing_store) {} +  void WidgetDidUpdateBackingStore(RenderWidgetHost* widget) {} +  void WidgetDidReceivePaintAtSizeAck(RenderWidgetHost* host, +                                      const TransportDIB::Handle& dib_handle, +                                      const gfx::Size& size) { +    host_ = reinterpret_cast<MockRenderWidgetHost*>(host); +    dib_handle_ = dib_handle; +    size_ = size; +  } + +  MockRenderWidgetHost* host() const { return host_; } +  TransportDIB::Handle dib_handle() const { return dib_handle_; } +  gfx::Size size() const { return size_; } + + private: +  MockRenderWidgetHost* host_; +  TransportDIB::Handle dib_handle_; +  gfx::Size size_; +}; + +  // RenderWidgetHostTest --------------------------------------------------------  class RenderWidgetHostTest : public testing::Test { @@ -504,6 +535,24 @@ TEST_F(RenderWidgetHostTest, HiddenPaint) {    EXPECT_TRUE(needs_repaint.a);  } +TEST_F(RenderWidgetHostTest, PaintAtSize) { +  host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), gfx::Size(20, 30)); +  EXPECT_TRUE( +      process_->sink().GetUniqueMessageMatching(ViewMsg_PaintAtSize::ID)); + +  MockPaintingObserver observer; +  host_->set_painting_observer(&observer); + +  // Need to generate a fake handle value on all platforms. +  TransportDIB::Handle handle = TransportDIB::GetFakeHandleForTest(); +  host_->OnMsgPaintAtSizeAck(handle, gfx::Size(20, 30)); +  EXPECT_TRUE(host_ == observer.host()); +  EXPECT_TRUE(handle == observer.dib_handle()); +  EXPECT_EQ(20, observer.size().width()); +  EXPECT_EQ(30, observer.size().height()); +  host_->set_painting_observer(NULL); +} +  TEST_F(RenderWidgetHostTest, HandleKeyEventsWeSent) {    // Simulate a keyboard event.    SimulateKeyboardEvent(WebInputEvent::RawKeyDown); @@ -649,4 +698,3 @@ TEST_F(RenderWidgetHostTest, StopAndStartHangMonitorTimeout) {    MessageLoop::current()->Run();    EXPECT_TRUE(host_->unresponsive_timer_fired());  } - diff --git a/chrome/browser/tab_contents/thumbnail_generator.cc b/chrome/browser/tab_contents/thumbnail_generator.cc index 212045e..a3c476c 100644 --- a/chrome/browser/tab_contents/thumbnail_generator.cc +++ b/chrome/browser/tab_contents/thumbnail_generator.cc @@ -5,12 +5,14 @@  #include "chrome/browser/tab_contents/thumbnail_generator.h"  #include <algorithm> +#include <map>  #include "base/histogram.h"  #include "base/time.h"  #include "build/build_config.h"  #include "chrome/browser/renderer_host/backing_store.h"  #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/tab_contents.h"  #include "chrome/common/notification_service.h"  #include "chrome/common/property_bag.h"  #include "gfx/rect.h" @@ -105,17 +107,26 @@ SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {                                             &temp_canvas))      return result;    const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false); -  result = SkBitmapOperations::DownsampleByTwoUntilSize(bmp, kThumbnailWidth, + +#if defined(OS_CHROMEOS) +  // On ChromeOS, the thumbnail is always half the dimensions of the +  // original. +  result = SkBitmapOperations::DownsampleByTwo(bmp); +#else +  result = SkBitmapOperations::DownsampleByTwoUntilSize(bmp, +                                                        kThumbnailWidth,                                                          kThumbnailHeight); -  // This is a bit subtle. SkBitmaps are refcounted, but the magic ones in -  // PlatformCanvas can't be ssigned to SkBitmap with proper -  // refcounting.  If the bitmap doesn't change, then the downsampler will -  // return the input bitmap, which will be the reference to the weird -  // PlatformCanvas one insetad of a regular one. To get a regular refcounted -  // bitmap, we need to copy it. -  if (bmp.width() == result.width() && bmp.height() == result.height()) +  // This is a bit subtle. SkBitmaps are refcounted, but the magic +  // ones in PlatformCanvas can't be assigned to SkBitmap with proper +  // refcounting.  If the bitmap doesn't change, then the downsampler +  // will return the input bitmap, which will be the reference to the +  // weird PlatformCanvas one insetad of a regular one. To get a +  // regular refcounted bitmap, we need to copy it. +  if (bmp.width() == result.width() && +      bmp.height() == result.height())      bmp.copyTo(&result, SkBitmap::kARGB_8888_Config); +#endif    HISTOGRAM_TIMES(kThumbnailHistogramName,                    base::TimeTicks::Now() - begin_compute_thumbnail); @@ -124,6 +135,12 @@ SkBitmap GetThumbnailForBackingStore(BackingStore* backing_store) {  }  // namespace +struct ThumbnailGenerator::AsyncRequestInfo { +  scoped_ptr<ThumbnailReadyCallback> callback; +  scoped_ptr<TransportDIB> thumbnail_dib; +  RenderWidgetHost* renderer;  // Not owned. +}; +  ThumbnailGenerator::ThumbnailGenerator()      : no_timeout_(false) {    // The BrowserProcessImpl creates this non-lazily. If you add nontrivial @@ -144,14 +161,48 @@ void ThumbnailGenerator::StartThumbnailing() {      // aren't views like select popups.      registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,                     NotificationService::AllSources()); -      registrar_.Add(this, NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED,                     NotificationService::AllSources());      registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DESTROYED,                     NotificationService::AllSources()); +    registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED, +                   NotificationService::AllSources());    }  } +void ThumbnailGenerator::AskForThumbnail(RenderWidgetHost* renderer, +                                         ThumbnailReadyCallback* callback, +                                         gfx::Size size) { +  SkBitmap first_try = GetThumbnailForRenderer(renderer); +  if (!first_try.isNull()) { +    // We were able to find a non-null thumbnail for this renderer, so +    // we'll go with it. +    callback->Run(first_try); +    delete callback; +    return; +  } + +  // We are going to render the thumbnail asynchronously now, so keep +  // this callback for later lookup when the rendering is done. +  static int sequence_num = 0; +  TransportDIB* thumbnail_dib = TransportDIB::Create( +      size.width() * size.height() * 4, sequence_num++); +  linked_ptr<AsyncRequestInfo> request_info(new AsyncRequestInfo); +  request_info->callback.reset(callback); +  request_info->thumbnail_dib.reset(thumbnail_dib); +  request_info->renderer = renderer; +  ThumbnailCallbackMap::value_type new_value(thumbnail_dib->handle(), +                                             request_info); +  std::pair<ThumbnailCallbackMap::iterator, bool> result = +      callback_map_.insert(new_value); +  if (!result.second) { +    NOTREACHED() << "Callback already registered?"; +    return; +  } + +  renderer->PaintAtSize(thumbnail_dib->handle(), size); +} +  SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(      RenderWidgetHost* renderer) const {    WidgetThumbnail* wt = GetDataForHost(renderer); @@ -221,6 +272,42 @@ void ThumbnailGenerator::WidgetDidUpdateBackingStore(    wt->thumbnail = SkBitmap();  } +void ThumbnailGenerator::WidgetDidReceivePaintAtSizeAck( +    RenderWidgetHost* widget, +    const TransportDIB::Handle& dib_handle, +    const gfx::Size& size) { +  // Lookup the callback, run it, and erase it. +  ThumbnailCallbackMap::iterator item = callback_map_.find(dib_handle); +  if (item != callback_map_.end()) { +    TransportDIB* dib = item->second->thumbnail_dib.get(); +    DCHECK(dib); +    if (!dib) { +      return; +    } + +    // Create an SkBitmap from the DIB. +    SkBitmap non_owned_bitmap; +    SkBitmap result; + +    // Fill out the non_owned_bitmap with the right config.  Note that +    // this code assumes that the transport dib is a 32-bit ARGB +    // image. +    non_owned_bitmap.setConfig(SkBitmap::kARGB_8888_Config, +                               size.width(), size.height()); +    non_owned_bitmap.setPixels(dib->memory()); + +    // Now alloc/copy the memory so we own it and can pass it around, +    // and the memory won't go away when the DIB goes away. +    // TODO: Figure out a way to avoid this copy? +    non_owned_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config); + +    item->second->callback->Run(result); + +    // We're done with the callback, and with the DIB, so delete both. +    callback_map_.erase(item); +  } +} +  void ThumbnailGenerator::Observe(NotificationType type,                                   const NotificationSource& source,                                   const NotificationDetails& details) { @@ -243,6 +330,10 @@ void ThumbnailGenerator::Observe(NotificationType type,        WidgetDestroyed(Source<RenderWidgetHost>(source).ptr());        break; +    case NotificationType::TAB_CONTENTS_DISCONNECTED: +      TabContentsDisconnected(Source<TabContents>(source).ptr()); +      break; +      default:        NOTREACHED();    } @@ -294,6 +385,20 @@ void ThumbnailGenerator::WidgetDestroyed(RenderWidgetHost* widget) {    EraseHostFromShownList(widget);  } +void ThumbnailGenerator::TabContentsDisconnected(TabContents* contents) { +  // Go through the existing callbacks, and find any that have the +  // same renderer as this TabContents and remove them so they don't +  // hang around. +  ThumbnailCallbackMap::iterator iterator = callback_map_.begin(); +  RenderWidgetHost* renderer = contents->render_view_host(); +  while (iterator != callback_map_.end()) { +    if (iterator->second->renderer == renderer) { +      callback_map_.erase(iterator); +    } +    ++iterator; +  } +} +  void ThumbnailGenerator::ShownDelayHandler() {    base::TimeTicks threshold = base::TimeTicks::Now() -        base::TimeDelta::FromMilliseconds(kVisibilitySlopMS); diff --git a/chrome/browser/tab_contents/thumbnail_generator.h b/chrome/browser/tab_contents/thumbnail_generator.h index 499c2d6..6398e17 100644 --- a/chrome/browser/tab_contents/thumbnail_generator.h +++ b/chrome/browser/tab_contents/thumbnail_generator.h @@ -5,22 +5,30 @@  #ifndef CHROME_BROWSER_TAB_CONTENTS_THUMBNAIL_GENERATOR_H_  #define CHROME_BROWSER_TAB_CONTENTS_THUMBNAIL_GENERATOR_H_ +#include <map> +#include <utility>  #include <vector>  #include "base/basictypes.h" +#include "base/callback.h" +#include "base/linked_ptr.h" +#include "base/lock.h"  #include "base/timer.h"  #include "chrome/browser/renderer_host/render_widget_host_painting_observer.h"  #include "chrome/common/notification_observer.h"  #include "chrome/common/notification_registrar.h" +class RenderViewHost;  class RenderWidgetHost;  class SkBitmap; +class TabContents;  // This class MUST be destroyed after the RenderWidgetHosts, since it installs  // a painting observer that is not removed.  class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,                             public NotificationObserver {   public: +  typedef Callback1<const SkBitmap&>::Type ThumbnailReadyCallback;    // This class will do nothing until you call StartThumbnailing.    ThumbnailGenerator();    ~ThumbnailGenerator(); @@ -29,6 +37,17 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,    // be called repeatedly and with wild abandon to no ill effect.    void StartThumbnailing(); +  // This registers a callback that can receive the resulting SkBitmap +  // from the renderer when it is done rendering it.  This differs +  // from GetThumbnailForRenderer in that it is asynchronous, and +  // because it will also fetch the bitmap even if the tab is hidden. +  // In addition, if the renderer has to be invoked, the scaling of +  // the thumbnail happens on the rendering thread.  Takes ownership +  // of the callback object. +  void AskForThumbnail(RenderWidgetHost* renderer, +                       ThumbnailReadyCallback* callback, +                       gfx::Size size); +    SkBitmap GetThumbnailForRenderer(RenderWidgetHost* renderer) const;  #ifdef UNIT_TEST @@ -45,6 +64,11 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,                                               BackingStore* backing_store);    virtual void WidgetDidUpdateBackingStore(RenderWidgetHost* widget); +  virtual void WidgetDidReceivePaintAtSizeAck( +      RenderWidgetHost* widget, +      const TransportDIB::Handle& dib_handle, +      const gfx::Size& size); +    // NotificationObserver interface.    virtual void Observe(NotificationType type,                         const NotificationSource& source, @@ -57,6 +81,10 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,    // Called when the given widget is destroyed.    void WidgetDestroyed(RenderWidgetHost* widget); +  // Called when the given tab contents are disconnected (either +  // through being closed, or because the renderer is no longer there). +  void TabContentsDisconnected(TabContents* contents); +    // Timer function called on a delay after a tab has been shown. It will    // invalidate the thumbnail for hosts with expired thumbnails in shown_hosts_.    void ShownDelayHandler(); @@ -76,6 +104,12 @@ class ThumbnailGenerator : public RenderWidgetHostPaintingObserver,    // See the setter above.    bool no_timeout_; +  // Map of callback objects by TransportDIB::Handle. +  struct AsyncRequestInfo; +  typedef std::map<TransportDIB::Handle, +                   linked_ptr<AsyncRequestInfo> > ThumbnailCallbackMap; +  ThumbnailCallbackMap callback_map_; +    DISALLOW_COPY_AND_ASSIGN(ThumbnailGenerator);  }; diff --git a/chrome/browser/tab_contents/thumbnail_generator_unittest.cc b/chrome/browser/tab_contents/thumbnail_generator_unittest.cc index 38a40f0..787873c 100644 --- a/chrome/browser/tab_contents/thumbnail_generator_unittest.cc +++ b/chrome/browser/tab_contents/thumbnail_generator_unittest.cc @@ -11,8 +11,8 @@  #include "chrome/common/notification_service.h"  #include "chrome/common/render_messages.h"  #include "chrome/test/testing_profile.h" -#include "testing/gtest/include/gtest/gtest.h"  #include "skia/ext/platform_canvas.h" +#include "testing/gtest/include/gtest/gtest.h"  #include "third_party/skia/include/core/SkColorPriv.h"  static const int kBitmapWidth = 100; @@ -142,7 +142,7 @@ TEST_F(ThumbnailGeneratorTest, DiscardBackingStore) {    SendPaint(TRANSPORT_BLACK);    widget_.WasHidden();    ASSERT_TRUE(BackingStoreManager::ExpireBackingStoreForTest(&widget_)); -  ASSERT_FALSE(widget_.GetBackingStore(false)); +  ASSERT_FALSE(widget_.GetBackingStore(false, false));    // The thumbnail generator should have stashed a thumbnail of the page.    SkBitmap result = generator_.GetThumbnailForRenderer(&widget_); @@ -156,7 +156,7 @@ TEST_F(ThumbnailGeneratorTest, QuickShow) {    SendPaint(TRANSPORT_BLACK);    widget_.WasHidden();    ASSERT_TRUE(BackingStoreManager::ExpireBackingStoreForTest(&widget_)); -  ASSERT_FALSE(widget_.GetBackingStore(false)); +  ASSERT_FALSE(widget_.GetBackingStore(false, false));    // Now show the widget and paint white.    widget_.WasRestored(); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 4677735..6c3dc0a 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -481,6 +481,10 @@          'browser/chromeos/wm_ipc.h',          'browser/chromeos/wm_message_listener.cc',          'browser/chromeos/wm_message_listener.h', +        'browser/chromeos/wm_overview_controller.cc', +        'browser/chromeos/wm_overview_controller.h', +        'browser/chromeos/wm_overview_snapshot.cc', +        'browser/chromeos/wm_overview_snapshot.h',          'browser/cocoa/about_ipc_bridge.h',          'browser/cocoa/about_ipc_bridge.mm',          'browser/cocoa/about_ipc_controller.h', diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 2976575..4e69acb 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -141,6 +141,14 @@ IPC_BEGIN_MESSAGES(View)    // JS garbage, not in purging irreplaceable objects.    IPC_MESSAGE_CONTROL0(ViewMsg_PurgeMemory) +  // Sent to render the view into the supplied transport DIB, scale it +  // by the appropriate scale to make it fit the given size, and +  // return it.  In response to this message, the host generates a +  // ViewHostMsg_PaintAtSize_ACK message. +  IPC_MESSAGE_ROUTED2(ViewMsg_PaintAtSize, +                      TransportDIB::Handle /* dib_handle */, +                      gfx::Size /* size */) +    // Tells the render view that a ViewHostMsg_UpdateRect message was processed.    // This signals the render view that it can send another UpdateRect message.    IPC_MESSAGE_ROUTED0(ViewMsg_UpdateRect_ACK) @@ -1084,6 +1092,12 @@ IPC_BEGIN_MESSAGES(ViewHost)                                navigating to a POST again and we're going to                                show the POST interstitial */ ) +  // Tells the render view that a ViewHostMsg_PaintAtSize message was +  // processed, and the DIB is ready for use. +  IPC_MESSAGE_ROUTED2(ViewHostMsg_PaintAtSize_ACK, +                      TransportDIB::Handle /* dib_handle */, +                      gfx::Size /* size */) +    // Sent to update part of the view.  In response to this message, the host    // generates a ViewMsg_UpdateRect_ACK message.    IPC_MESSAGE_ROUTED1(ViewHostMsg_UpdateRect, diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index d7676ed..243665a 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -149,6 +149,7 @@ IPC_DEFINE_MESSAGE_MAP(RenderWidget)    IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus)    IPC_MESSAGE_HANDLER(ViewMsg_ImeSetInputMode, OnImeSetInputMode)    IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition) +  IPC_MESSAGE_HANDLER(ViewMsg_PaintAtSize, OnMsgPaintAtSize)    IPC_MESSAGE_HANDLER(ViewMsg_Repaint, OnMsgRepaint)    IPC_MESSAGE_HANDLER(ViewMsg_SetTextDirection, OnSetTextDirection)    IPC_MESSAGE_HANDLER(ViewMsg_Move_ACK, OnRequestMoveAck) @@ -250,8 +251,8 @@ void RenderWidget::OnWasRestored(bool needs_repainting) {      return;    needs_repainting_on_restore_ = false; -  // Tag the next paint as a restore ack, which is picked up by DoDeferredUpdate -  // when it sends out the next PaintRect message. +  // Tag the next paint as a restore ack, which is picked up by +  // DoDeferredUpdate when it sends out the next PaintRect message.    set_next_paint_is_restore_ack();    // Generate a full repaint. @@ -268,7 +269,7 @@ void RenderWidget::OnUpdateRectAck() {    update_reply_pending_ = false;    // If we sent an UpdateRect message with a zero-sized bitmap, then we should -  // have no current update buf. +  // have no current paint buffer.    if (current_paint_buf_) {      RenderProcess::current()->ReleaseTransportDIB(current_paint_buf_);      current_paint_buf_ = NULL; @@ -720,6 +721,72 @@ void RenderWidget::OnImeSetComposition(WebCompositionCommand command,    ime_control_busy_ = false;  } +// Forces a repaint even if we're hidden, so we can update backing +// store even before a tab has been shown for the first time, and it +// does it synchronously. +void RenderWidget::OnMsgPaintAtSize(const TransportDIB::Handle& dib_handle, +                                    const gfx::Size& desired_size) { +  if (!webwidget_ || dib_handle == TransportDIB::DefaultHandleValue()) +    return; + +  if (webwidget_->size().isEmpty() || +      desired_size.IsEmpty()) { +    // If one of these is empty, then we just return the dib we were +    // given, to avoid leaking it. +    Send(new ViewHostMsg_PaintAtSize_ACK(routing_id_, +                                         dib_handle, +                                         desired_size)); +    return; +  } + +  // Map the given DIB ID into this process, and unmap it at the end +  // of this function. +  scoped_ptr<TransportDIB> paint_at_scale_buffer(TransportDIB::Map(dib_handle)); + +  DCHECK(paint_at_scale_buffer.get()); +  if (!paint_at_scale_buffer.get()) +    return; + +  // Have to make sure we're laid out before rendering. +  webwidget_->layout(); + +  gfx::Size canvas_size = webwidget_->size(); +  float x_scale = static_cast<float>(desired_size.width()) / +                  static_cast<float>(canvas_size.width()); +  float y_scale = static_cast<float>(desired_size.height()) / +                  static_cast<float>(canvas_size.height()); + +  gfx::Rect orig_bounds(gfx::Point(0,0), canvas_size); +  canvas_size.set_width(static_cast<int>(canvas_size.width() * x_scale)); +  canvas_size.set_height(static_cast<int>(canvas_size.height() * y_scale)); +  gfx::Rect bounds(gfx::Point(0,0), canvas_size); + +  scoped_ptr<skia::PlatformCanvas> canvas( +      paint_at_scale_buffer->GetPlatformCanvas(canvas_size.width(), +                                               canvas_size.height())); +  if (!canvas.get()) { +    NOTREACHED(); +    return; +  } + +  // Reset bounds to what we actually received, but they should be the +  // same. +  DCHECK_EQ(bounds.width(), canvas->getDevice()->width()); +  DCHECK_EQ(bounds.height(), canvas->getDevice()->height()); +  bounds.set_width(canvas->getDevice()->width()); +  bounds.set_height(canvas->getDevice()->height()); + +  canvas->save(); +  // Add the scale factor to the canvas, so that we'll get what we expect. +  canvas->scale(SkFloatToScalar(x_scale), SkFloatToScalar(y_scale)); + +  // Paint the entire thing (using original bounds, not scaled bounds). +  PaintRect(orig_bounds, orig_bounds.origin(), canvas.get()); +  canvas->restore(); + +  Send(new ViewHostMsg_PaintAtSize_ACK(routing_id_, dib_handle, bounds.size())); +} +  void RenderWidget::OnMsgRepaint(const gfx::Size& size_to_paint) {    // During shutdown we can just ignore this message.    if (!webwidget_) @@ -878,4 +945,3 @@ void RenderWidget::CleanupWindowInPluginMoves(gfx::PluginWindowHandle window) {      }    }  } - diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 2fdc1e5..9577630 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -7,6 +7,7 @@  #include <vector> +#include "app/surface/transport_dib.h"  #include "base/basictypes.h"  #include "base/ref_counted.h"  #include "base/shared_memory.h" @@ -18,12 +19,12 @@  #include "gfx/size.h"  #include "ipc/ipc_channel.h"  #include "skia/ext/platform_canvas.h" -#include "third_party/skia/include/core/SkBitmap.h"  #include "third_party/WebKit/WebKit/chromium/public/WebCompositionCommand.h"  #include "third_party/WebKit/WebKit/chromium/public/WebPopupType.h"  #include "third_party/WebKit/WebKit/chromium/public/WebRect.h"  #include "third_party/WebKit/WebKit/chromium/public/WebTextDirection.h"  #include "third_party/WebKit/WebKit/chromium/public/WebWidgetClient.h" +#include "third_party/skia/include/core/SkBitmap.h"  #include "webkit/glue/webcursor.h"  class RenderThreadBase; @@ -160,6 +161,8 @@ class RenderWidget : public IPC::Channel::Listener,                             int cursor_position,                             int target_start, int target_end,                             const string16& ime_string); +  void OnMsgPaintAtSize(const TransportDIB::Handle& dib_id, +                        const gfx::Size& desired_size);    void OnMsgRepaint(const gfx::Size& size_to_paint);    void OnSetTextDirection(WebKit::WebTextDirection direction); diff --git a/gfx/skbitmap_operations.h b/gfx/skbitmap_operations.h index 71f6473..ec55e55 100644 --- a/gfx/skbitmap_operations.h +++ b/gfx/skbitmap_operations.h @@ -78,16 +78,14 @@ class SkBitmapOperations {    static SkBitmap DownsampleByTwoUntilSize(const SkBitmap& bitmap,                                             int min_w, int min_h); - private: -  SkBitmapOperations();  // Class for scoping only. -    // Makes a bitmap half has large in each direction by averaging groups of    // 4 pixels. This is one step in generating a mipmap.    static SkBitmap DownsampleByTwo(const SkBitmap& bitmap); + private: +  SkBitmapOperations();  // Class for scoping only.    FRIEND_TEST(SkBitmapOperationsTest, DownsampleByTwo);    FRIEND_TEST(SkBitmapOperationsTest, DownsampleByTwoSmall);  };  #endif  // APP_GFX_SKBITMAP_OPERATIONS_H_ - diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc index 11af0a1..4bde019 100644 --- a/views/widget/widget_gtk.cc +++ b/views/widget/widget_gtk.cc @@ -1088,7 +1088,8 @@ bool WidgetGtk::ProcessMousePressed(GdkEventButton* event) {    // An event may come from a contained widget which has its own gdk window.    // Translate it to the widget's coordinates. -  int x, y; +  int x = 0; +  int y = 0;    GetContainedWidgetEventCoordinates(event, &x, &y);    last_mouse_event_was_move_ = false;    MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED, x, y, | 
