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