Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/views/chrome_views_delegate.h"
      6 
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/prefs/scoped_user_pref_update.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "base/time/time.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/profiles/profile_manager.h"
     15 #include "chrome/browser/ui/views/accessibility/accessibility_event_router_views.h"
     16 #include "chrome/common/pref_names.h"
     17 #include "grit/chrome_unscaled_resources.h"
     18 #include "ui/base/resource/resource_bundle.h"
     19 #include "ui/base/ui_base_switches.h"
     20 #include "ui/gfx/rect.h"
     21 #include "ui/gfx/screen.h"
     22 #include "ui/views/widget/native_widget.h"
     23 #include "ui/views/widget/widget.h"
     24 
     25 #if defined(OS_WIN)
     26 #include <dwmapi.h>
     27 #include "base/win/windows_version.h"
     28 #include "chrome/browser/app_icon_win.h"
     29 #include "ui/base/win/shell.h"
     30 #endif
     31 
     32 #if defined(USE_AURA)
     33 #include "ui/aura/root_window.h"
     34 #endif
     35 
     36 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
     37 #include "chrome/browser/ui/host_desktop.h"
     38 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     39 #include "ui/views/widget/native_widget_aura.h"
     40 #endif
     41 
     42 #if defined(USE_ASH)
     43 #include "ash/shell.h"
     44 #include "ash/wm/window_state.h"
     45 #include "chrome/browser/ui/ash/ash_init.h"
     46 #include "chrome/browser/ui/ash/ash_util.h"
     47 #endif
     48 
     49 namespace {
     50 
     51 // If the given window has a profile associated with it, use that profile's
     52 // preference service. Otherwise, store and retrieve the data from Local State.
     53 // This function may return NULL if the necessary pref service has not yet
     54 // been initialized.
     55 // TODO(mirandac): This function will also separate windows by profile in a
     56 // multi-profile environment.
     57 PrefService* GetPrefsForWindow(const views::Widget* window) {
     58   Profile* profile = reinterpret_cast<Profile*>(
     59       window->GetNativeWindowProperty(Profile::kProfileKey));
     60   if (!profile) {
     61     // Use local state for windows that have no explicit profile.
     62     return g_browser_process->local_state();
     63   }
     64   return profile->GetPrefs();
     65 }
     66 
     67 }  // namespace
     68 
     69 ///////////////////////////////////////////////////////////////////////////////
     70 // ChromeViewsDelegate, views::ViewsDelegate implementation:
     71 
     72 void ChromeViewsDelegate::SaveWindowPlacement(const views::Widget* window,
     73                                               const std::string& window_name,
     74                                               const gfx::Rect& bounds,
     75                                               ui::WindowShowState show_state) {
     76   PrefService* prefs = GetPrefsForWindow(window);
     77   if (!prefs)
     78     return;
     79 
     80   DCHECK(prefs->FindPreference(window_name.c_str()));
     81   DictionaryPrefUpdate update(prefs, window_name.c_str());
     82   DictionaryValue* window_preferences = update.Get();
     83   window_preferences->SetInteger("left", bounds.x());
     84   window_preferences->SetInteger("top", bounds.y());
     85   window_preferences->SetInteger("right", bounds.right());
     86   window_preferences->SetInteger("bottom", bounds.bottom());
     87   window_preferences->SetBoolean("maximized",
     88                                  show_state == ui::SHOW_STATE_MAXIMIZED);
     89   gfx::Rect work_area(gfx::Screen::GetScreenFor(window->GetNativeView())->
     90       GetDisplayNearestWindow(window->GetNativeView()).work_area());
     91   window_preferences->SetInteger("work_area_left", work_area.x());
     92   window_preferences->SetInteger("work_area_top", work_area.y());
     93   window_preferences->SetInteger("work_area_right", work_area.right());
     94   window_preferences->SetInteger("work_area_bottom", work_area.bottom());
     95 }
     96 
     97 bool ChromeViewsDelegate::GetSavedWindowPlacement(
     98     const views::Widget* widget,
     99     const std::string& window_name,
    100     gfx::Rect* bounds,
    101     ui::WindowShowState* show_state) const {
    102   PrefService* prefs = g_browser_process->local_state();
    103   if (!prefs)
    104     return false;
    105 
    106   DCHECK(prefs->FindPreference(window_name.c_str()));
    107   const DictionaryValue* dictionary = prefs->GetDictionary(window_name.c_str());
    108   int left, top, right, bottom;
    109   if (!dictionary || !dictionary->GetInteger("left", &left) ||
    110       !dictionary->GetInteger("top", &top) ||
    111       !dictionary->GetInteger("right", &right) ||
    112       !dictionary->GetInteger("bottom", &bottom))
    113     return false;
    114 
    115   bounds->SetRect(left, top, right - left, bottom - top);
    116 
    117   bool maximized = false;
    118   if (dictionary)
    119     dictionary->GetBoolean("maximized", &maximized);
    120   *show_state = maximized ? ui::SHOW_STATE_MAXIMIZED : ui::SHOW_STATE_NORMAL;
    121 
    122 #if defined(USE_ASH)
    123   // On Ash environment, a window won't span across displays.  Adjust
    124   // the bounds to fit the work area.
    125   gfx::NativeView window = widget->GetNativeView();
    126   if (chrome::GetHostDesktopTypeForNativeView(window) ==
    127       chrome::HOST_DESKTOP_TYPE_ASH) {
    128     gfx::Display display = gfx::Screen::GetScreenFor(window)->
    129         GetDisplayMatching(*bounds);
    130     bounds->AdjustToFit(display.work_area());
    131     ash::wm::GetWindowState(window)->set_minimum_visibility(true);
    132   }
    133 #endif
    134   return true;
    135 }
    136 
    137 void ChromeViewsDelegate::NotifyAccessibilityEvent(
    138     views::View* view, ui::AccessibilityTypes::Event event_type) {
    139   AccessibilityEventRouterViews::GetInstance()->HandleAccessibilityEvent(
    140       view, event_type);
    141 }
    142 
    143 void ChromeViewsDelegate::NotifyMenuItemFocused(
    144     const base::string16& menu_name,
    145     const base::string16& menu_item_name,
    146     int item_index,
    147     int item_count,
    148     bool has_submenu) {
    149   AccessibilityEventRouterViews::GetInstance()->HandleMenuItemFocused(
    150       menu_name, menu_item_name, item_index, item_count, has_submenu);
    151 }
    152 
    153 #if defined(OS_WIN)
    154 HICON ChromeViewsDelegate::GetDefaultWindowIcon() const {
    155   return GetAppIcon();
    156 }
    157 
    158 bool ChromeViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const {
    159   return chrome::IsNativeViewInAsh(window);
    160 }
    161 
    162 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
    163 gfx::ImageSkia* ChromeViewsDelegate::GetDefaultWindowIcon() const {
    164   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    165   return rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_64);
    166 }
    167 #endif
    168 
    169 views::NonClientFrameView* ChromeViewsDelegate::CreateDefaultNonClientFrameView(
    170     views::Widget* widget) {
    171 #if defined(USE_ASH)
    172   if (chrome::IsNativeViewInAsh(widget->GetNativeView()))
    173     return ash::Shell::GetInstance()->CreateDefaultNonClientFrameView(widget);
    174 #endif
    175   return NULL;
    176 }
    177 
    178 bool ChromeViewsDelegate::UseTransparentWindows() const {
    179 #if defined(USE_ASH)
    180   // TODO(scottmg): http://crbug.com/133312. This needs context to determine
    181   // if it's desktop or ash.
    182 #if defined(OS_CHROMEOS)
    183   return true;
    184 #else
    185   NOTIMPLEMENTED();
    186   return false;
    187 #endif
    188 #else
    189   return false;
    190 #endif
    191 }
    192 
    193 void ChromeViewsDelegate::AddRef() {
    194   g_browser_process->AddRefModule();
    195 }
    196 
    197 void ChromeViewsDelegate::ReleaseRef() {
    198   g_browser_process->ReleaseModule();
    199 }
    200 
    201 content::WebContents* ChromeViewsDelegate::CreateWebContents(
    202     content::BrowserContext* browser_context,
    203     content::SiteInstance* site_instance) {
    204   return NULL;
    205 }
    206 
    207 void ChromeViewsDelegate::OnBeforeWidgetInit(
    208     views::Widget::InitParams* params,
    209     views::internal::NativeWidgetDelegate* delegate) {
    210   // If we already have a native_widget, we don't have to try to come
    211   // up with one.
    212   if (params->native_widget)
    213     return;
    214 
    215 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
    216   bool use_non_toplevel_window =
    217       params->parent && params->type != views::Widget::InitParams::TYPE_MENU;
    218 
    219 #if defined(OS_WIN)
    220   // On desktop Linux Chrome must run in an environment that supports a variety
    221   // of window managers, some of which do not play nicely with parts of our UI
    222   // that have specific expectations about window sizing and placement. For this
    223   // reason windows opened as top level (params.top_level) are always
    224   // constrained by the browser frame, so we can position them correctly. This
    225   // has some negative side effects, like dialogs being clipped by the browser
    226   // frame, but the side effects are not as bad as the poor window manager
    227   // interactions. On Windows however these WM interactions are not an issue, so
    228   // we open windows requested as top_level as actual top level windows on the
    229   // desktop.
    230   use_non_toplevel_window = use_non_toplevel_window &&
    231       (!params->top_level ||
    232        chrome::GetHostDesktopTypeForNativeView(params->parent) !=
    233           chrome::HOST_DESKTOP_TYPE_NATIVE);
    234 
    235   if (!ui::win::IsAeroGlassEnabled()) {
    236     // If we don't have composition (either because Glass is not enabled or
    237     // because it was disabled at the command line), anything that requires
    238     // transparency will be broken with a toplevel window, so force the use of
    239     // a non toplevel window.
    240     if (params->opacity == views::Widget::InitParams::TRANSLUCENT_WINDOW &&
    241         params->type != views::Widget::InitParams::TYPE_MENU)
    242       use_non_toplevel_window = true;
    243   } else {
    244     // If we're on Vista+ with composition enabled, then we can use toplevel
    245     // windows for most things (they get blended via WS_EX_COMPOSITED, which
    246     // allows for animation effects, but also exceeding the bounds of the parent
    247     // window).
    248     if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH &&
    249         params->parent &&
    250         params->type != views::Widget::InitParams::TYPE_CONTROL &&
    251         params->type != views::Widget::InitParams::TYPE_WINDOW) {
    252       // When we set this to false, we get a DesktopNativeWidgetAura from the
    253       // default case (not handled in this function).
    254       use_non_toplevel_window = false;
    255     }
    256   }
    257 #endif  // OS_WIN
    258 #endif  // USE_AURA
    259 
    260 #if defined(OS_CHROMEOS)
    261   // When we are doing straight chromeos builds, we still need to handle the
    262   // toplevel window case.
    263   // There may be a few remaining widgets in Chrome OS that are not top level,
    264   // but have neither a context nor a parent. Provide a fallback context so
    265   // users don't crash. Developers will hit the DCHECK and should provide a
    266   // context.
    267   if (params->context)
    268     params->context = params->context->GetRootWindow();
    269   DCHECK(params->parent || params->context || params->top_level)
    270       << "Please provide a parent or context for this widget.";
    271   if (!params->parent && !params->context)
    272     params->context = ash::Shell::GetPrimaryRootWindow();
    273 #elif defined(USE_AURA)
    274   // While the majority of the time, context wasn't plumbed through due to the
    275   // existence of a global WindowTreeClient, if this window is a toplevel, it's
    276   // possible that there is no contextual state that we can use.
    277   if (params->parent == NULL && params->context == NULL && params->top_level) {
    278     // We need to make a decision about where to place this window based on the
    279     // desktop type.
    280     switch (chrome::GetActiveDesktop()) {
    281       case chrome::HOST_DESKTOP_TYPE_NATIVE:
    282         // If we're native, we should give this window its own toplevel desktop
    283         // widget.
    284         params->native_widget = new views::DesktopNativeWidgetAura(delegate);
    285         break;
    286 #if defined(USE_ASH)
    287       case chrome::HOST_DESKTOP_TYPE_ASH:
    288         // If we're in ash, give this window the context of the main monitor.
    289         params->context = ash::Shell::GetPrimaryRootWindow();
    290         break;
    291 #endif
    292       default:
    293         NOTREACHED();
    294     }
    295   } else if (use_non_toplevel_window) {
    296     params->native_widget = new views::NativeWidgetAura(delegate);
    297   } else if (params->type != views::Widget::InitParams::TYPE_TOOLTIP) {
    298     // TODO(erg): Once we've threaded context to everywhere that needs it, we
    299     // should remove this check here.
    300     gfx::NativeView to_check =
    301         params->context ? params->context : params->parent;
    302     if (chrome::GetHostDesktopTypeForNativeView(to_check) ==
    303         chrome::HOST_DESKTOP_TYPE_NATIVE) {
    304       params->native_widget = new views::DesktopNativeWidgetAura(delegate);
    305     }
    306   }
    307 #endif
    308 }
    309 
    310 base::TimeDelta
    311 ChromeViewsDelegate::GetDefaultTextfieldObscuredRevealDuration() {
    312   return base::TimeDelta();
    313 }
    314