summaryrefslogtreecommitdiffstats
path: root/chrome/browser/vista_frame.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/vista_frame.cc')
-rw-r--r--chrome/browser/vista_frame.cc1575
1 files changed, 1575 insertions, 0 deletions
diff --git a/chrome/browser/vista_frame.cc b/chrome/browser/vista_frame.cc
new file mode 100644
index 0000000..c804400
--- /dev/null
+++ b/chrome/browser/vista_frame.cc
@@ -0,0 +1,1575 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/vista_frame.h"
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atltheme.h>
+#include <commctrl.h>
+#include <dwmapi.h>
+#include <limits>
+
+#include "base/gfx/native_theme.h"
+#include "base/logging.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/suspend_controller.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/tab_contents_container_view.h"
+#include "chrome/browser/tabs/tab_strip.h"
+#include "chrome/browser/window_clipping_info.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/browser/views/download_shelf_view.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/win_util.h"
+#include "chrome/views/accessibility/view_accessibility.h"
+#include "chrome/views/aero_tooltip_manager.h"
+#include "chrome/views/background.h"
+#include "chrome/views/event.h"
+#include "chrome/views/hwnd_view_container.h"
+#include "chrome/views/hwnd_notification_source.h"
+
+#include "generated_resources.h"
+
+// By how much the toolbar overlaps with the tab strip.
+static const int kToolbarOverlapVertOffset = 3;
+
+// How much space on the right is not used for the tab strip (to provide
+// separation between the tabs and the window controls).
+static const int kTabStripRightHorizOffset = 30;
+
+static const int kResizeCornerSize = 12;
+static const int kResizeBorder = 5;
+static const int kTitlebarHeight = 14;
+static const int kTabShadowSize = 2;
+
+// Status Bubble metrics.
+static const int kStatusBubbleHeight = 20;
+static const int kStatusBubbleOffset = 2;
+
+// The line drawn to separate tab end contents.
+static const int kSeparationLineHeight = 1;
+
+// OTR image offsets.
+static const int kOTRImageHorizMargin = 2;
+static const int kOTRImageVertMargin = 2;
+
+// The DWM puts a light border around the client area - we need to
+// take this border size into account when we reduce its size so that
+// we don't draw our content border dropshadow images over the top.
+static const int kDwmBorderSize = 1;
+
+// When laying out the tabstrip, we size it such that it fits to the left of
+// the window controls. We get the bounds of the window controls by sending a
+// message to the window, but Windows answers the question assuming 96 dpi and
+// a fairly conventional screen layout (i.e. not rotated etc). So we need to
+// hack around this by making sure the tabstrip is at least this amount inset
+// from the right side of the window.
+static const int kWindowControlsMinOffset = 100;
+
+//static
+bool VistaFrame::g_initialized = FALSE;
+
+//static
+SkBitmap** VistaFrame::g_bitmaps;
+
+static const int kImageNames[] = { IDR_CONTENT_BOTTOM_CENTER,
+ IDR_CONTENT_BOTTOM_LEFT_CORNER,
+ IDR_CONTENT_BOTTOM_RIGHT_CORNER,
+ IDR_CONTENT_LEFT_SIDE,
+ IDR_CONTENT_RIGHT_SIDE,
+ IDR_CONTENT_TOP_CENTER,
+ IDR_CONTENT_TOP_LEFT_CORNER,
+ IDR_CONTENT_TOP_RIGHT_CORNER,
+};
+
+typedef enum { CT_BOTTOM_CENTER = 0, CT_BOTTOM_LEFT_CORNER,
+ CT_BOTTOM_RIGHT_CORNER, CT_LEFT_SIDE, CT_RIGHT_SIDE,
+ CT_TOP_CENTER, CT_TOP_LEFT_CORNER, CT_TOP_RIGHT_CORNER };
+
+using ChromeViews::Accelerator;
+using ChromeViews::FocusManager;
+
+//static
+VistaFrame* VistaFrame::CreateFrame(const gfx::Rect& bounds,
+ Browser* browser,
+ bool is_off_the_record) {
+ VistaFrame* instance = new VistaFrame(browser);
+ instance->Create(NULL, bounds.ToRECT(),
+ l10n_util::GetString(IDS_PRODUCT_NAME).c_str());
+ instance->InitAfterHWNDCreated();
+ instance->SetIsOffTheRecord(is_off_the_record);
+ FocusManager::CreateFocusManager(instance->m_hWnd, instance->GetRootView());
+ return instance;
+}
+
+VistaFrame::VistaFrame(Browser* browser)
+ : browser_(browser),
+ root_view_(this, true),
+ tabstrip_(NULL),
+ toolbar_(NULL),
+ tab_contents_container_(NULL),
+ custom_window_enabled_(false),
+ saved_window_placement_(false),
+ on_mouse_leave_armed_(false),
+ in_drag_session_(false),
+ browser_paint_pending_(false),
+ shelf_view_(NULL),
+ bookmark_bar_view_(NULL),
+ info_bar_view_(NULL),
+ is_off_the_record_(false),
+ off_the_record_image_(NULL),
+ ignore_ncactivate_(false),
+ should_save_window_placement_(browser->GetType() != BrowserType::BROWSER) {
+ InitializeIfNeeded();
+}
+
+void VistaFrame::InitializeIfNeeded() {
+ if (!g_initialized) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ g_bitmaps = new SkBitmap*[arraysize(kImageNames)];
+ for (int i = 0; i < arraysize(kImageNames); i++)
+ g_bitmaps[i] = rb.GetBitmapNamed(kImageNames[i]);
+ g_initialized = TRUE;
+ }
+}
+
+VistaFrame::~VistaFrame() {
+ DestroyBrowser();
+}
+
+// On Vista (unlike on XP), we let the OS render the Windows decor (close
+// button, maximize button, etc.). Since the mirroring infrastructure in
+// ChromeViews does not rely on HWND flipping, the Windows decor on Vista are
+// not mirrored for RTL locales; that is, they appear on the upper right
+// instead of on the upper left (see bug http://b/issue?id=1128173).
+//
+// Due to the above, we need to be careful when positioning the tabstrip and
+// the OTR image. The OTR image and the tabstrip are automatically mirrored for
+// RTL locales by the mirroring infrastructure. In order to make sure they are
+// not mirrored, we flip them manually so make sure they don't overlap the
+// Windows decor. Unfortunately, there is no cleaner way to do this because the
+// current ChromeViews mirroring API does not allow mirroring the position of
+// a subset of child Views; in other words, once a View is mirrored (in our
+// case frame_view_), then the positions of all its child Views (including, in
+// our case, the OTR image and the tabstrip) are mirrored. Once bug mentioned
+// above is fixed, we won't need to perform any manual mirroring.
+void VistaFrame::Layout() {
+ CRect client_rect;
+ GetClientRect(&client_rect);
+ int width = client_rect.Width();
+ int height = client_rect.Height();
+
+ root_view_.SetBounds(0, 0, width, height);
+ frame_view_->SetBounds(0, 0, width, height);
+
+ if (IsTabStripVisible()) {
+ tabstrip_->SetVisible(true);
+ int tabstrip_x = g_bitmaps[CT_LEFT_SIDE]->width();
+ if (is_off_the_record_) {
+ off_the_record_image_->SetVisible(true);
+ CSize otr_image_size;
+ off_the_record_image_->GetPreferredSize(&otr_image_size);
+ tabstrip_x += otr_image_size.cx + (2 * kOTRImageHorizMargin);
+ gfx::Rect off_the_record_bounds;
+ if (IsZoomed()) {
+ off_the_record_bounds.SetRect(g_bitmaps[CT_LEFT_SIDE]->width(),
+ kResizeBorder,
+ otr_image_size.cx,
+ tabstrip_->GetPreferredHeight() -
+ kToolbarOverlapVertOffset + 1);
+ } else {
+ off_the_record_bounds.SetRect(g_bitmaps[CT_LEFT_SIDE]->width(),
+ kResizeBorder + kTitlebarHeight +
+ tabstrip_->GetPreferredHeight() -
+ otr_image_size.cy -
+ kToolbarOverlapVertOffset + 1,
+ otr_image_size.cx,
+ otr_image_size.cy);
+ }
+
+ if (frame_view_->UILayoutIsRightToLeft())
+ off_the_record_bounds.set_x(frame_view_->MirroredLeftPointForRect(
+ off_the_record_bounds));
+ off_the_record_image_->SetBounds(off_the_record_bounds.x(),
+ off_the_record_bounds.y(),
+ off_the_record_bounds.width(),
+ off_the_record_bounds.height());
+
+ }
+
+ // If we are maxmized, the tab strip will be in line with the window
+ // controls, so we need to make sure they don't overlap.
+ int controls_offset = 0;
+ if(IsZoomed()) {
+ TITLEBARINFOEX titlebar_info;
+ titlebar_info.cbSize = sizeof(TITLEBARINFOEX);
+ SendMessage(WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info);
+
+ // rgrect[2] refers to the minimize button.
+ controls_offset = client_rect.right - titlebar_info.rgrect[2].left;
+ controls_offset = std::max(controls_offset, kWindowControlsMinOffset);
+ }
+
+ gfx::Rect tabstrip_bounds(tabstrip_x,
+ kResizeBorder + (IsZoomed() ?
+ kDwmBorderSize : kTitlebarHeight),
+ width - tabstrip_x - kTabStripRightHorizOffset -
+ controls_offset,
+ tabstrip_->GetPreferredHeight());
+ if (frame_view_->UILayoutIsRightToLeft() &&
+ (IsZoomed() || is_off_the_record_))
+ tabstrip_bounds.set_x(
+ frame_view_->MirroredLeftPointForRect(tabstrip_bounds));
+ tabstrip_->SetBounds(tabstrip_bounds.x(),
+ tabstrip_bounds.y(),
+ tabstrip_bounds.width(),
+ tabstrip_bounds.height());
+
+ frame_view_->SetContentsOffset(tabstrip_->GetY() +
+ tabstrip_->GetHeight() -
+ kToolbarOverlapVertOffset);
+ } else {
+ tabstrip_->SetBounds(0, 0, 0, 0);
+ tabstrip_->SetVisible(false);
+ if (is_off_the_record_)
+ off_the_record_image_->SetVisible(false);
+ }
+
+ int toolbar_bottom;
+ if (IsToolBarVisible()) {
+ toolbar_->SetVisible(true);
+ toolbar_->SetBounds(g_bitmaps[CT_LEFT_SIDE]->width(),
+ tabstrip_->GetY() + tabstrip_->GetHeight() -
+ kToolbarOverlapVertOffset,
+ width - g_bitmaps[CT_LEFT_SIDE]->width() -
+ g_bitmaps[CT_RIGHT_SIDE]->width(),
+ g_bitmaps[CT_TOP_CENTER]->height());
+ toolbar_->Layout();
+ toolbar_bottom = toolbar_->GetY() + toolbar_->GetHeight();
+ } else {
+ toolbar_->SetBounds(0, 0, 0, 0);
+ toolbar_->SetVisible(false);
+ toolbar_bottom = tabstrip_->GetY() + tabstrip_->GetHeight();
+ }
+ int browser_x, browser_y;
+ int browser_w, browser_h;
+
+ if (IsTabStripVisible()) {
+ browser_x = g_bitmaps[CT_LEFT_SIDE]->width();
+ browser_y = toolbar_bottom;
+ browser_w = width - g_bitmaps[CT_LEFT_SIDE]->width() -
+ g_bitmaps[CT_RIGHT_SIDE]->width();
+ browser_h = height - browser_y - g_bitmaps[CT_BOTTOM_CENTER]->height();
+ } else {
+ browser_x = 0;
+ browser_y = toolbar_bottom;
+ browser_w = width;
+ browser_h = height;
+ }
+
+ CSize preferred_size;
+ if (shelf_view_) {
+ shelf_view_->GetPreferredSize(&preferred_size);
+ shelf_view_->SetBounds(browser_x,
+ height - g_bitmaps[CT_BOTTOM_CENTER]->height() -
+ preferred_size.cy,
+ browser_w,
+ preferred_size.cy);
+ browser_h -= preferred_size.cy;
+ }
+
+ CSize bookmark_bar_size;
+ CSize info_bar_size;
+
+ if (bookmark_bar_view_)
+ bookmark_bar_view_->GetPreferredSize(&bookmark_bar_size);
+
+ if (info_bar_view_)
+ info_bar_view_->GetPreferredSize(&info_bar_size);
+
+ // If we're showing a bookmarks bar in the new tab page style and we
+ // have an infobar showing, we need to flip them.
+ if (info_bar_view_ &&
+ bookmark_bar_view_ &&
+ static_cast<BookmarkBarView*>(bookmark_bar_view_)->IsNewTabPage() &&
+ !static_cast<BookmarkBarView*>(bookmark_bar_view_)->IsAlwaysShown()) {
+ info_bar_view_->SetBounds(browser_x,
+ browser_y,
+ browser_w,
+ info_bar_size.cy);
+ browser_h -= info_bar_size.cy;
+
+ browser_y += info_bar_size.cy - kSeparationLineHeight;
+
+ bookmark_bar_view_->SetBounds(browser_x,
+ browser_y,
+ browser_w,
+ bookmark_bar_size.cy);
+ browser_h -= bookmark_bar_size.cy - kSeparationLineHeight;
+ browser_y += bookmark_bar_size.cy;
+ } else {
+ if (bookmark_bar_view_) {
+ // We want our bookmarks bar to be responsible for drawing its own
+ // separator, so we let it overlap ours.
+ browser_y -= kSeparationLineHeight;
+
+ bookmark_bar_view_->SetBounds(browser_x,
+ browser_y,
+ browser_w,
+ bookmark_bar_size.cy);
+ browser_h -= bookmark_bar_size.cy - kSeparationLineHeight;
+ browser_y += bookmark_bar_size.cy;
+ }
+
+ if (info_bar_view_) {
+ info_bar_view_->SetBounds(browser_x,
+ browser_y,
+ browser_w,
+ info_bar_size.cy);
+ browser_h -= info_bar_size.cy;
+ browser_y += info_bar_size.cy;
+ }
+ }
+
+ // While our OnNCCalcSize handler does a good job of covering most of the
+ // cases where we need to do this, it unfortunately doesn't cover the
+ // case where we're returning from maximized mode.
+ ResetDWMFrame();
+
+ tab_contents_container_->SetBounds(browser_x,
+ browser_y,
+ browser_w,
+ browser_h);
+
+ status_bubble_->SetBounds(browser_x - kStatusBubbleOffset,
+ browser_y + browser_h - kStatusBubbleHeight +
+ kStatusBubbleOffset,
+ width / 3,
+ kStatusBubbleHeight);
+
+ frame_view_->SchedulePaint();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ChromeFrame implementation
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void VistaFrame::Init() {
+ ChromeFrame::RegisterChromeFrame(this);
+
+ // Link the HWND with its root view so we can retrieve the RootView from the
+ // HWND for automation purposes.
+ ChromeViews::SetRootViewForHWND(m_hWnd, &root_view_);
+
+ frame_view_ = new VistaFrameView(this);
+ root_view_.AddChildView(frame_view_);
+ root_view_.SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME));
+ frame_view_->SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME));
+
+ toolbar_ = browser_->GetToolbar();
+ toolbar_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TOOLBAR));
+ frame_view_->AddChildView(toolbar_);
+
+ tabstrip_ = CreateTabStrip(browser_);
+ tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP));
+ frame_view_->AddChildView(tabstrip_);
+
+ if (is_off_the_record_) {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ off_the_record_image_ = new ChromeViews::ImageView();
+ frame_view_->AddViewToDropList(off_the_record_image_);
+ SkBitmap* otr_icon = rb.GetBitmapNamed(IDR_OTR_ICON);
+ off_the_record_image_->SetImage(*otr_icon);
+ off_the_record_image_->SetTooltipText(
+ l10n_util::GetString(IDS_OFF_THE_RECORD_TOOLTIP));
+ off_the_record_image_->SetVerticalAlignment(
+ ChromeViews::ImageView::LEADING);
+ frame_view_->AddChildView(off_the_record_image_);
+ }
+
+ tab_contents_container_ = new TabContentsContainerView();
+ frame_view_->AddChildView(tab_contents_container_);
+
+ status_bubble_.reset(new StatusBubble(this));
+
+ // Add the task manager item to the system menu before the last entry.
+ task_manager_label_text_ = l10n_util::GetString(IDS_TASKMANAGER);
+ HMENU system_menu = ::GetSystemMenu(m_hWnd, FALSE);
+ int index = ::GetMenuItemCount(system_menu) - 1;
+ if (index < 0) {
+ NOTREACHED();
+ // Paranoia check.
+ index = 0;
+ }
+
+ // First we add the separator.
+ MENUITEMINFO menu_info;
+ memset(&menu_info, 0, sizeof(MENUITEMINFO));
+ menu_info.cbSize = sizeof(MENUITEMINFO);
+ menu_info.fMask = MIIM_FTYPE;
+ menu_info.fType = MFT_SEPARATOR;
+ ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
+ // Then the actual menu.
+ menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ menu_info.fType = MFT_STRING;
+ menu_info.fState = MFS_ENABLED;
+ menu_info.wID = IDC_TASKMANAGER;
+ menu_info.dwTypeData = const_cast<wchar_t*>(task_manager_label_text_.c_str());
+ ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
+
+ // Register accelerators.
+ HACCEL accelerators_table = AtlLoadAccelerators(IDR_MAINFRAME);
+ DCHECK(accelerators_table);
+ ChromeFrame::LoadAccelerators(this, accelerators_table, this);
+
+ ShelfVisibilityChanged();
+ root_view_.OnViewContainerCreated();
+ Layout();
+}
+
+TabStrip* VistaFrame::CreateTabStrip(Browser* browser) {
+ return new TabStrip(browser->tabstrip_model());
+}
+
+void VistaFrame::Show(int command, bool adjust_to_fit) {
+ if (adjust_to_fit)
+ win_util::AdjustWindowToFit(*this);
+ ::ShowWindow(*this, command);
+}
+
+void VistaFrame::BrowserDidPaint(HRGN region) {
+ browser_paint_pending_ = false;
+}
+
+// This is called when we receive WM_ENDSESSION. In Vista the we have 5 seconds
+// or will be forcefully terminated if we get stuck servicing this message and
+// not pump the final messages.
+void VistaFrame::OnEndSession(BOOL ending, UINT logoff) {
+ tabstrip_->AbortActiveDragSession();
+ EndSession();
+}
+
+// Note: called directly by the handler macros to handle WM_CLOSE messages.
+void VistaFrame::Close() {
+ // You cannot close a frame for which there is an active originating drag
+ // session.
+ if (tabstrip_->IsDragSessionActive())
+ return;
+
+ // Give beforeunload handlers the chance to cancel the close before we hide
+ // the window below.
+ if (!browser_->ShouldCloseWindow())
+ return;
+
+ // We call this here so that the window position gets saved before moving
+ // the window into hyperspace.
+ if (!saved_window_placement_ && should_save_window_placement_ && browser_) {
+ browser_->SaveWindowPlacement();
+ browser_->SaveWindowPlacementToDatabase();
+ saved_window_placement_ = true;
+ }
+
+ if (browser_ && !browser_->tabstrip_model()->empty()) {
+ // Tab strip isn't empty. Hide the window (so it appears to have closed
+ // immediately) and close all the tabs, allowing the renderers to shut
+ // down. When the tab strip is empty we'll be called back recursively.
+ // NOTE: Don't use ShowWindow(SW_HIDE) here, otherwise end session blocks
+ // here.
+ SetWindowPos(NULL, 0, 0, 0, 0,
+ SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
+ SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
+ browser_->OnWindowClosing();
+ } else {
+ // Empty tab strip, it's now safe to clean-up.
+ root_view_.OnViewContainerDestroyed();
+
+ NotificationService::current()->Notify(
+ NOTIFY_WINDOW_CLOSED, Source<HWND>(m_hWnd),
+ NotificationService::NoDetails());
+
+ DestroyWindow();
+ }
+}
+
+void* VistaFrame::GetPlatformID() {
+ return reinterpret_cast<void*>(m_hWnd);
+}
+
+void VistaFrame::SetAcceleratorTable(
+ std::map<Accelerator, int>* accelerator_table) {
+ accelerator_table_.reset(accelerator_table);
+}
+
+bool VistaFrame::GetAccelerator(int cmd_id,
+ ChromeViews::Accelerator* accelerator) {
+ std::map<ChromeViews::Accelerator, int>::iterator it =
+ accelerator_table_->begin();
+ for (; it != accelerator_table_->end(); ++it) {
+ if(it->second == cmd_id) {
+ *accelerator = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VistaFrame::AcceleratorPressed(const Accelerator& accelerator) {
+ DCHECK(accelerator_table_.get());
+ std::map<Accelerator, int>::const_iterator iter =
+ accelerator_table_->find(accelerator);
+ DCHECK(iter != accelerator_table_->end());
+
+ int command_id = iter->second;
+ if (browser_->SupportsCommand(command_id) &&
+ browser_->IsCommandEnabled(command_id)) {
+ browser_->ExecuteCommand(command_id);
+ return true;
+ }
+ return false;
+}
+
+gfx::Rect VistaFrame::GetNormalBounds() {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ const bool ret = !!GetWindowPlacement(&wp);
+ DCHECK(ret);
+ return gfx::Rect(wp.rcNormalPosition);
+}
+
+bool VistaFrame::IsMaximized() {
+ return !!IsZoomed();
+}
+
+gfx::Rect VistaFrame::GetBoundsForContentBounds(const gfx::Rect content_rect) {
+ if (tab_contents_container_->GetX() == 0 &&
+ tab_contents_container_->GetWidth() == 0) {
+ Layout();
+ }
+
+ CPoint p(0, 0);
+ ChromeViews::View::ConvertPointToViewContainer(tab_contents_container_, &p);
+ CRect bounds;
+ GetBounds(&bounds, true);
+
+ gfx::Rect r;
+ r.set_x(content_rect.x() - p.x);
+ r.set_y(content_rect.y() - p.y);
+ r.set_width(p.x + content_rect.width() +
+ (bounds.Width() - (p.x + tab_contents_container_->GetWidth())));
+ r.set_height(p.y + content_rect.height() +
+ (bounds.Height() - (p.y +
+ tab_contents_container_->GetHeight())));
+ return r;
+}
+
+void VistaFrame::SetBounds(const gfx::Rect& bounds) {
+ SetWindowPos(NULL, bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE);
+}
+
+void VistaFrame::DetachFromBrowser() {
+ browser_->tabstrip_model()->RemoveObserver(tabstrip_);
+ browser_ = NULL;
+}
+
+void VistaFrame::InfoBubbleShowing() {
+ ignore_ncactivate_ = true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Events
+//
+////////////////////////////////////////////////////////////////////////////////
+
+LRESULT VistaFrame::OnSettingChange(UINT u_msg, WPARAM w_param, LPARAM l_param,
+ BOOL& handled) {
+ // Set this to false, if we actually handle the message, we'll set it to
+ // true below.
+ handled = FALSE;
+ if (w_param != SPI_SETWORKAREA)
+ return 0; // Return value is effectively ignored in atlwin.h.
+
+ win_util::AdjustWindowToFit(m_hWnd);
+ handled = TRUE;
+ return 0; // Return value is effectively ignored in atlwin.h.
+}
+
+LRESULT VistaFrame::OnNCActivate(BOOL param) {
+ if (ignore_ncactivate_) {
+ ignore_ncactivate_ = false;
+ return DefWindowProc(WM_NCACTIVATE, TRUE, 0);
+ }
+ SetMsgHandled(false);
+ return 0;
+}
+
+BOOL VistaFrame::OnPowerBroadcast(DWORD power_event, DWORD data) {
+ if (PBT_APMSUSPEND == power_event) {
+ SuspendController::OnSuspend(browser_->profile());
+ return TRUE;
+ } else if (PBT_APMRESUMEAUTOMATIC == power_event) {
+ SuspendController::OnResume(browser_->profile());
+ return TRUE;
+ }
+
+ SetMsgHandled(FALSE);
+ return FALSE;
+}
+
+void VistaFrame::OnThemeChanged() {
+ // Notify NativeTheme.
+ gfx::NativeTheme::instance()->CloseHandles();
+ ChromeFrame::NotifyTabsOfThemeChange(browser_);
+}
+
+void VistaFrame::OnMouseButtonDown(UINT flags, const CPoint& pt) {
+ if (m_hWnd == NULL) {
+ return;
+ }
+
+ if (ProcessMousePressed(pt, flags, FALSE)) {
+ SetMsgHandled(TRUE);
+ } else {
+ SetMsgHandled(FALSE);
+ }
+}
+
+void VistaFrame::OnMouseButtonUp(UINT flags, const CPoint& pt) {
+ if (m_hWnd == NULL) {
+ return;
+ }
+
+ if (in_drag_session_) {
+ ProcessMouseReleased(pt, flags);
+ }
+}
+
+void VistaFrame::OnMouseButtonDblClk(UINT flags, const CPoint& pt) {
+ if (ProcessMousePressed(pt, flags, TRUE)) {
+ SetMsgHandled(TRUE);
+ } else {
+ SetMsgHandled(FALSE);
+ }
+}
+
+void VistaFrame::OnLButtonUp(UINT flags, const CPoint& pt) {
+ OnMouseButtonUp(flags | MK_LBUTTON, pt);
+}
+
+void VistaFrame::OnMButtonUp(UINT flags, const CPoint& pt) {
+ OnMouseButtonUp(flags | MK_MBUTTON, pt);
+}
+
+void VistaFrame::OnRButtonUp(UINT flags, const CPoint& pt) {
+ OnMouseButtonUp(flags | MK_RBUTTON, pt);
+}
+
+void VistaFrame::OnNCMButtonDown(UINT flags, const CPoint& pt) {
+ // The point is in screen coordinate system so we need to convert
+ CRect window_rect;
+ GetWindowRect(&window_rect);
+ CPoint point(pt);
+ point.x -= window_rect.left;
+ point.y -= window_rect.top;
+
+ // Yes we need to add MK_MBUTTON. Windows doesn't include it
+ OnMouseButtonDown(flags | MK_MBUTTON, point);
+}
+
+void VistaFrame::OnNCRButtonDown(UINT flags, const CPoint& pt) {
+ if (flags == HTCAPTION) {
+ HMENU system_menu = ::GetSystemMenu(*this, FALSE);
+ int id = ::TrackPopupMenu(system_menu,
+ TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD,
+ pt.x,
+ pt.y,
+ 0,
+ *this,
+ NULL);
+ if (id)
+ ::SendMessage(*this,
+ WM_SYSCOMMAND,
+ id,
+ 0);
+ } else {
+ SetMsgHandled(FALSE);
+ }
+}
+
+void VistaFrame::OnMouseMove(UINT flags, const CPoint& pt) {
+ if (m_hWnd == NULL) {
+ return;
+ }
+
+ if (in_drag_session_) {
+ ProcessMouseDragged(pt, flags);
+ } else {
+ ArmOnMouseLeave();
+ ProcessMouseMoved(pt, flags);
+ }
+}
+
+void VistaFrame::OnMouseLeave() {
+ if (m_hWnd == NULL) {
+ return;
+ }
+
+ ProcessMouseExited();
+ on_mouse_leave_armed_ = false;
+}
+
+LRESULT VistaFrame::OnGetObject(UINT uMsg, WPARAM w_param, LPARAM object_id) {
+ LRESULT reference_result = static_cast<LRESULT>(0L);
+
+ // Accessibility readers will send an OBJID_CLIENT message
+ if (OBJID_CLIENT == object_id) {
+ // If our MSAA root is already created, reuse that pointer. Otherwise,
+ // create a new one.
+ if (!accessibility_root_) {
+ CComObject<ViewAccessibility>* instance = NULL;
+
+ HRESULT hr = CComObject<ViewAccessibility>::CreateInstance(&instance);
+ DCHECK(SUCCEEDED(hr));
+
+ if (!instance) {
+ // Return with failure.
+ return static_cast<LRESULT>(0L);
+ }
+
+ CComPtr<IAccessible> accessibility_instance(instance);
+
+ if (!SUCCEEDED(instance->Initialize(&root_view_))) {
+ // Return with failure.
+ return static_cast<LRESULT>(0L);
+ }
+
+ // All is well, assign the temp instance to the class smart pointer
+ accessibility_root_.Attach(accessibility_instance.Detach());
+
+ if (!accessibility_root_) {
+ // Return with failure.
+ return static_cast<LRESULT>(0L);
+ }
+
+ // Notify that an instance of IAccessible was allocated for m_hWnd
+ ::NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT,
+ CHILDID_SELF);
+ }
+
+ // Create a reference to ViewAccessibility that MSAA will marshall
+ // to the client.
+ reference_result = LresultFromObject(IID_IAccessible, w_param,
+ static_cast<IAccessible*>(accessibility_root_));
+ }
+ return reference_result;
+}
+
+void VistaFrame::OnKeyDown(TCHAR c, UINT rep_cnt, UINT flags) {
+ ChromeViews::KeyEvent event(ChromeViews::Event::ET_KEY_PRESSED, c,
+ rep_cnt, flags);
+ root_view_.ProcessKeyEvent(event);
+}
+
+void VistaFrame::OnKeyUp(TCHAR c, UINT rep_cnt, UINT flags) {
+ ChromeViews::KeyEvent event(ChromeViews::Event::ET_KEY_RELEASED, c,
+ rep_cnt, flags);
+ root_view_.ProcessKeyEvent(event);
+}
+
+LRESULT VistaFrame::OnAppCommand(
+ HWND w_param, short app_command, WORD device, int keystate) {
+ if (browser_ && !browser_->ExecuteWindowsAppCommand(app_command))
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+void VistaFrame::OnCommand(UINT notification_code,
+ int command_id,
+ HWND window) {
+ if (browser_ && browser_->SupportsCommand(command_id)) {
+ browser_->ExecuteCommand(command_id);
+ } else {
+ SetMsgHandled(FALSE);
+ }
+}
+
+void VistaFrame::OnSysCommand(UINT notification_code, CPoint click) {
+ switch (notification_code) {
+ case IDC_TASKMANAGER:
+ if (browser_)
+ browser_->ExecuteCommand(IDC_TASKMANAGER);
+ break;
+ default:
+ // Use the default implementation for any other command.
+ SetMsgHandled(FALSE);
+ break;
+ }
+}
+
+void VistaFrame::OnMove(const CSize& size) {
+ if (!saved_window_placement_ && should_save_window_placement_ )
+ browser_->SaveWindowPlacementToDatabase();
+
+ browser_->WindowMoved();
+}
+
+void VistaFrame::OnMoving(UINT param, LPRECT new_bounds) {
+ // We want to let the browser know that the window moved so that it can
+ // update the positions of any dependent WS_POPUPs
+ browser_->WindowMoved();
+}
+
+void VistaFrame::OnSize(UINT param, const CSize& size) {
+ Layout();
+
+ if (root_view_.NeedsPainting(false)) {
+ PaintNow(root_view_.GetScheduledPaintRect());
+ }
+
+ if (!saved_window_placement_ && should_save_window_placement_)
+ browser_->SaveWindowPlacementToDatabase();
+}
+
+void VistaFrame::OnFinalMessage(HWND hwnd) {
+ delete this;
+}
+
+void VistaFrame::OnNCLButtonDown(UINT flags, const CPoint& pt) {
+ SetMsgHandled(false);
+}
+
+LRESULT VistaFrame::OnNCCalcSize(BOOL w_param, LPARAM l_param) {
+ // By default the client side is set to the window size which is what
+ // we want.
+ if (w_param == TRUE) {
+ // Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
+ NCCALCSIZE_PARAMS *pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param);
+
+ // Hack necessary to stop black background flicker, we cut out
+ // resizeborder here to save us from having to do too much
+ // addition and subtraction in Layout(). We don't cut off the
+ // top + titlebar as that prevents the window controls from
+ // highlighting.
+ pncsp->rgrc[0].left = pncsp->rgrc[0].left + kResizeBorder;
+ pncsp->rgrc[0].right = pncsp->rgrc[0].right - kResizeBorder;
+ pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - kResizeBorder;
+
+ // We need to reset the frame, as Vista resets it whenever it changes
+ // composition modes (and NCCALCSIZE is the closest thing we get to
+ // a reliable message about the change).
+ ResetDWMFrame();
+
+ SetMsgHandled(TRUE);
+ return 0;
+ }
+
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+LRESULT VistaFrame::OnNCHitTest(const CPoint& pt) {
+ SetMsgHandled(TRUE);
+
+ // Test the caption buttons
+ LRESULT l_res;
+ BOOL dwm_processed = DwmDefWindowProc(m_hWnd,
+ WM_NCHITTEST,
+ 0,
+ (LPARAM)MAKELONG(pt.x, pt.y),
+ &l_res);
+
+ if (dwm_processed) {
+ return l_res;
+ }
+
+ CRect cr;
+ GetBounds(&cr, false);
+
+ CPoint tab_pt(pt);
+ ChromeViews::View::ConvertPointToView(NULL, tabstrip_, &tab_pt);
+
+ // If we are over the tabstrip
+ if (tab_pt.x > 0 && tab_pt.y >= kTabShadowSize &&
+ tab_pt.x < tabstrip_->GetWidth() &&
+ tab_pt.y < tabstrip_->GetHeight()) {
+ ChromeViews::View* v = tabstrip_->GetViewForPoint(tab_pt);
+ if (v == tabstrip_)
+ return HTCAPTION;
+
+ // If the view under mouse is a tab, check if the tab strip allows tab
+ // dragging or not. If not, return caption to get window dragging.
+ if (v->GetClassName() == Tab::kTabClassName &&
+ !tabstrip_->HasAvailableDragActions())
+ return HTCAPTION;
+
+ return HTCLIENT;
+ }
+
+ CPoint p(pt);
+ CRect r;
+ GetWindowRect(&r);
+
+ // Convert from screen to window coordinates
+ p.x -= r.left;
+ p.y -= r.top;
+
+ if (p.x < kResizeBorder + g_bitmaps[CT_LEFT_SIDE]->width()) {
+ if (p.y < kResizeCornerSize) {
+ return HTTOPLEFT;
+ } else if (p.y >= (r.Height() - kResizeCornerSize)) {
+ return HTBOTTOMLEFT;
+ } else {
+ return HTLEFT;
+ }
+ // BOTTOM_LEFT / TOP_LEFT horizontal extensions
+ } else if (p.x < kResizeCornerSize) {
+ if (p.y < kResizeBorder) {
+ return HTTOPLEFT;
+ } else if (p.y >= (r.Height() - kResizeBorder)) {
+ return HTBOTTOMLEFT;
+ }
+ // EAST / BOTTOMRIGHT / TOPRIGHT edge
+ } else if (p.x >= (r.Width() - kResizeBorder -
+ g_bitmaps[CT_RIGHT_SIDE]->width())) {
+ if (p.y < kResizeCornerSize) {
+ return HTTOPRIGHT;
+ } else if (p.y >= (r.Height() - kResizeCornerSize)) {
+ return HTBOTTOMRIGHT;
+ } else {
+ return HTRIGHT;
+ }
+ // EAST / BOTTOMRIGHT / TOPRIGHT horizontal extensions
+ } else if (p.x >= (r.Width() - kResizeCornerSize)) {
+ if (p.y < kResizeBorder) {
+ return HTTOPRIGHT;
+ } else if (p.y >= (r.Height() - kResizeBorder)) {
+ return HTBOTTOMRIGHT;
+ }
+ // TOP edge
+ } else if (p.y < kResizeBorder) {
+ return HTTOP;
+ // BOTTOM edge
+ } else if (p.y >= (r.Height() - kResizeBorder -
+ g_bitmaps[CT_BOTTOM_CENTER]->height())) {
+ return HTBOTTOM;
+ }
+
+ if (p.y <= tabstrip_->GetY() + tabstrip_->GetHeight()) {
+ return HTCAPTION;
+ }
+
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+void VistaFrame::OnActivate(UINT n_state, BOOL is_minimized, HWND other) {
+ if (ActivateAppModalDialog(browser_))
+ return;
+
+ // Enable our custom window if we haven't already (this works in combination
+ // with our NCCALCSIZE handler).
+ if (!custom_window_enabled_) {
+ RECT rcClient;
+ ::GetWindowRect(m_hWnd, &rcClient);
+ ::SetWindowPos(
+ m_hWnd,
+ NULL,
+ rcClient.left, rcClient.top,
+ rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
+ SWP_FRAMECHANGED);
+ custom_window_enabled_ = true;
+
+ // We need to fire this here as well as in OnNCCalcSize, as that function
+ // does not fire at the right time for this to work when opening the
+ // window.
+ ResetDWMFrame();
+ }
+
+ SetMsgHandled(FALSE);
+ if (!is_minimized)
+ browser_->WindowActivationChanged(n_state != WA_INACTIVE);
+}
+
+int VistaFrame::OnMouseActivate(CWindow wndTopLevel, UINT nHitTest,
+ UINT message) {
+ return ActivateAppModalDialog(browser_) ? MA_NOACTIVATEANDEAT : MA_ACTIVATE;
+}
+
+void VistaFrame::OnPaint(HDC dc) {
+ // Warning: on Vista the ChromeCanvasPaint *must* use an opaque flag of true
+ // so that ChromeCanvasPaint performs a BitBlt and not an alpha blend.
+ //
+ root_view_.OnPaint(*this);
+}
+
+LRESULT VistaFrame::OnEraseBkgnd(HDC dc) {
+ SetMsgHandled(TRUE);
+ return 0;
+}
+
+void VistaFrame::ArmOnMouseLeave() {
+ if (!on_mouse_leave_armed_) {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = *this;
+ tme.dwHoverTime = 0;
+ TrackMouseEvent(&tme);
+ on_mouse_leave_armed_ = TRUE;
+ }
+}
+
+void VistaFrame::OnCaptureChanged(HWND hwnd) {
+ if (in_drag_session_)
+ root_view_.ProcessMouseDragCanceled();
+ in_drag_session_ = false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// View events propagation
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool VistaFrame::ProcessMousePressed(const CPoint& pt, UINT flags,
+ bool dbl_click) {
+ using namespace ChromeViews;
+ MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED,
+ pt.x,
+ pt.y,
+ (dbl_click ? MouseEvent::EF_IS_DOUBLE_CLICK : 0) |
+ Event::ConvertWindowsFlags(flags));
+ if (root_view_.OnMousePressed(mouse_pressed)) {
+ // If an additional button is pressed during a drag session we don't want
+ // to call SetCapture() again as it will result in no more events.
+ if (!in_drag_session_) {
+ in_drag_session_ = true;
+ SetCapture();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void VistaFrame::ProcessMouseDragged(const CPoint& pt, UINT flags) {
+ using namespace ChromeViews;
+ MouseEvent drag_event(Event::ET_MOUSE_DRAGGED,
+ pt.x,
+ pt.y,
+ Event::ConvertWindowsFlags(flags));
+ root_view_.OnMouseDragged(drag_event);
+}
+
+void VistaFrame::ProcessMouseReleased(const CPoint& pt, UINT flags) {
+ using namespace ChromeViews;
+ if (in_drag_session_) {
+ in_drag_session_ = false;
+ ReleaseCapture();
+ }
+ MouseEvent mouse_released(Event::ET_MOUSE_RELEASED,
+ pt.x,
+ pt.y,
+ Event::ConvertWindowsFlags(flags));
+ root_view_.OnMouseReleased(mouse_released, false);
+}
+
+void VistaFrame::ProcessMouseMoved(const CPoint &pt, UINT flags) {
+ using namespace ChromeViews;
+ MouseEvent mouse_move(Event::ET_MOUSE_MOVED,
+ pt.x,
+ pt.y,
+ Event::ConvertWindowsFlags(flags));
+ root_view_.OnMouseMoved(mouse_move);
+}
+
+void VistaFrame::ProcessMouseExited() {
+ root_view_.ProcessOnMouseExited();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ChromeViews::ViewContainer
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void VistaFrame::GetBounds(CRect *out, bool including_frame) const {
+ if (including_frame) {
+ GetWindowRect(out);
+ } else {
+ GetClientRect(out);
+
+ POINT p = {0, 0};
+ ::ClientToScreen(*this, &p);
+
+ out->left += p.x;
+ out->top += p.y;
+ out->right += p.x;
+ out->bottom += p.y;
+ }
+}
+
+void VistaFrame::MoveToFront(bool should_activate) {
+ int flags = SWP_NOMOVE | SWP_NOSIZE;
+ if (!should_activate) {
+ flags |= SWP_NOACTIVATE;
+ }
+ SetWindowPos(HWND_TOP, 0, 0, 0, 0, flags);
+ SetForegroundWindow(*this);
+}
+
+HWND VistaFrame::GetHWND() const {
+ return m_hWnd;
+}
+
+void VistaFrame::PaintNow(const CRect& update_rect) {
+ if (!update_rect.IsRectNull() && IsVisible()) {
+ RedrawWindow(update_rect,
+ NULL,
+ RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_NOERASE);
+ }
+}
+
+ChromeViews::RootView* VistaFrame::GetRootView() {
+ return const_cast<ChromeViews::RootView*>(&root_view_);
+}
+
+bool VistaFrame::IsVisible() {
+ return !!::IsWindowVisible(*this);
+}
+
+bool VistaFrame::IsActive() {
+ return win_util::IsWindowActive(*this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// VistaFrameView
+//
+////////////////////////////////////////////////////////////////////////////////
+void VistaFrame::VistaFrameView::PaintContentsBorder(ChromeCanvas* canvas,
+ int x,
+ int y,
+ int w,
+ int h) {
+ if (!parent_->IsTabStripVisible())
+ return;
+ int ro, bo;
+ canvas->DrawBitmapInt(*g_bitmaps[CT_TOP_LEFT_CORNER], x, y);
+ canvas->TileImageInt(*g_bitmaps[CT_TOP_CENTER],
+ x + g_bitmaps[CT_TOP_LEFT_CORNER]->width(),
+ y,
+ w - g_bitmaps[CT_TOP_LEFT_CORNER]->width() -
+ g_bitmaps[CT_TOP_RIGHT_CORNER]->width(),
+ g_bitmaps[CT_TOP_CENTER]->height());
+ ro = x + w - g_bitmaps[CT_TOP_RIGHT_CORNER]->width();
+ canvas->DrawBitmapInt(*g_bitmaps[CT_TOP_RIGHT_CORNER], ro, y);
+ canvas->TileImageInt(*g_bitmaps[CT_RIGHT_SIDE],
+ ro,
+ y + g_bitmaps[CT_TOP_RIGHT_CORNER]->height(),
+ g_bitmaps[CT_RIGHT_SIDE]->width(),
+ h - (g_bitmaps[CT_TOP_RIGHT_CORNER]->height() +
+ g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->height()));
+ bo = y + h - g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->height();
+ canvas->DrawBitmapInt(*g_bitmaps[CT_BOTTOM_RIGHT_CORNER],
+ x + w - g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->width(),
+ bo);
+
+ canvas->TileImageInt(*g_bitmaps[CT_BOTTOM_CENTER],
+ x + g_bitmaps[CT_BOTTOM_LEFT_CORNER]->width(),
+ bo,
+ w - (g_bitmaps[CT_BOTTOM_LEFT_CORNER]->width() +
+ g_bitmaps[CT_BOTTOM_RIGHT_CORNER]->width()),
+ g_bitmaps[CT_BOTTOM_CENTER]->height());
+ canvas->DrawBitmapInt(*g_bitmaps[CT_BOTTOM_LEFT_CORNER], x, bo);
+ canvas->TileImageInt(*g_bitmaps[CT_LEFT_SIDE],
+ x,
+ y + g_bitmaps[CT_TOP_LEFT_CORNER]->height(),
+ g_bitmaps[CT_LEFT_SIDE]->width(),
+ h - (g_bitmaps[CT_TOP_LEFT_CORNER]->height() +
+ g_bitmaps[CT_BOTTOM_LEFT_CORNER]->height()));
+}
+
+void VistaFrame::VistaFrameView::Paint(ChromeCanvas* canvas) {
+ canvas->save();
+
+ // When painting the border, exclude the contents area. This will prevent the
+ // border bitmaps (which might be larger than the visible area) from coming
+ // into the content area when there is no tab painted yet.
+ int x = parent_->tab_contents_container_->GetX();
+ int y = parent_->tab_contents_container_->GetY();
+ SkRect clip;
+ clip.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + parent_->tab_contents_container_->GetWidth()),
+ SkIntToScalar(y + parent_->tab_contents_container_->GetHeight()));
+ canvas->clipRect(clip, SkRegion::kDifference_Op);
+
+ PaintContentsBorder(canvas,
+ 0,
+ contents_offset_,
+ GetWidth(),
+ GetHeight() - contents_offset_);
+
+ canvas->restore();
+}
+
+void VistaFrame::VistaFrameView::SetContentsOffset(int o) {
+ contents_offset_ = o;
+}
+
+bool VistaFrame::VistaFrameView::GetAccessibleRole(VARIANT* role) {
+ DCHECK(role);
+
+ role->vt = VT_I4;
+ role->lVal = ROLE_SYSTEM_CLIENT;
+ return true;
+}
+
+bool VistaFrame::VistaFrameView::GetAccessibleName(std::wstring* name) {
+ if (!accessible_name_.empty()) {
+ name->assign(accessible_name_);
+ return true;
+ }
+ return false;
+}
+
+void VistaFrame::VistaFrameView::SetAccessibleName(const std::wstring& name) {
+ accessible_name_.assign(name);
+}
+
+// Helper function to extract the min/max x-coordinate as well as the max
+// y coordinate from the TITLEBARINFOEX struct at the specified index.
+static void UpdatePosition(const TITLEBARINFOEX& info,
+ int element,
+ int* min_x,
+ int* max_x,
+ int* max_y) {
+ if ((info.rgstate[element] & (STATE_SYSTEM_INVISIBLE |
+ STATE_SYSTEM_OFFSCREEN |
+ STATE_SYSTEM_UNAVAILABLE)) == 0) {
+ *min_x = std::min(*min_x, static_cast<int>(info.rgrect[element].left));
+ *max_x = std::max(*max_x, static_cast<int>(info.rgrect[element].right));
+ *max_y = std::max(*max_y, static_cast<int>(info.rgrect[element].bottom));
+ }
+}
+
+bool VistaFrame::VistaFrameView::ShouldForwardToTabStrip(
+ const ChromeViews::DropTargetEvent& event) {
+ if (!FrameView::ShouldForwardToTabStrip(event))
+ return false;
+
+ TITLEBARINFOEX titlebar_info;
+ titlebar_info.cbSize = sizeof(TITLEBARINFOEX);
+ SendMessage(parent_->m_hWnd, WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info);
+
+ int min_x = std::numeric_limits<int>::max();
+ int max_x = std::numeric_limits<int>::min();
+ int max_y = std::numeric_limits<int>::min();
+
+ // 2 is the minimize button.
+ UpdatePosition(titlebar_info, 2, &min_x, &max_x, &max_y);
+ // 3 is the maximize button.
+ UpdatePosition(titlebar_info, 3, &min_x, &max_x, &max_y);
+ // 5 is the close button.
+ UpdatePosition(titlebar_info, 5, &min_x, &max_x, &max_y);
+
+ if (min_x != std::numeric_limits<int>::max() &&
+ max_x != std::numeric_limits<int>::min() &&
+ max_y != std::numeric_limits<int>::min()) {
+ CPoint screen_drag_point(event.GetX(), event.GetY());
+ ConvertPointToScreen(this, &screen_drag_point);
+ if (screen_drag_point.x >= min_x && screen_drag_point.x <= max_x &&
+ screen_drag_point.y <= max_y) {
+ return false;
+ }
+ }
+ return true;
+}
+
+LRESULT VistaFrame::OnMouseRange(UINT u_msg, WPARAM w_param, LPARAM l_param,
+ BOOL& handled) {
+ // Tooltip handling is broken in Vista when using custom frames, so
+ // we have to implement a lot of this ourselves.
+ tooltip_manager_->OnMouse(u_msg, w_param, l_param);
+ handled = FALSE;
+
+ return 0;
+}
+
+LRESULT VistaFrame::OnNotify(int w_param, NMHDR* l_param) {
+ bool handled;
+ LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled);
+ SetMsgHandled(handled);
+ return result;
+}
+
+ChromeViews::TooltipManager* VistaFrame::GetTooltipManager() {
+ return tooltip_manager_.get();
+}
+
+StatusBubble* VistaFrame::GetStatusBubble() {
+ return status_bubble_.get();
+}
+
+void VistaFrame::InitAfterHWNDCreated() {
+ tooltip_manager_.reset(new ChromeViews::AeroTooltipManager(this, m_hWnd));
+}
+
+void VistaFrame::ResetDWMFrame() {
+ if (IsTabStripVisible()) {
+ // Note: we don't use DwmEnableBlurBehindWindow because any region not
+ // included in the glass region is composited source over. This means
+ // that anything drawn directly with GDI appears fully transparent.
+ //
+ // We want this region to extend past our content border images, as they
+ // may be partially-transparent.
+ MARGINS margins = {kDwmBorderSize +
+ g_bitmaps[CT_TOP_LEFT_CORNER]->width(),
+ kDwmBorderSize +
+ g_bitmaps[CT_TOP_RIGHT_CORNER]->width(),
+ kDwmBorderSize +
+ IsToolBarVisible() ?
+ toolbar_->GetY() + kToolbarOverlapVertOffset :
+ tabstrip_->GetHeight(),
+ kDwmBorderSize +
+ g_bitmaps[CT_BOTTOM_CENTER]->height()};
+
+ DwmExtendFrameIntoClientArea(*this, &margins);
+ }
+}
+
+void VistaFrame::ShelfVisibilityChanged() {
+ ShelfVisibilityChangedImpl(browser_->GetSelectedTabContents());
+}
+
+void VistaFrame::SelectedTabToolbarSizeChanged(bool is_animating) {
+ if (is_animating) {
+ tab_contents_container_->set_fast_resize(true);
+ ShelfVisibilityChanged();
+ tab_contents_container_->set_fast_resize(false);
+ } else {
+ ShelfVisibilityChanged();
+ tab_contents_container_->UpdateHWNDBounds();
+ }
+}
+
+bool VistaFrame::UpdateChildViewAndLayout(ChromeViews::View* new_view,
+ ChromeViews::View** view) {
+ DCHECK(view);
+ if (*view == new_view) {
+ // The views haven't changed, if the views pref changed schedule a layout.
+ if (new_view) {
+ CSize pref_size;
+ new_view->GetPreferredSize(&pref_size);
+ if (pref_size.cy != new_view->GetHeight())
+ return true;
+ }
+ return false;
+ }
+
+ // The views differ, and one may be null (but not both). Remove the old
+ // view (if it non-null), and add the new one (if it is non-null). If the
+ // height has changed, schedule a layout, otherwise reuse the existing
+ // bounds to avoid scheduling a layout.
+
+ int current_height = 0;
+ if (*view) {
+ current_height = (*view)->GetHeight();
+ root_view_.RemoveChildView(*view);
+ }
+
+ int new_height = 0;
+ if (new_view) {
+ CSize preferred_size;
+ new_view->GetPreferredSize(&preferred_size);
+ new_height = preferred_size.cy;
+ root_view_.AddChildView(new_view);
+ }
+
+ bool changed = false;
+ if (new_height != current_height) {
+ changed = true;
+ } else if (new_view && *view) {
+ // The view changed, but the new view wants the same size, give it the
+ // bounds of the last view and have it repaint.
+ CRect last_bounds;
+ (*view)->GetBounds(&last_bounds);
+ new_view->SetBounds(last_bounds.left, last_bounds.top,
+ last_bounds.Width(), last_bounds.Height());
+ new_view->SchedulePaint();
+ } else if (new_view) {
+ DCHECK(new_height == 0);
+ // The heights are the same, but the old view is null. This only happens
+ // when the height is zero. Zero out the bounds.
+ new_view->SetBounds(0, 0, 0, 0);
+ }
+ *view = new_view;
+ return changed;
+}
+
+void VistaFrame::SetWindowTitle(const std::wstring& title) {
+ SetWindowText(title.c_str());
+}
+
+void VistaFrame::Activate() {
+ if (IsIconic())
+ ShowWindow(SW_RESTORE);
+ MoveToFront(true);
+}
+
+void VistaFrame::FlashFrame() {
+ FLASHWINFO flash_window_info;
+ flash_window_info.cbSize = sizeof(FLASHWINFO);
+ flash_window_info.hwnd = GetHWND();
+ flash_window_info.dwFlags = FLASHW_ALL;
+ flash_window_info.uCount = 4;
+ flash_window_info.dwTimeout = 0;
+ ::FlashWindowEx(&flash_window_info);
+}
+
+void VistaFrame::ShowTabContents(TabContents* selected_contents) {
+ tab_contents_container_->SetTabContents(selected_contents);
+
+ // Force a LoadingStateChanged notification because the TabContents
+ // could be loading (such as when the user unconstrains a tab.
+ if (selected_contents && selected_contents->delegate())
+ selected_contents->delegate()->LoadingStateChanged(selected_contents);
+
+ ShelfVisibilityChangedImpl(selected_contents);
+}
+
+TabStrip* VistaFrame::GetTabStrip() const {
+ return tabstrip_;
+}
+
+void VistaFrame::ContinueDetachConstrainedWindowDrag(const gfx::Point& mouse_pt,
+ int frame_component) {
+ // Need to force a paint at this point so that the newly created window looks
+ // correct. (Otherwise parts of the tabstrip are clipped).
+ CRect cr;
+ GetClientRect(&cr);
+ PaintNow(cr);
+
+ // The user's mouse is already moving, and the left button is down, but we
+ // need to start moving this frame, so we _post_ it a NCLBUTTONDOWN message
+ // with the HTCAPTION flag to trick windows into believing the user just
+ // started dragging on the title bar. All the frame moving is then handled
+ // automatically by windows. Note that we use PostMessage here since we need
+ // to return to the message loop first otherwise Windows' built in move code
+ // will not be able to be triggered.
+ POINTS pts;
+ pts.x = mouse_pt.x();
+ pts.y = mouse_pt.y();
+ PostMessage(WM_NCLBUTTONDOWN, frame_component,
+ reinterpret_cast<LPARAM>(&pts));
+}
+
+void VistaFrame::SizeToContents(const gfx::Rect& contents_bounds) {
+ // First we need to ensure everything has an initial size. Currently, the
+ // window has the wrong size, but that's OK, doing this will allow us to
+ // figure out how big all the UI bits are.
+ Layout();
+
+ // Now figure out the height of the non-client area (the Native windows
+ // chrome) by comparing the window bounds to the client bounds.
+ CRect window_bounds, client_bounds;
+ GetBounds(&window_bounds, true);
+ GetBounds(&client_bounds, false);
+ int left_edge_width = client_bounds.left - window_bounds.left;
+ int top_edge_height = client_bounds.top - window_bounds.top;
+ int right_edge_width = window_bounds.right - client_bounds.right;
+ int bottom_edge_height = window_bounds.bottom - client_bounds.bottom;
+
+ // Now resize the window. This will result in Layout() getting called again
+ // and the contents getting sized to the value specified in |contents_bounds|
+ SetWindowPos(NULL, contents_bounds.x() - left_edge_width,
+ contents_bounds.y() - top_edge_height,
+ contents_bounds.width() + left_edge_width + right_edge_width,
+ contents_bounds.height() + top_edge_height + bottom_edge_height,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+}
+
+void VistaFrame::SetIsOffTheRecord(bool value) {
+ is_off_the_record_ = value;
+}
+
+TabContentsContainerView* VistaFrame::GetTabContentsContainer() {
+ return tab_contents_container_;
+}
+
+void VistaFrame::DestroyBrowser() {
+ // TODO(beng): (Cleanup) tidy this up, just fixing the build red for now.
+ // Need to do this first, before the browser_ is deleted and we can't remove
+ // the tabstrip from the model's observer list because the model was
+ // destroyed with browser_.
+ if (browser_) {
+ browser_->tabstrip_model()->RemoveObserver(tabstrip_);
+ delete browser_;
+ browser_ = NULL;
+ }
+}
+
+void VistaFrame::ShelfVisibilityChangedImpl(TabContents* current_tab) {
+ // Coalesce layouts.
+ bool changed = false;
+
+ ChromeViews::View* new_shelf = NULL;
+ if (current_tab && current_tab->IsDownloadShelfVisible())
+ new_shelf = current_tab->GetDownloadShelfView();
+ changed |= UpdateChildViewAndLayout(new_shelf, &shelf_view_);
+
+ ChromeViews::View* new_info_bar = NULL;
+ if (current_tab && current_tab->IsInfoBarVisible())
+ new_info_bar = current_tab->GetInfoBarView();
+ changed |= UpdateChildViewAndLayout(new_info_bar, &info_bar_view_);
+
+ ChromeViews::View* new_bookmark_bar_view = NULL;
+ if (SupportsBookmarkBar() && current_tab)
+ new_bookmark_bar_view = browser_->GetBookmarkBarView();
+ changed |= UpdateChildViewAndLayout(new_bookmark_bar_view,
+ &bookmark_bar_view_);
+
+ // Only do a layout if the current contents is non-null. We assume that if the
+ // contents is NULL, we're either being destroyed, or ShowTabContents is going
+ // to be invoked with a non-null TabContents again so that there is no need
+ // in doing a layout now (and would result in extra work/invalidation on
+ // tab switches).
+ if (changed && current_tab)
+ Layout();
+}