Home | History | Annotate | Download | only in apps
      1 // Copyright 2014 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/apps/chrome_native_app_window_views_win.h"
      6 
      7 #include "apps/ui/views/app_window_frame_view.h"
      8 #include "ash/shell.h"
      9 #include "base/command_line.h"
     10 #include "base/files/file_util.h"
     11 #include "base/path_service.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/threading/sequenced_worker_pool.h"
     14 #include "chrome/browser/apps/per_app_settings_service.h"
     15 #include "chrome/browser/apps/per_app_settings_service_factory.h"
     16 #include "chrome/browser/jumplist_updater_win.h"
     17 #include "chrome/browser/metro_utils/metro_chrome_win.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/browser/shell_integration.h"
     20 #include "chrome/browser/ui/views/apps/app_window_desktop_native_widget_aura_win.h"
     21 #include "chrome/browser/ui/views/apps/glass_app_window_frame_view_win.h"
     22 #include "chrome/browser/web_applications/web_app.h"
     23 #include "chrome/browser/web_applications/web_app_win.h"
     24 #include "chrome/common/chrome_icon_resources_win.h"
     25 #include "chrome/common/chrome_switches.h"
     26 #include "chrome/grit/generated_resources.h"
     27 #include "content/public/browser/browser_thread.h"
     28 #include "extensions/browser/app_window/app_window.h"
     29 #include "extensions/browser/app_window/app_window_registry.h"
     30 #include "extensions/browser/extension_util.h"
     31 #include "extensions/common/extension.h"
     32 #include "ui/aura/remote_window_tree_host_win.h"
     33 #include "ui/base/l10n/l10n_util.h"
     34 #include "ui/base/win/shell.h"
     35 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     36 #include "ui/views/win/hwnd_util.h"
     37 
     38 ChromeNativeAppWindowViewsWin::ChromeNativeAppWindowViewsWin()
     39     : glass_frame_view_(NULL), weak_ptr_factory_(this) {
     40 }
     41 
     42 void ChromeNativeAppWindowViewsWin::ActivateParentDesktopIfNecessary() {
     43   // Only switching into Ash from Native is supported. Tearing the user out of
     44   // Metro mode can only be done by launching a process from Metro mode itself.
     45   // This is done for launching apps, but not regular activations.
     46   if (IsRunningInAsh() &&
     47       chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_NATIVE) {
     48     chrome::ActivateMetroChrome();
     49   }
     50 }
     51 
     52 HWND ChromeNativeAppWindowViewsWin::GetNativeAppWindowHWND() const {
     53   return views::HWNDForWidget(widget()->GetTopLevelWidget());
     54 }
     55 
     56 bool ChromeNativeAppWindowViewsWin::IsRunningInAsh() {
     57   if (!ash::Shell::HasInstance())
     58     return false;
     59 
     60   views::Widget* widget =
     61       implicit_cast<views::WidgetDelegate*>(this)->GetWidget();
     62   chrome::HostDesktopType host_desktop_type =
     63       chrome::GetHostDesktopTypeForNativeWindow(widget->GetNativeWindow());
     64   return host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH;
     65 }
     66 
     67 void ChromeNativeAppWindowViewsWin::EnsureCaptionStyleSet() {
     68   // Windows seems to have issues maximizing windows without WS_CAPTION.
     69   // The default views / Aura implementation will remove this if we are using
     70   // frameless or colored windows, so we put it back here.
     71   HWND hwnd = GetNativeAppWindowHWND();
     72   int current_style = ::GetWindowLong(hwnd, GWL_STYLE);
     73   ::SetWindowLong(hwnd, GWL_STYLE, current_style | WS_CAPTION);
     74 }
     75 
     76 void ChromeNativeAppWindowViewsWin::OnBeforeWidgetInit(
     77     views::Widget::InitParams* init_params,
     78     views::Widget* widget) {
     79   content::BrowserContext* browser_context = app_window()->browser_context();
     80   std::string extension_id = app_window()->extension_id();
     81   // If an app has any existing windows, ensure new ones are created on the
     82   // same desktop.
     83   extensions::AppWindow* any_existing_window =
     84       extensions::AppWindowRegistry::Get(browser_context)
     85           ->GetCurrentAppWindowForApp(extension_id);
     86   chrome::HostDesktopType desktop_type;
     87   if (any_existing_window) {
     88     desktop_type = chrome::GetHostDesktopTypeForNativeWindow(
     89         any_existing_window->GetNativeWindow());
     90   } else {
     91     PerAppSettingsService* settings =
     92         PerAppSettingsServiceFactory::GetForBrowserContext(browser_context);
     93     if (settings->HasDesktopLastLaunchedFrom(extension_id)) {
     94       desktop_type = settings->GetDesktopLastLaunchedFrom(extension_id);
     95     } else {
     96       // We don't know what desktop this app was last launched from, so take our
     97       // best guess as to what desktop the user is on.
     98       desktop_type = chrome::GetActiveDesktop();
     99     }
    100   }
    101   if (desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
    102     init_params->context = ash::Shell::GetPrimaryRootWindow();
    103   else
    104     init_params->native_widget = new AppWindowDesktopNativeWidgetAuraWin(this);
    105 }
    106 
    107 void ChromeNativeAppWindowViewsWin::InitializeDefaultWindow(
    108     const extensions::AppWindow::CreateParams& create_params) {
    109   ChromeNativeAppWindowViews::InitializeDefaultWindow(create_params);
    110 
    111   // Remaining initialization is for Windows shell integration, which doesn't
    112   // apply to app windows in Ash.
    113   if (IsRunningInAsh())
    114     return;
    115 
    116   const extensions::Extension* extension = app_window()->GetExtension();
    117   if (!extension)
    118     return;
    119 
    120   std::string app_name =
    121       web_app::GenerateApplicationNameFromExtensionId(extension->id());
    122   base::string16 app_name_wide = base::UTF8ToWide(app_name);
    123   HWND hwnd = GetNativeAppWindowHWND();
    124   Profile* profile =
    125       Profile::FromBrowserContext(app_window()->browser_context());
    126   app_model_id_ =
    127       ShellIntegration::GetAppModelIdForProfile(app_name_wide,
    128                                                 profile->GetPath());
    129   ui::win::SetAppIdForWindow(app_model_id_, hwnd);
    130   web_app::UpdateRelaunchDetailsForApp(profile, extension, hwnd);
    131 
    132   if (!create_params.alpha_enabled)
    133     EnsureCaptionStyleSet();
    134   UpdateShelfMenu();
    135 }
    136 
    137 views::NonClientFrameView*
    138 ChromeNativeAppWindowViewsWin::CreateStandardDesktopAppFrame() {
    139   glass_frame_view_ = NULL;
    140   if (ui::win::IsAeroGlassEnabled()) {
    141     glass_frame_view_ = new GlassAppWindowFrameViewWin(this, widget());
    142     return glass_frame_view_;
    143   }
    144   return ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame();
    145 }
    146 
    147 void ChromeNativeAppWindowViewsWin::Show() {
    148   ActivateParentDesktopIfNecessary();
    149   ChromeNativeAppWindowViews::Show();
    150 }
    151 
    152 void ChromeNativeAppWindowViewsWin::Activate() {
    153   ActivateParentDesktopIfNecessary();
    154   ChromeNativeAppWindowViews::Activate();
    155 }
    156 
    157 void ChromeNativeAppWindowViewsWin::UpdateShelfMenu() {
    158   if (!JumpListUpdater::IsEnabled() || IsRunningInAsh())
    159     return;
    160 
    161   // Currently the only option is related to ephemeral apps, so avoid updating
    162   // the app's jump list when the feature is not enabled.
    163   if (!CommandLine::ForCurrentProcess()->HasSwitch(
    164           switches::kEnableEphemeralApps)) {
    165     return;
    166   }
    167 
    168   const extensions::Extension* extension = app_window()->GetExtension();
    169   if (!extension)
    170     return;
    171 
    172   // For the icon resources.
    173   base::FilePath chrome_path;
    174   if (!PathService::Get(base::FILE_EXE, &chrome_path))
    175     return;
    176 
    177   DCHECK(!app_model_id_.empty());
    178 
    179   JumpListUpdater jumplist_updater(app_model_id_);
    180   if (!jumplist_updater.BeginUpdate())
    181     return;
    182 
    183   // Add item to install ephemeral apps.
    184   if (extensions::util::IsEphemeralApp(extension->id(),
    185                                        app_window()->browser_context())) {
    186     scoped_refptr<ShellLinkItem> link(new ShellLinkItem());
    187     link->set_title(l10n_util::GetStringUTF16(IDS_APP_INSTALL_TITLE));
    188     link->set_icon(chrome_path.value(),
    189                    icon_resources::kInstallPackagedAppIndex);
    190     ShellIntegration::AppendProfileArgs(
    191         app_window()->browser_context()->GetPath(), link->GetCommandLine());
    192     link->GetCommandLine()->AppendSwitchASCII(
    193         switches::kInstallEphemeralAppFromWebstore, extension->id());
    194 
    195     ShellLinkItemList items;
    196     items.push_back(link);
    197     jumplist_updater.AddTasks(items);
    198   }
    199 
    200   // Note that an empty jumplist must still be committed to clear all items.
    201   jumplist_updater.CommitUpdate();
    202 }
    203