summaryrefslogtreecommitdiffstats
path: root/win8
diff options
context:
space:
mode:
authorrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-07 20:44:51 +0000
committerrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-07 20:44:51 +0000
commitbd63ce0ad16cab9822d7120bdcf2d5b7353598e3 (patch)
treeaed796bf22fb22cf0a7ff1e6c12b151de3f5bf40 /win8
parent25da1685160963a14710a128977525ea55aee091 (diff)
downloadchromium_src-bd63ce0ad16cab9822d7120bdcf2d5b7353598e3.zip
chromium_src-bd63ce0ad16cab9822d7120bdcf2d5b7353598e3.tar.gz
chromium_src-bd63ce0ad16cab9822d7120bdcf2d5b7353598e3.tar.bz2
Integrate the Windows 8 code into the Chromium tree (take 3).
This time, also: fix invalid path in metro_driver.gyp. set executable bit on check_sdk_patch.py and post_build.bat. add back a switch that was removed recently This reverts "Revert 155429 - Integrate the Windows 8 code into the Chromium tree." Original CL: https://chromiumcodereview.appspot.com/10875008 BUG=127799 TEST=A Chromium build can run as the immersive browser on Windows 8. TBR=ben Review URL: https://chromiumcodereview.appspot.com/10919158 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155474 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'win8')
-rw-r--r--win8/delegate_execute/chrome_util.cc267
-rw-r--r--win8/delegate_execute/chrome_util.h22
-rw-r--r--win8/delegate_execute/command_execute_impl.cc440
-rw-r--r--win8/delegate_execute/command_execute_impl.h105
-rw-r--r--win8/delegate_execute/command_execute_impl.rgs10
-rw-r--r--win8/delegate_execute/delegate_execute.cc120
-rw-r--r--win8/delegate_execute/delegate_execute.gyp46
-rw-r--r--win8/delegate_execute/delegate_execute.rgs3
-rw-r--r--win8/delegate_execute/delegate_execute_operation.cc37
-rw-r--r--win8/delegate_execute/delegate_execute_operation.h52
-rwxr-xr-xwin8/delegate_execute/post_build.bat3
-rw-r--r--win8/delegate_execute/resource.h22
-rw-r--r--win8/metro_driver/OWNERS5
-rw-r--r--win8/metro_driver/chrome_app_view.cc1059
-rw-r--r--win8/metro_driver/chrome_app_view.h177
-rw-r--r--win8/metro_driver/chrome_url_launch_handler.cc201
-rw-r--r--win8/metro_driver/chrome_url_launch_handler.h58
-rw-r--r--win8/metro_driver/devices_handler.cc23
-rw-r--r--win8/metro_driver/devices_handler.h31
-rw-r--r--win8/metro_driver/file_picker.cc618
-rw-r--r--win8/metro_driver/file_picker.h18
-rw-r--r--win8/metro_driver/metro_dialog_box.cc160
-rw-r--r--win8/metro_driver/metro_dialog_box.h64
-rw-r--r--win8/metro_driver/metro_driver.cc211
-rw-r--r--win8/metro_driver/metro_driver.gyp98
-rw-r--r--win8/metro_driver/metro_driver_win7.cc139
-rw-r--r--win8/metro_driver/print_document_source.cc525
-rw-r--r--win8/metro_driver/print_document_source.h164
-rw-r--r--win8/metro_driver/print_handler.cc487
-rw-r--r--win8/metro_driver/print_handler.h116
-rw-r--r--win8/metro_driver/run_all_unittests.cc19
-rw-r--r--win8/metro_driver/secondary_tile.cc155
-rw-r--r--win8/metro_driver/secondary_tile.h16
-rw-r--r--win8/metro_driver/settings_handler.cc175
-rw-r--r--win8/metro_driver/settings_handler.h44
-rw-r--r--win8/metro_driver/stdafx.h35
-rw-r--r--win8/metro_driver/toast_notification_handler.cc234
-rw-r--r--win8/metro_driver/toast_notification_handler.h48
-rw-r--r--win8/metro_driver/winrt_utils.cc225
-rw-r--r--win8/metro_driver/winrt_utils.h60
-rw-r--r--win8/metro_driver/winrt_utils_unittest.cc115
-rw-r--r--win8/win8.gyp33
42 files changed, 6440 insertions, 0 deletions
diff --git a/win8/delegate_execute/chrome_util.cc b/win8/delegate_execute/chrome_util.cc
new file mode 100644
index 0000000..701f2e8
--- /dev/null
+++ b/win8/delegate_execute/chrome_util.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2012 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 "win8/delegate_execute/chrome_util.h"
+
+#include <atlbase.h>
+#include <shlobj.h>
+#include <windows.h>
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/md5.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+#include "google_update/google_update_idl.h"
+
+namespace {
+
+#if defined(GOOGLE_CHROME_BUILD)
+const wchar_t kAppUserModelId[] = L"Chrome";
+#else // GOOGLE_CHROME_BUILD
+const wchar_t kAppUserModelId[] = L"Chromium";
+#endif // GOOGLE_CHROME_BUILD
+
+#if defined(GOOGLE_CHROME_BUILD)
+
+// TODO(grt): These constants live in installer_util. Consider moving them
+// into common_constants to allow for reuse.
+const FilePath::CharType kNewChromeExe[] = FILE_PATH_LITERAL("new_chrome.exe");
+const wchar_t kRenameCommandValue[] = L"cmd";
+const wchar_t kChromeAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+const wchar_t kRegPathChromeClient[] =
+ L"Software\\Google\\Update\\Clients\\"
+ L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+const int kExitCodeRenameSuccessful = 23;
+
+// Returns the name of the global event used to detect if |chrome_exe| is in
+// use by a browser process.
+// TODO(grt): Move this somewhere central so it can be used by both this
+// IsBrowserRunning (below) and IsBrowserAlreadyRunning (browser_util_win.cc).
+string16 GetEventName(const FilePath& chrome_exe) {
+ static wchar_t const kEventPrefix[] = L"Global\\";
+ const size_t prefix_len = arraysize(kEventPrefix) - 1;
+ string16 name;
+ name.reserve(prefix_len + chrome_exe.value().size());
+ name.assign(kEventPrefix, prefix_len);
+ name.append(chrome_exe.value());
+ std::replace(name.begin() + prefix_len, name.end(), '\\', '!');
+ std::transform(name.begin() + prefix_len, name.end(),
+ name.begin() + prefix_len, tolower);
+ return name;
+}
+
+// Returns true if |chrome_exe| is in use by a browser process. In this case,
+// "in use" means past ChromeBrowserMainParts::PreMainMessageLoopRunImpl.
+bool IsBrowserRunning(const FilePath& chrome_exe) {
+ base::win::ScopedHandle handle(::OpenEvent(
+ SYNCHRONIZE, FALSE, GetEventName(chrome_exe).c_str()));
+ if (handle.IsValid())
+ return true;
+ DWORD last_error = ::GetLastError();
+ if (last_error != ERROR_FILE_NOT_FOUND) {
+ AtlTrace("%hs. Failed to open browser event; error %u.\n", __FUNCTION__,
+ last_error);
+ }
+ return false;
+}
+
+// Returns true if the file new_chrome.exe exists in the same directory as
+// |chrome_exe|.
+bool NewChromeExeExists(const FilePath& chrome_exe) {
+ FilePath new_chrome_exe(chrome_exe.DirName().Append(kNewChromeExe));
+ return file_util::PathExists(new_chrome_exe);
+}
+
+bool GetUpdateCommand(bool is_per_user, string16* update_command) {
+ const HKEY root = is_per_user ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
+ base::win::RegKey key(root, kRegPathChromeClient, KEY_QUERY_VALUE);
+
+ return key.ReadValue(kRenameCommandValue, update_command) == ERROR_SUCCESS;
+}
+
+#endif // GOOGLE_CHROME_BUILD
+
+// TODO(grt): This code also lives in installer_util. Refactor for reuse.
+bool IsPerUserInstall(const FilePath& chrome_exe) {
+ wchar_t program_files_path[MAX_PATH] = {0};
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
+ SHGFP_TYPE_CURRENT, program_files_path))) {
+ return !StartsWith(chrome_exe.value().c_str(), program_files_path, false);
+ } else {
+ NOTREACHED();
+ }
+ return true;
+}
+
+// TODO(gab): This code also lives in shell_util. Refactor for reuse.
+string16 ByteArrayToBase32(const uint8* bytes, size_t size) {
+ static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+ // Eliminate special cases first.
+ if (size == 0) {
+ return string16();
+ } else if (size == 1) {
+ string16 ret;
+ ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]);
+ ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]);
+ return ret;
+ } else if (size >= std::numeric_limits<size_t>::max() / 8) {
+ // If |size| is too big, the calculation of |encoded_length| below will
+ // overflow.
+ AtlTrace("%hs. Byte array is too long.\n", __FUNCTION__);
+ return string16();
+ }
+
+ // Overestimate the number of bits in the string by 4 so that dividing by 5
+ // is the equivalent of rounding up the actual number of bits divided by 5.
+ const size_t encoded_length = (size * 8 + 4) / 5;
+
+ string16 ret;
+ ret.reserve(encoded_length);
+
+ // A bit stream which will be read from the left and appended to from the
+ // right as it's emptied.
+ uint16 bit_stream = (bytes[0] << 8) + bytes[1];
+ size_t next_byte_index = 2;
+ int free_bits = 0;
+ while (free_bits < 16) {
+ // Extract the 5 leftmost bits in the stream
+ ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]);
+ bit_stream <<= 5;
+ free_bits += 5;
+
+ // If there is enough room in the bit stream, inject another byte (if there
+ // are any left...).
+ if (free_bits >= 8 && next_byte_index < size) {
+ free_bits -= 8;
+ bit_stream += bytes[next_byte_index++] << free_bits;
+ }
+ }
+
+ if (ret.length() != encoded_length) {
+ AtlTrace("%hs. Encoding doesn't match expected length.\n", __FUNCTION__);
+ return string16();
+ }
+ return ret;
+}
+
+// TODO(gab): This code also lives in shell_util. Refactor for reuse.
+bool GetUserSpecificRegistrySuffix(string16* suffix) {
+ string16 user_sid;
+ if (!base::win::GetUserSidString(&user_sid)) {
+ AtlTrace("%hs. GetUserSidString failed.\n", __FUNCTION__);
+ return false;
+ }
+ COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_);
+ base::MD5Digest md5_digest;
+ std::string user_sid_ascii(UTF16ToASCII(user_sid));
+ base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest);
+ const string16 base32_md5(
+ ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a)));
+ // The value returned by the base32 algorithm above must never change and must
+ // always be 26 characters long (i.e. if someone ever moves this to base and
+ // implements the full base32 algorithm (i.e. with appended '=' signs in the
+ // output), they must provide a flag to allow this method to still request
+ // the output with no appended '=' signs).
+ if (base32_md5.length() != 26U) {
+ AtlTrace("%hs. Base32 encoding of md5 hash is incorrect.\n", __FUNCTION__);
+ return false;
+ }
+ suffix->reserve(base32_md5.length() + 1);
+ suffix->assign(1, L'.');
+ suffix->append(base32_md5);
+ return true;
+}
+
+} // namespace
+
+namespace delegate_execute {
+
+void UpdateChromeIfNeeded(const FilePath& chrome_exe) {
+#if defined(GOOGLE_CHROME_BUILD)
+ // Nothing to do if a browser is already running or if there's no
+ // new_chrome.exe.
+ if (IsBrowserRunning(chrome_exe) || !NewChromeExeExists(chrome_exe))
+ return;
+
+ base::ProcessHandle process_handle = base::kNullProcessHandle;
+
+ if (IsPerUserInstall(chrome_exe)) {
+ // Read the update command from the registry.
+ string16 update_command;
+ if (!GetUpdateCommand(true, &update_command)) {
+ AtlTrace("%hs. Failed to read update command from registry.\n",
+ __FUNCTION__);
+ } else {
+ // Run the update command.
+ base::LaunchOptions launch_options;
+ launch_options.start_hidden = true;
+ if (!base::LaunchProcess(update_command, launch_options,
+ &process_handle)) {
+ AtlTrace("%hs. Failed to launch command to finalize update; "
+ "error %u.\n", __FUNCTION__, ::GetLastError());
+ process_handle = base::kNullProcessHandle;
+ }
+ }
+ } else {
+ // Run the update command via Google Update.
+ HRESULT hr = S_OK;
+ base::win::ScopedComPtr<IProcessLauncher> process_launcher;
+ hr = process_launcher.CreateInstance(__uuidof(ProcessLauncherClass));
+ if (FAILED(hr)) {
+ AtlTrace("%hs. Failed to Create ProcessLauncher; hr=0x%X.\n",
+ __FUNCTION__, hr);
+ } else {
+ ULONG_PTR handle = 0;
+ hr = process_launcher->LaunchCmdElevated(
+ kChromeAppGuid, kRenameCommandValue, GetCurrentProcessId(), &handle);
+ if (FAILED(hr)) {
+ AtlTrace("%hs. Failed to launch command to finalize update; "
+ "hr=0x%X.\n", __FUNCTION__, hr);
+ } else {
+ process_handle = reinterpret_cast<base::ProcessHandle>(handle);
+ }
+ }
+ }
+
+ // Wait for the update to complete and report the results.
+ if (process_handle != base::kNullProcessHandle) {
+ int exit_code = 0;
+ // WaitForExitCode will close the handle in all cases.
+ if (!base::WaitForExitCode(process_handle, &exit_code)) {
+ AtlTrace("%hs. Failed to get result when finalizing update.\n",
+ __FUNCTION__);
+ } else if (exit_code != kExitCodeRenameSuccessful) {
+ AtlTrace("%hs. Failed to finalize update with exit code %d.\n",
+ __FUNCTION__, exit_code);
+ } else {
+ AtlTrace("%hs. Finalized pending update.\n", __FUNCTION__);
+ }
+ }
+#endif
+}
+
+// TODO(gab): This code also lives in shell_util. Refactor for reuse.
+string16 GetAppId(const FilePath& chrome_exe) {
+ string16 app_id(kAppUserModelId);
+ string16 suffix;
+ if (IsPerUserInstall(chrome_exe) &&
+ !GetUserSpecificRegistrySuffix(&suffix)) {
+ AtlTrace("%hs. GetUserSpecificRegistrySuffix failed.\n",
+ __FUNCTION__);
+ }
+ return app_id.append(suffix);
+}
+
+} // delegate_execute
diff --git a/win8/delegate_execute/chrome_util.h b/win8/delegate_execute/chrome_util.h
new file mode 100644
index 0000000..634ce6c
--- /dev/null
+++ b/win8/delegate_execute/chrome_util.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 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 WIN8_DELEGATE_EXECUTE_CHROME_UTIL_H_
+#define WIN8_DELEGATE_EXECUTE_CHROME_UTIL_H_
+
+#include "base/string16.h"
+
+class FilePath;
+
+namespace delegate_execute {
+
+// Finalizes a previously updated installation.
+void UpdateChromeIfNeeded(const FilePath& chrome_exe);
+
+// Returns the appid of the Chrome pointed to by |chrome_exe|.
+string16 GetAppId(const FilePath& chrome_exe);
+
+} // namespace delegate_execute
+
+#endif // WIN8_DELEGATE_EXECUTE_CHROME_UTIL_H_
diff --git a/win8/delegate_execute/command_execute_impl.cc b/win8/delegate_execute/command_execute_impl.cc
new file mode 100644
index 0000000..ef1378a
--- /dev/null
+++ b/win8/delegate_execute/command_execute_impl.cc
@@ -0,0 +1,440 @@
+// Copyright (c) 2012 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.
+// Implementation of the CommandExecuteImpl class which implements the
+// IExecuteCommand and related interfaces for handling ShellExecute based
+// launches of the Chrome browser.
+
+#include "win8/delegate_execute/command_execute_impl.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_handle.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "win8/delegate_execute/chrome_util.h"
+
+// CommandExecuteImpl is resposible for activating chrome in Windows 8. The
+// flow is complicated and this tries to highlight the important events.
+// The current approach is to have a single instance of chrome either
+// running in desktop or metro mode. If there is no current instance then
+// the desktop shortcut launches desktop chrome and the metro tile or search
+// charm launches metro chrome.
+// If chrome is running then focus/activation is given to the existing one
+// regarless of what launch point the user used.
+//
+// The general flow when chrome is the default browser is as follows:
+//
+// 1- User interacts with launch point (icon, tile, search, shellexec, etc)
+// 2- Windows finds the appid for launch item and resolves it to chrome
+// 3- Windows activates CommandExecuteImpl inside a surrogate process
+// 4- Windows calls the following sequence of entry points:
+// CommandExecuteImpl::SetShowWindow
+// CommandExecuteImpl::SetPosition
+// CommandExecuteImpl::SetDirectory
+// CommandExecuteImpl::SetParameter
+// CommandExecuteImpl::SetNoShowUI
+// CommandExecuteImpl::SetSelection
+// CommandExecuteImpl::Initialize
+// Up to this point the code basically just gathers values passed in, like
+// the launch scheme (or url) and the activation verb.
+// 5- Windows calls CommandExecuteImpl::Getvalue()
+// Here we need to return AHE_IMMERSIVE or AHE_DESKTOP. That depends on:
+// a) if run in high-integrity return AHE_DESKTOP
+// b) if chrome is running return the AHE_ mode of chrome
+// c) if the current process is inmmersive return AHE_IMMERSIVE
+// d) if the protocol is file and IExecuteCommandHost::GetUIMode() is not
+// ECHUIM_DESKTOP then return AHE_IMMERSIVE
+// e) if none of the above return AHE_DESKTOP
+// 6- If we returned AHE_DESKTOP in step 5 then CommandExecuteImpl::Execute()
+// is called, here we call GetLaunchMode() which:
+// a) if chrome is running return the mode of chrome or
+// b) return IExecuteCommandHost::GetUIMode()
+// 7- If GetLaunchMode() returns
+// a) ECHUIM_DESKTOP we call LaunchDestopChrome() that calls ::CreateProcess
+// b) else we call one of the IApplicationActivationManager activation
+// functions depending on the parameters passed in step 4.
+//
+// Some examples will help clarify the common cases.
+//
+// I - No chrome running, taskbar icon launch:
+// a) Scheme is 'file', Verb is 'open'
+// b) GetValue() returns at e) step : AHE_DESKTOP
+// c) Execute() calls LaunchDestopChrome()
+// --> desktop chrome runs
+// II- No chrome running, tile activation launch:
+// a) Scheme is 'file', Verb is 'open'
+// b) GetValue() returns at d) step : AHE_IMMERSIVE
+// c) Windows does not call us back and just activates chrome
+// --> metro chrome runs
+// III- No chrome running, url link click on metro app:
+// a) Scheme is 'http', Verb is 'open'
+// b) Getvalue() returns at e) step : AHE_DESKTOP
+// c) Execute() calls IApplicationActivationManager::ActivateForProtocol
+// --> metro chrome runs
+//
+CommandExecuteImpl::CommandExecuteImpl()
+ : integrity_level_(base::INTEGRITY_UNKNOWN),
+ launch_scheme_(INTERNET_SCHEME_DEFAULT),
+ chrome_mode_(ECHUIM_SYSTEM_LAUNCHER) {
+ memset(&start_info_, 0, sizeof(start_info_));
+ start_info_.cb = sizeof(start_info_);
+ // We need to query the user data dir of chrome so we need chrome's
+ // path provider.
+ chrome::RegisterPathProvider();
+}
+
+// CommandExecuteImpl
+STDMETHODIMP CommandExecuteImpl::SetKeyState(DWORD key_state) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::SetParameters(LPCWSTR params) {
+ AtlTrace("In %hs [%S]\n", __FUNCTION__, params);
+ if (params) {
+ parameters_ = params;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::SetPosition(POINT pt) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::SetShowWindow(int show) {
+ AtlTrace("In %hs show=%d\n", __FUNCTION__, show);
+ start_info_.wShowWindow = show;
+ start_info_.dwFlags |= STARTF_USESHOWWINDOW;
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::SetNoShowUI(BOOL no_show_ui) {
+ AtlTrace("In %hs no_show=%d\n", __FUNCTION__, no_show_ui);
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::SetDirectory(LPCWSTR directory) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::GetValue(enum AHE_TYPE* pahe) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+
+ if (!GetLaunchScheme(&display_name_, &launch_scheme_)) {
+ AtlTrace("Failed to get scheme, E_FAIL\n");
+ return E_FAIL;
+ }
+
+ if (integrity_level_ == base::HIGH_INTEGRITY) {
+ // Metro mode apps don't work in high integrity mode.
+ AtlTrace("High integrity, AHE_DESKTOP\n");
+ *pahe = AHE_DESKTOP;
+ return S_OK;
+ }
+
+ FilePath user_data_dir;
+ if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
+ AtlTrace("Failed to get chrome's data dir path, E_FAIL\n");
+ return E_FAIL;
+ }
+
+ HWND chrome_window = ::FindWindowEx(HWND_MESSAGE, NULL,
+ chrome::kMessageWindowClass,
+ user_data_dir.value().c_str());
+ if (chrome_window) {
+ AtlTrace("Found chrome window %p\n", chrome_window);
+ // The failure cases below are deemed to happen due to the inherently racy
+ // procedure of going from chrome's window to process handle during which
+ // chrome might have exited. Failing here would probably just cause the
+ // user to retry at which point we would do the right thing.
+ DWORD chrome_pid = 0;
+ ::GetWindowThreadProcessId(chrome_window, &chrome_pid);
+ if (!chrome_pid) {
+ AtlTrace("Failed to get chrome's PID, E_FAIL\n");
+ return E_FAIL;
+ }
+ base::win::ScopedHandle process(
+ ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, chrome_pid));
+ if (!process.IsValid()) {
+ AtlTrace("Failed to open chrome's process [%d], E_FAIL\n", chrome_pid);
+ return E_FAIL;
+ }
+ if (IsImmersiveProcess(process.Get())) {
+ AtlTrace("Chrome [%d] is inmmersive, AHE_IMMERSIVE\n", chrome_pid);
+ chrome_mode_ = ECHUIM_IMMERSIVE;
+ *pahe = AHE_IMMERSIVE;
+ } else {
+ AtlTrace("Chrome [%d] is Desktop, AHE_DESKTOP\n");
+ chrome_mode_ = ECHUIM_DESKTOP;
+ *pahe = AHE_DESKTOP;
+ }
+ return S_OK;
+ }
+
+ if (IsImmersiveProcess(GetCurrentProcess())) {
+ AtlTrace("Current process is inmmersive, AHE_IMMERSIVE\n");
+ *pahe = AHE_IMMERSIVE;
+ return S_OK;
+ }
+
+ if ((launch_scheme_ == INTERNET_SCHEME_FILE) &&
+ (GetLaunchMode() != ECHUIM_DESKTOP)) {
+ AtlTrace("INTERNET_SCHEME_FILE, mode != ECHUIM_DESKTOP, AHE_IMMERSIVE\n");
+ *pahe = AHE_IMMERSIVE;
+ return S_OK;
+ }
+
+ AtlTrace("Fallback is AHE_DESKTOP\n");
+ *pahe = AHE_DESKTOP;
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::Execute() {
+ AtlTrace("In %hs\n", __FUNCTION__);
+
+ if (integrity_level_ == base::HIGH_INTEGRITY)
+ return LaunchDesktopChrome();
+
+ EC_HOST_UI_MODE mode = GetLaunchMode();
+ if (mode == ECHUIM_DESKTOP)
+ return LaunchDesktopChrome();
+
+ HRESULT hr = E_FAIL;
+ CComPtr<IApplicationActivationManager> activation_manager;
+ hr = activation_manager.CoCreateInstance(CLSID_ApplicationActivationManager);
+ if (!activation_manager) {
+ AtlTrace("Failed to get the activation manager, error 0x%x\n", hr);
+ return S_OK;
+ }
+
+ string16 app_id = delegate_execute::GetAppId(chrome_exe_);
+
+ DWORD pid = 0;
+ if (launch_scheme_ == INTERNET_SCHEME_FILE) {
+ AtlTrace("Activating for file\n");
+ hr = activation_manager->ActivateApplication(app_id.c_str(),
+ verb_.c_str(),
+ AO_NOERRORUI,
+ &pid);
+ } else {
+ AtlTrace("Activating for protocol\n");
+ hr = activation_manager->ActivateForProtocol(app_id.c_str(),
+ item_array_,
+ &pid);
+ }
+ if (hr == E_APPLICATION_NOT_REGISTERED) {
+ AtlTrace("Metro chrome is not registered, launching in desktop\n");
+ return LaunchDesktopChrome();
+ }
+ AtlTrace("Metro Chrome launch, pid=%d, returned 0x%x\n", pid, hr);
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::Initialize(LPCWSTR name,
+ IPropertyBag* bag) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ if (!FindChromeExe(&chrome_exe_))
+ return E_FAIL;
+ delegate_execute::UpdateChromeIfNeeded(chrome_exe_);
+ if (name) {
+ AtlTrace("Verb is %S\n", name);
+ verb_ = name;
+ }
+
+ base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(),
+ &integrity_level_);
+ if (integrity_level_ == base::HIGH_INTEGRITY) {
+ AtlTrace("Delegate execute launched in high integrity level\n");
+ }
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::SetSelection(IShellItemArray* item_array) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ item_array_ = item_array;
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::GetSelection(REFIID riid, void** selection) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ return S_OK;
+}
+
+STDMETHODIMP CommandExecuteImpl::AllowForegroundTransfer(void* reserved) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ return S_OK;
+}
+
+// Returns false if chrome.exe cannot be found.
+// static
+bool CommandExecuteImpl::FindChromeExe(FilePath* chrome_exe) {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ // Look for chrome.exe one folder above delegate_execute.exe (as expected in
+ // Chrome installs). Failing that, look for it alonside delegate_execute.exe.
+ FilePath dir_exe;
+ if (!PathService::Get(base::DIR_EXE, &dir_exe)) {
+ AtlTrace("Failed to get current exe path\n");
+ return false;
+ }
+
+ *chrome_exe = dir_exe.DirName().Append(chrome::kBrowserProcessExecutableName);
+ if (!file_util::PathExists(*chrome_exe)) {
+ *chrome_exe = dir_exe.Append(chrome::kBrowserProcessExecutableName);
+ if (!file_util::PathExists(*chrome_exe)) {
+ AtlTrace("Failed to find chrome exe file\n");
+ return false;
+ }
+ }
+
+ AtlTrace("Got chrome exe path as %ls\n", chrome_exe->value().c_str());
+ return true;
+}
+
+bool CommandExecuteImpl::GetLaunchScheme(
+ string16* display_name, INTERNET_SCHEME* scheme) {
+ if (!item_array_)
+ return false;
+
+ ATLASSERT(display_name);
+ ATLASSERT(scheme);
+
+ DWORD count = 0;
+ item_array_->GetCount(&count);
+
+ if (count != 1) {
+ AtlTrace("Cannot handle %d elements in the IShellItemArray\n", count);
+ return false;
+ }
+
+ CComPtr<IEnumShellItems> items;
+ item_array_->EnumItems(&items);
+ CComPtr<IShellItem> shell_item;
+ HRESULT hr = items->Next(1, &shell_item, &count);
+ if (hr != S_OK) {
+ AtlTrace("Failed to read element from the IShellItemsArray\n");
+ return false;
+ }
+
+ base::win::ScopedCoMem<wchar_t> name;
+ hr = shell_item->GetDisplayName(SIGDN_URL, &name);
+ if (hr != S_OK) {
+ AtlTrace("Failed to get display name\n");
+ return false;
+ }
+
+ AtlTrace("Display name is [%ls]\n", name);
+
+ wchar_t scheme_name[16];
+ URL_COMPONENTS components = {0};
+ components.lpszScheme = scheme_name;
+ components.dwSchemeLength = sizeof(scheme_name)/sizeof(scheme_name[0]);
+
+ components.dwStructSize = sizeof(components);
+ if (!InternetCrackUrlW(name, 0, 0, &components)) {
+ AtlTrace("Failed to crack url %ls\n", name);
+ return false;
+ }
+
+ AtlTrace("Launch scheme is [%ls] (%d)\n", scheme_name, components.nScheme);
+ *display_name = name;
+ *scheme = components.nScheme;
+ return true;
+}
+
+HRESULT CommandExecuteImpl::LaunchDesktopChrome() {
+ AtlTrace("In %hs\n", __FUNCTION__);
+ string16 display_name = display_name_;
+
+ switch (launch_scheme_) {
+ case INTERNET_SCHEME_FILE:
+ // If anything other than chrome.exe is passed in the display name we
+ // should honor it. For e.g. If the user clicks on a html file when
+ // chrome is the default we should treat it as a parameter to be passed
+ // to chrome.
+ if (display_name.find(L"chrome.exe") != string16::npos)
+ display_name.clear();
+ break;
+
+ default:
+ break;
+ }
+
+ string16 command_line = L"\"";
+ command_line += chrome_exe_.value();
+ command_line += L"\"";
+
+ if (!parameters_.empty()) {
+ AtlTrace("Adding parameters %ls to command line\n", parameters_.c_str());
+ command_line += L" ";
+ command_line += parameters_.c_str();
+ }
+
+ if (!display_name.empty()) {
+ command_line += L" -- ";
+ command_line += display_name;
+ }
+
+ AtlTrace("Formatted command line is %ls\n", command_line.c_str());
+
+ PROCESS_INFORMATION proc_info = {0};
+ BOOL ret = CreateProcess(NULL, const_cast<LPWSTR>(command_line.c_str()),
+ NULL, NULL, FALSE, 0, NULL, NULL, &start_info_,
+ &proc_info);
+ if (ret) {
+ AtlTrace("Process id is %d\n", proc_info.dwProcessId);
+ CloseHandle(proc_info.hProcess);
+ CloseHandle(proc_info.hThread);
+ } else {
+ AtlTrace("Process launch failed, error %d\n", ::GetLastError());
+ }
+
+ return S_OK;
+}
+
+EC_HOST_UI_MODE CommandExecuteImpl::GetLaunchMode() {
+ // We are to return chrome's mode if chrome exists else we query our embedder
+ // IServiceProvider and learn the mode from them.
+ static bool launch_mode_determined = false;
+ static EC_HOST_UI_MODE launch_mode = ECHUIM_DESKTOP;
+
+ const char* modes[] = { "Desktop", "Inmmersive", "SysLauncher", "??" };
+
+ if (launch_mode_determined)
+ return launch_mode;
+
+ if (chrome_mode_ != ECHUIM_SYSTEM_LAUNCHER) {
+ launch_mode = chrome_mode_;
+ AtlTrace("Launch mode is that of chrome, %s\n", modes[launch_mode]);
+ launch_mode_determined = true;
+ return launch_mode;
+ }
+
+ if (parameters_ == ASCIIToWide(switches::kForceImmersive)) {
+ launch_mode = ECHUIM_IMMERSIVE;
+ AtlTrace("Launch mode forced to %s\n", modes[launch_mode]);
+ launch_mode_determined = true;
+ return launch_mode;
+ }
+
+ CComPtr<IExecuteCommandHost> host;
+ CComQIPtr<IServiceProvider> service_provider = m_spUnkSite;
+ if (service_provider) {
+ service_provider->QueryService(IID_IExecuteCommandHost, &host);
+ if (host) {
+ host->GetUIMode(&launch_mode);
+ } else {
+ AtlTrace("Failed to get IID_IExecuteCommandHost. Assuming desktop\n");
+ }
+ } else {
+ AtlTrace("Failed to get IServiceProvider. Assuming desktop mode\n");
+ }
+ AtlTrace("Launch mode is %s\n", modes[launch_mode]);
+ launch_mode_determined = true;
+ return launch_mode;
+}
diff --git a/win8/delegate_execute/command_execute_impl.h b/win8/delegate_execute/command_execute_impl.h
new file mode 100644
index 0000000..5a1a95c
--- /dev/null
+++ b/win8/delegate_execute/command_execute_impl.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 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 <atlbase.h>
+#include <atlcom.h>
+#include <atlctl.h>
+#include <ShObjIdl.h>
+#include <WinInet.h>
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/process_util.h"
+#include "win8/delegate_execute/resource.h" // main symbols
+
+using namespace ATL;
+
+EXTERN_C const GUID CLSID_CommandExecuteImpl;
+
+// CommandExecuteImpl
+// This class implements the IExecuteCommand and related interfaces for
+// handling ShellExecute launches of the Chrome browser, i.e. whether to
+// launch Chrome in metro mode or desktop mode.
+#if defined(GOOGLE_CHROME_BUILD)
+class ATL_NO_VTABLE DECLSPEC_UUID("5C65F4B0-3651-4514-B207-D10CB699B14B")
+ CommandExecuteImpl
+#else // GOOGLE_CHROME_BUILD
+class ATL_NO_VTABLE DECLSPEC_UUID("A2DF06F9-A21A-44A8-8A99-8B9C84F29160")
+ CommandExecuteImpl
+#endif // GOOGLE_CHROME_BUILD
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public CComCoClass<CommandExecuteImpl, &CLSID_CommandExecuteImpl>,
+ public IExecuteCommand,
+ public IObjectWithSiteImpl<CommandExecuteImpl>,
+ public IInitializeCommand,
+ public IObjectWithSelection,
+ public IExecuteCommandApplicationHostEnvironment,
+ public IForegroundTransfer {
+ public:
+ CommandExecuteImpl();
+
+ DECLARE_REGISTRY_RESOURCEID(IDR_COMMANDEXECUTEIMPL)
+
+ BEGIN_COM_MAP(CommandExecuteImpl)
+ COM_INTERFACE_ENTRY(IExecuteCommand)
+ COM_INTERFACE_ENTRY(IObjectWithSite)
+ COM_INTERFACE_ENTRY(IInitializeCommand)
+ COM_INTERFACE_ENTRY(IObjectWithSelection)
+ COM_INTERFACE_ENTRY(IExecuteCommandApplicationHostEnvironment)
+ COM_INTERFACE_ENTRY(IForegroundTransfer)
+ END_COM_MAP()
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ HRESULT FinalConstruct() {
+ return S_OK;
+ }
+
+ void FinalRelease() {
+ }
+
+ public:
+ // IExecuteCommand
+ STDMETHOD(SetKeyState)(DWORD key_state);
+ STDMETHOD(SetParameters)(LPCWSTR params);
+ STDMETHOD(SetPosition)(POINT pt);
+ STDMETHOD(SetShowWindow)(int show);
+ STDMETHOD(SetNoShowUI)(BOOL no_show_ui);
+ STDMETHOD(SetDirectory)(LPCWSTR directory);
+ STDMETHOD(Execute)(void);
+
+ // IInitializeCommand
+ STDMETHOD(Initialize)(LPCWSTR name, IPropertyBag* bag);
+
+ // IObjectWithSelection
+ STDMETHOD(SetSelection)(IShellItemArray* item_array);
+ STDMETHOD(GetSelection)(REFIID riid, void** selection);
+
+ // IExecuteCommandApplicationHostEnvironment
+ STDMETHOD(GetValue)(enum AHE_TYPE* pahe);
+
+ // IForegroundTransfer
+ STDMETHOD(AllowForegroundTransfer)(void* reserved);
+
+ private:
+ static bool FindChromeExe(FilePath* chrome_exe);
+ bool GetLaunchScheme(string16* display_name, INTERNET_SCHEME* scheme);
+ HRESULT LaunchDesktopChrome();
+ // Returns the launch mode, i.e. desktop launch/metro launch, etc.
+ EC_HOST_UI_MODE GetLaunchMode();
+
+ CComPtr<IShellItemArray> item_array_;
+ string16 parameters_;
+ FilePath chrome_exe_;
+ STARTUPINFO start_info_;
+ string16 verb_;
+ string16 display_name_;
+ INTERNET_SCHEME launch_scheme_;
+
+ base::IntegrityLevel integrity_level_;
+ EC_HOST_UI_MODE chrome_mode_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(CommandExecuteImpl), CommandExecuteImpl)
diff --git a/win8/delegate_execute/command_execute_impl.rgs b/win8/delegate_execute/command_execute_impl.rgs
new file mode 100644
index 0000000..4f1aba3
--- /dev/null
+++ b/win8/delegate_execute/command_execute_impl.rgs
@@ -0,0 +1,10 @@
+HKCR {
+ NoRemove CLSID {
+ ForceRemove '%DELEGATE_EXECUTE_CLSID%' = s 'CommandExecuteImpl Class' {
+ ForceRemove Programmable
+ LocalServer32 = s '%MODULE%' {
+ val ServerExecutable = s '%MODULE_RAW%'
+ }
+ }
+ }
+}
diff --git a/win8/delegate_execute/delegate_execute.cc b/win8/delegate_execute/delegate_execute.cc
new file mode 100644
index 0000000..9f3430c
--- /dev/null
+++ b/win8/delegate_execute/delegate_execute.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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 <atlbase.h>
+#include <atlcom.h>
+#include <atlctl.h>
+#include <initguid.h>
+#include <shellapi.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/string16.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_handle.h"
+#include "chrome/common/chrome_switches.h"
+#include "command_execute_impl.h"
+#include "win8/delegate_execute/delegate_execute_operation.h"
+#include "win8/delegate_execute/resource.h"
+
+using namespace ATL;
+
+class DelegateExecuteModule
+ : public ATL::CAtlExeModuleT< DelegateExecuteModule > {
+ public :
+ typedef ATL::CAtlExeModuleT<DelegateExecuteModule> ParentClass;
+
+ DECLARE_REGISTRY_APPID_RESOURCEID(IDR_DELEGATEEXECUTE,
+ "{B1935DA1-112F-479A-975B-AB8588ABA636}")
+
+ virtual HRESULT AddCommonRGSReplacements(IRegistrarBase* registrar) throw() {
+ AtlTrace(L"In %hs\n", __FUNCTION__);
+ HRESULT hr = ParentClass::AddCommonRGSReplacements(registrar);
+ if (FAILED(hr))
+ return hr;
+
+ wchar_t delegate_execute_clsid[MAX_PATH] = {0};
+ if (!StringFromGUID2(__uuidof(CommandExecuteImpl), delegate_execute_clsid,
+ ARRAYSIZE(delegate_execute_clsid))) {
+ ATLASSERT(false);
+ return E_FAIL;
+ }
+
+ hr = registrar->AddReplacement(L"DELEGATE_EXECUTE_CLSID",
+ delegate_execute_clsid);
+ ATLASSERT(SUCCEEDED(hr));
+ return hr;
+ }
+};
+
+DelegateExecuteModule _AtlModule;
+
+// Relaunch metro Chrome by ShellExecute on |shortcut| with --force-immersive.
+// |handle_value| is an optional handle on which this function will wait before
+// performing the relaunch.
+int RelaunchChrome(const FilePath& shortcut, const string16& handle_value) {
+ base::win::ScopedHandle handle;
+
+ if (!handle_value.empty()) {
+ uint32 the_handle = 0;
+ if (!base::StringToUint(handle_value, &the_handle)) {
+ // Failed to parse the handle value. Skip the wait but proceed with the
+ // relaunch.
+ AtlTrace(L"Failed to parse handle value %ls\n", handle_value.c_str());
+ } else {
+ handle.Set(reinterpret_cast<HANDLE>(the_handle));
+ }
+ }
+
+ if (handle.IsValid()) {
+ AtlTrace(L"Waiting for chrome.exe to exit.\n");
+ DWORD result = ::WaitForSingleObject(handle, 10 * 1000);
+ AtlTrace(L"And we're back.\n");
+ if (result != WAIT_OBJECT_0) {
+ AtlTrace(L"Failed to wait for parent to exit; result=%u.\n", result);
+ // This could mean that Chrome has hung. Conservatively proceed with
+ // the relaunch anyway.
+ }
+ handle.Close();
+ }
+
+ base::win::ScopedCOMInitializer com_initializer;
+
+ AtlTrace(L"Launching Chrome via %ls.\n", shortcut.value().c_str());
+ int ser = reinterpret_cast<int>(
+ ::ShellExecute(NULL, NULL, shortcut.value().c_str(),
+ ASCIIToWide(switches::kForceImmersive).c_str(), NULL,
+ SW_SHOWNORMAL));
+ AtlTrace(L"ShellExecute returned %d.\n", ser);
+ return ser <= 32;
+}
+
+//
+extern "C" int WINAPI _tWinMain(HINSTANCE , HINSTANCE,
+ LPTSTR, int nShowCmd) {
+ using delegate_execute::DelegateExecuteOperation;
+ base::AtExitManager exit_manager;
+
+ CommandLine::Init(0, NULL);
+ CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ DelegateExecuteOperation operation;
+
+ operation.Initialize(cmd_line);
+ switch (operation.operation_type()) {
+ case DelegateExecuteOperation::EXE_MODULE:
+ return _AtlModule.WinMain(nShowCmd);
+
+ case DelegateExecuteOperation::RELAUNCH_CHROME:
+ return RelaunchChrome(operation.relaunch_shortcut(),
+ cmd_line->GetSwitchValueNative(switches::kWaitForHandle));
+
+ default:
+ NOTREACHED();
+ }
+
+ return 1;
+}
diff --git a/win8/delegate_execute/delegate_execute.gyp b/win8/delegate_execute/delegate_execute.gyp
new file mode 100644
index 0000000..e4feff4
--- /dev/null
+++ b/win8/delegate_execute/delegate_execute.gyp
@@ -0,0 +1,46 @@
+# Copyright (c) 2012 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.
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../../build/win_precompile.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'delegate_execute',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../chrome/chrome.gyp:installer_util',
+ '../../google_update/google_update.gyp:google_update',
+ '../../win8/win8.gyp:check_sdk_patch',
+ ],
+ 'sources': [
+ 'chrome_util.cc',
+ 'chrome_util.h',
+ 'command_execute_impl.cc',
+ 'command_execute_impl.h',
+ 'command_execute_impl.rgs',
+ 'delegate_execute.cc',
+ 'delegate_execute.rc',
+ 'delegate_execute.rgs',
+ 'delegate_execute_operation.cc',
+ 'delegate_execute_operation.h',
+ 'resource.h',
+ ],
+ 'defines': [
+ # This define is required to pull in the new Win8 interfaces from
+ # system headers like ShObjIdl.h
+ 'NTDDI_VERSION=0x06020000',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ },
+ },
+ },
+ ],
+}
diff --git a/win8/delegate_execute/delegate_execute.rgs b/win8/delegate_execute/delegate_execute.rgs
new file mode 100644
index 0000000..e7d3740
--- /dev/null
+++ b/win8/delegate_execute/delegate_execute.rgs
@@ -0,0 +1,3 @@
+HKCR
+{
+}
diff --git a/win8/delegate_execute/delegate_execute_operation.cc b/win8/delegate_execute/delegate_execute_operation.cc
new file mode 100644
index 0000000..e6823f0
--- /dev/null
+++ b/win8/delegate_execute/delegate_execute_operation.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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 "win8/delegate_execute/delegate_execute_operation.h"
+
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace delegate_execute {
+
+DelegateExecuteOperation::DelegateExecuteOperation()
+ : operation_type_(EXE_MODULE) {
+}
+
+DelegateExecuteOperation::~DelegateExecuteOperation() {
+ Clear();
+}
+
+void DelegateExecuteOperation::Initialize(const CommandLine* command_line) {
+ Clear();
+
+ // --relaunch-shortcut=PathToShortcut triggers the relaunch Chrome operation.
+ FilePath shortcut(
+ command_line->GetSwitchValuePath(switches::kRelaunchShortcut));
+ if (!shortcut.empty()) {
+ relaunch_shortcut_ = shortcut;
+ operation_type_ = RELAUNCH_CHROME;
+ }
+}
+
+void DelegateExecuteOperation::Clear() {
+ operation_type_ = EXE_MODULE;
+ relaunch_shortcut_.clear();
+}
+
+} // namespace delegate_execute
diff --git a/win8/delegate_execute/delegate_execute_operation.h b/win8/delegate_execute/delegate_execute_operation.h
new file mode 100644
index 0000000..59acfd7a
--- /dev/null
+++ b/win8/delegate_execute/delegate_execute_operation.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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 WIN8_DELEGATE_EXECUTE_DELEGATE_EXECUTE_OPERATION_H_
+#define WIN8_DELEGATE_EXECUTE_DELEGATE_EXECUTE_OPERATION_H_
+
+#include <atldef.h>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+
+class CommandLine;
+
+namespace delegate_execute {
+
+// Parses a portion of the DelegateExecute handler's command line to determine
+// the desired operation.
+class DelegateExecuteOperation {
+ public:
+ enum OperationType {
+ EXE_MODULE,
+ RELAUNCH_CHROME,
+ };
+
+ DelegateExecuteOperation();
+ ~DelegateExecuteOperation();
+
+ void Initialize(const CommandLine* command_line);
+
+ OperationType operation_type() const {
+ return operation_type_;
+ }
+
+ // Returns the argument to the --relaunch-shortcut switch. Valid only when
+ // the operation is RELAUNCH_CHROME.
+ const FilePath& relaunch_shortcut() const {
+ ATLASSERT(operation_type_ == RELAUNCH_CHROME);
+ return relaunch_shortcut_;
+ }
+
+ private:
+ void Clear();
+
+ OperationType operation_type_;
+ FilePath relaunch_shortcut_;
+ DISALLOW_COPY_AND_ASSIGN(DelegateExecuteOperation);
+};
+
+} // namespace delegate_execute
+
+#endif // WIN8_DELEGATE_EXECUTE_DELEGATE_EXECUTE_OPERATION_H_
diff --git a/win8/delegate_execute/post_build.bat b/win8/delegate_execute/post_build.bat
new file mode 100755
index 0000000..8aa9a76
--- /dev/null
+++ b/win8/delegate_execute/post_build.bat
@@ -0,0 +1,3 @@
+REM invoke as: post_build.bat OUTPUT_PATH_TO_EXE PATH_TO_CHROME_ROOT CONFIGURATION
+mkdir %2\build\%3
+copy %1 %2\build\%3\
diff --git a/win8/delegate_execute/resource.h b/win8/delegate_execute/resource.h
new file mode 100644
index 0000000..cb81bf0
--- /dev/null
+++ b/win8/delegate_execute/resource.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 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.
+
+// {{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by DelegateExecute.rc
+//
+#define IDS_PROJNAME 100
+#define IDR_DELEGATEEXECUTE 101
+#define IDR_COMMANDEXECUTEIMPL 106
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 201
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 201
+#define _APS_NEXT_SYMED_VALUE 107
+#endif
+#endif
diff --git a/win8/metro_driver/OWNERS b/win8/metro_driver/OWNERS
new file mode 100644
index 0000000..3802937
--- /dev/null
+++ b/win8/metro_driver/OWNERS
@@ -0,0 +1,5 @@
+ananta@chromium.org
+cpu@chromium.org
+grt@chromium.org
+mad@chromium.org
+robertshield@chromium.org \ No newline at end of file
diff --git a/win8/metro_driver/chrome_app_view.cc b/win8/metro_driver/chrome_app_view.cc
new file mode 100644
index 0000000..7d5580e
--- /dev/null
+++ b/win8/metro_driver/chrome_app_view.cc
@@ -0,0 +1,1059 @@
+// Copyright (c) 2012 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 "win8/metro_driver/stdafx.h"
+#include "win8/metro_driver/chrome_app_view.h"
+
+#include <algorithm>
+#include <windows.applicationModel.datatransfer.h>
+#include <windows.foundation.h>
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/win/metro.h"
+
+// This include allows to send WM_SYSCOMMANDs to chrome.
+#include "chrome/app/chrome_command_ids.h"
+#include "win8/metro_driver/winrt_utils.h"
+#include "ui/base/ui_base_switches.h"
+
+typedef winfoundtn::ITypedEventHandler<
+ winapp::Core::CoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Input::EdgeGesture*,
+ winui::Input::EdgeGestureEventArgs*> EdgeEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winapp::DataTransfer::DataTransferManager*,
+ winapp::DataTransfer::DataRequestedEventArgs*> ShareDataRequestedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::ViewManagement::InputPane*,
+ winui::ViewManagement::InputPaneVisibilityEventArgs*>
+ InputPaneEventHandler;
+
+struct Globals globals;
+
+// TODO(ananta)
+// Remove this once we consolidate metro driver with chrome.
+const wchar_t kMetroGetCurrentTabInfoMessage[] =
+ L"CHROME_METRO_GET_CURRENT_TAB_INFO";
+
+static const int kFlipWindowsHotKeyId = 0x0000baba;
+
+static const int kAnimateWindowTimeoutMs = 200;
+
+static const int kCheckOSKDelayMs = 300;
+
+const wchar_t kOSKClassName[] = L"IPTip_Main_Window";
+
+static const int kOSKAdjustmentOffset = 20;
+
+namespace {
+
+void AdjustToFitWindow(HWND hwnd, int flags) {
+ RECT rect = {0};
+ ::GetWindowRect(globals.core_window, &rect);
+ int cx = rect.right - rect.left;
+ int cy = rect.bottom - rect.top;
+
+ ::SetWindowPos(hwnd, HWND_TOP,
+ rect.left, rect.top, cx, cy,
+ SWP_NOZORDER | flags);
+}
+
+void AdjustFrameWindowStyleForMetro(HWND hwnd) {
+ DVLOG(1) << __FUNCTION__;
+ // Ajust the frame so the live preview works and the frame buttons dissapear.
+ ::SetWindowLong(hwnd, GWL_STYLE,
+ WS_POPUP | (::GetWindowLong(hwnd, GWL_STYLE) &
+ ~(WS_MAXIMIZE | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU)));
+ ::SetWindowLong(hwnd, GWL_EXSTYLE,
+ ::GetWindowLong(hwnd, GWL_EXSTYLE) & ~(WS_EX_DLGMODALFRAME |
+ WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+ AdjustToFitWindow(hwnd, SWP_FRAMECHANGED | SWP_NOACTIVATE);
+}
+
+void SetFrameWindowInternal(HWND hwnd) {
+ DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd);
+
+ HWND current_top_frame =
+ !globals.host_windows.empty() ? globals.host_windows.front().first : NULL;
+ if (hwnd != current_top_frame && IsWindow(current_top_frame)) {
+ DVLOG(1) << "Hiding current top window, hwnd="
+ << LONG_PTR(current_top_frame);
+ ::ShowWindow(current_top_frame, SW_HIDE);
+ }
+
+ // If chrome opens a url in a foreground tab, it may call SetFrameWindow
+ // again. Ensure that we don't have dups.
+ globals.host_windows.remove_if([hwnd](std::pair<HWND, bool>& item) {
+ return (item.first == hwnd);
+ });
+
+ globals.host_windows.push_front(std::make_pair(hwnd, false));
+
+ AdjustFrameWindowStyleForMetro(hwnd);
+}
+
+void CloseFrameWindowInternal(HWND hwnd) {
+ DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd);
+
+ globals.host_windows.remove_if([hwnd](std::pair<HWND, bool>& item) {
+ return (item.first == hwnd);
+ });
+
+ if (globals.host_windows.size() > 0) {
+ DVLOG(1) << "Making new top frame window visible:"
+ << reinterpret_cast<int>(globals.host_windows.front().first);
+ AdjustToFitWindow(globals.host_windows.front().first, SWP_SHOWWINDOW);
+ } else {
+ // time to quit
+ DVLOG(1) << "Last host window closed. Calling Exit().";
+ globals.app_exit->Exit();
+ }
+}
+
+void FlipFrameWindowsInternal() {
+ DVLOG(1) << __FUNCTION__;
+ // Get the first window in the frame windows queue and push it to the end.
+ // Metroize the next window in the queue.
+ if (globals.host_windows.size() > 1) {
+ std::pair<HWND, bool> current_top_window = globals.host_windows.front();
+ globals.host_windows.pop_front();
+
+ DVLOG(1) << "Making new top frame window visible:"
+ << reinterpret_cast<int>(globals.host_windows.front().first);
+
+ AdjustToFitWindow(globals.host_windows.front().first, SWP_SHOWWINDOW);
+
+ DVLOG(1) << "Hiding current top window:"
+ << reinterpret_cast<int>(current_top_window.first);
+ AnimateWindow(current_top_window.first, kAnimateWindowTimeoutMs,
+ AW_HIDE | AW_HOR_POSITIVE | AW_SLIDE);
+
+ globals.host_windows.push_back(current_top_window);
+ }
+}
+
+} // namespace
+
+HRESULT ChromeAppView::TileRequestCreateDone(
+ winfoundtn::IAsyncOperation<bool>* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ unsigned char result;
+ CheckHR(async->GetResults(&result));
+ DVLOG(1) << __FUNCTION__ << " result " << static_cast<int>(result);
+ } else {
+ LOG(ERROR) << __FUNCTION__ << " Unexpected async status " << status;
+ }
+
+ return S_OK;
+}
+
+void ChromeAppView::DisplayNotification(
+ const ToastNotificationHandler::DesktopNotification& notification) {
+ DVLOG(1) << __FUNCTION__;
+
+ if (IsValidNotification(notification.id)) {
+ NOTREACHED() << "Duplicate notification id passed in.";
+ return;
+ }
+
+ base::AutoLock lock(notification_lock_);
+
+ ToastNotificationHandler* notification_handler =
+ new ToastNotificationHandler;
+
+ notification_map_[notification.id].reset(notification_handler);
+ notification_handler->DisplayNotification(notification);
+}
+
+void ChromeAppView::CancelNotification(const std::string& notification) {
+ DVLOG(1) << __FUNCTION__;
+
+ base::AutoLock lock(notification_lock_);
+
+ NotificationMap::iterator index = notification_map_.find(notification);
+ if (index == notification_map_.end()) {
+ NOTREACHED() << "Invalid notification:" << notification.c_str();
+ return;
+ }
+
+ scoped_ptr<ToastNotificationHandler> notification_handler(
+ index->second.release());
+
+ notification_map_.erase(index);
+
+ notification_handler->CancelNotification();
+}
+
+// Returns true if the notification passed in is valid.
+bool ChromeAppView::IsValidNotification(const std::string& notification) {
+ DVLOG(1) << __FUNCTION__;
+
+ base::AutoLock lock(notification_lock_);
+ return notification_map_.find(notification) != notification_map_.end();
+}
+
+void ChromeAppView::ShowDialogBox(
+ const MetroDialogBox::DialogBoxInfo& dialog_box_info) {
+ VLOG(1) << __FUNCTION__;
+ dialog_box_.Show(dialog_box_info);
+}
+
+void ChromeAppView::DismissDialogBox() {
+ VLOG(1) << __FUNCTION__;
+ dialog_box_.Dismiss();
+}
+
+// static
+HRESULT ChromeAppView::Unsnap() {
+ mswr::ComPtr<winui::ViewManagement::IApplicationViewStatics> view_statics;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_ViewManagement_ApplicationView,
+ view_statics.GetAddressOf());
+ CheckHR(hr);
+
+ winui::ViewManagement::ApplicationViewState state =
+ winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
+ hr = view_statics->get_Value(&state);
+ CheckHR(hr);
+
+ if (state == winui::ViewManagement::ApplicationViewState_Snapped) {
+ boolean success = FALSE;
+ hr = view_statics->TryUnsnap(&success);
+
+ if (FAILED(hr) || !success) {
+ LOG(ERROR) << "Failed to unsnap. Error 0x" << hr;
+ if (SUCCEEDED(hr))
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+}
+
+void ChromeAppView::SetFullscreen(bool fullscreen) {
+ VLOG(1) << __FUNCTION__;
+
+ if (osk_offset_adjustment_) {
+ VLOG(1) << "Scrolling the window down by: "
+ << osk_offset_adjustment_;
+
+ ::ScrollWindowEx(globals.host_windows.front().first,
+ 0,
+ osk_offset_adjustment_,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SW_INVALIDATE | SW_SCROLLCHILDREN);
+ osk_offset_adjustment_ = 0;
+ }
+}
+
+void UnsnapHelper() {
+ ChromeAppView::Unsnap();
+}
+
+extern "C" __declspec(dllexport)
+void MetroUnsnap() {
+ DVLOG(1) << __FUNCTION__;
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&UnsnapHelper));
+}
+
+extern "C" __declspec(dllexport)
+HWND GetRootWindow() {
+ DVLOG(1) << __FUNCTION__;
+ return globals.core_window;
+}
+
+extern "C" __declspec(dllexport)
+void SetFrameWindow(HWND hwnd) {
+ DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd);
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&SetFrameWindowInternal, hwnd));
+}
+
+// TODO(ananta)
+// Handle frame window close by deleting it from the window list and making the
+// next guy visible.
+extern "C" __declspec(dllexport)
+ void CloseFrameWindow(HWND hwnd) {
+ DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd);
+
+ // This is a hack to ensure that the BrowserViewLayout code layout happens
+ // just at the right time to hide the switcher button if it is visible.
+ globals.appview_msg_loop->PostDelayedTask(
+ FROM_HERE, base::Bind(&CloseFrameWindowInternal, hwnd),
+ base::TimeDelta::FromMilliseconds(50));
+}
+
+// Returns the initial url. This returns a valid url only if we were launched
+// into metro via a url navigation.
+extern "C" __declspec(dllexport)
+const wchar_t* GetInitialUrl() {
+ DVLOG(1) << __FUNCTION__;
+ bool was_initial_activation = globals.is_initial_activation;
+ globals.is_initial_activation = false;
+ if (!was_initial_activation || globals.navigation_url.empty())
+ return L"";
+
+ const wchar_t* initial_url = globals.navigation_url.c_str();
+ DVLOG(1) << initial_url;
+ return initial_url;
+}
+
+// Returns the initial search string. This returns a valid url only if we were
+// launched into metro via the search charm
+extern "C" __declspec(dllexport)
+const wchar_t* GetInitialSearchString() {
+ DVLOG(1) << __FUNCTION__;
+ bool was_initial_activation = globals.is_initial_activation;
+ globals.is_initial_activation = false;
+ if (!was_initial_activation || globals.search_string.empty())
+ return L"";
+
+ const wchar_t* initial_search_string = globals.search_string.c_str();
+ DVLOG(1) << initial_search_string;
+ return initial_search_string;
+}
+
+// Returns the launch type.
+extern "C" __declspec(dllexport)
+base::win::MetroLaunchType GetLaunchType(
+ base::win::MetroPreviousExecutionState* previous_state) {
+ if (previous_state) {
+ *previous_state = static_cast<base::win::MetroPreviousExecutionState>(
+ globals.previous_state);
+ }
+ return static_cast<base::win::MetroLaunchType>(
+ globals.initial_activation_kind);
+}
+
+extern "C" __declspec(dllexport)
+void FlipFrameWindows() {
+ DVLOG(1) << __FUNCTION__;
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&FlipFrameWindowsInternal));
+}
+
+extern "C" __declspec(dllexport)
+void DisplayNotification(const char* origin_url, const char* icon_url,
+ const wchar_t* title, const wchar_t* body,
+ const wchar_t* display_source,
+ const char* notification_id) {
+ // TODO(ananta)
+ // Needs implementation.
+ DVLOG(1) << __FUNCTION__;
+
+ ToastNotificationHandler::DesktopNotification notification(origin_url,
+ icon_url,
+ title,
+ body,
+ display_source,
+ notification_id);
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppView::DisplayNotification,
+ globals.view, notification));
+}
+
+extern "C" __declspec(dllexport)
+bool CancelNotification(const char* notification_id) {
+ // TODO(ananta)
+ // Needs implementation.
+ DVLOG(1) << __FUNCTION__;
+
+ if (!globals.view->IsValidNotification(notification_id)) {
+ NOTREACHED() << "Invalid notification id :" << notification_id;
+ return false;
+ }
+
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppView::CancelNotification,
+ globals.view, std::string(notification_id)));
+ return true;
+}
+
+// Returns command line switches if any to be used by metro chrome.
+extern "C" __declspec(dllexport)
+const wchar_t* GetMetroCommandLineSwitches() {
+ DVLOG(1) << __FUNCTION__;
+ // The metro_command_line_switches field should be filled up once.
+ // ideally in ChromeAppView::Activate.
+ return globals.metro_command_line_switches.c_str();
+}
+
+// Provides functionality to display a metro style dialog box with two buttons.
+// Only one dialog box can be displayed at any given time.
+extern "C" __declspec(dllexport)
+void ShowDialogBox(
+ const wchar_t* title,
+ const wchar_t* content,
+ const wchar_t* button1_label,
+ const wchar_t* button2_label,
+ base::win::MetroDialogButtonPressedHandler button1_handler,
+ base::win::MetroDialogButtonPressedHandler button2_handler) {
+ VLOG(1) << __FUNCTION__;
+
+ DCHECK(title);
+ DCHECK(content);
+ DCHECK(button1_label);
+ DCHECK(button2_label);
+ DCHECK(button1_handler);
+ DCHECK(button2_handler);
+
+ MetroDialogBox::DialogBoxInfo dialog_box_info;
+ dialog_box_info.title = title;
+ dialog_box_info.content = content;
+ dialog_box_info.button1_label = button1_label;
+ dialog_box_info.button2_label = button2_label;
+ dialog_box_info.button1_handler = button1_handler;
+ dialog_box_info.button2_handler = button2_handler;
+
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(
+ &ChromeAppView::ShowDialogBox, globals.view, dialog_box_info));
+}
+
+// Provides functionality to dismiss the previously displayed metro style
+// dialog box.
+extern "C" __declspec(dllexport)
+void DismissDialogBox() {
+ VLOG(1) << __FUNCTION__;
+
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(
+ &ChromeAppView::DismissDialogBox,
+ globals.view));
+}
+
+extern "C" __declspec(dllexport)
+void SetFullscreen(bool fullscreen) {
+ VLOG(1) << __FUNCTION__;
+
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(
+ &ChromeAppView::SetFullscreen,
+ globals.view, fullscreen));
+}
+
+BOOL CALLBACK CoreWindowFinder(HWND hwnd, LPARAM) {
+ char classname[128];
+ if (::GetClassNameA(hwnd, classname, ARRAYSIZE(classname))) {
+ if (lstrcmpiA("Windows.UI.Core.CoreWindow", classname) == 0) {
+ globals.core_window = hwnd;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+template <typename ContainerT>
+void CloseSecondaryWindows(ContainerT& windows) {
+ DVLOG(1) << "Closing secondary windows", windows.size();
+ std::for_each(windows.begin(), windows.end(), [](HWND hwnd) {
+ ::PostMessageW(hwnd, WM_CLOSE, 0, 0);
+ });
+ windows.clear();
+}
+
+void EndChromeSession() {
+ DVLOG(1) << "Sending chrome WM_ENDSESSION window message.";
+ ::SendMessage(globals.host_windows.front().first, WM_ENDSESSION, FALSE,
+ ENDSESSION_CLOSEAPP);
+}
+
+DWORD WINAPI HostMainThreadProc(void*) {
+ // Test feature - devs have requested the ability to easily add metro-chrome
+ // command line arguments. This is hard since shortcut arguments are ignored,
+ // by Metro, so we instead read them directly from the pinned taskbar
+ // shortcut. This may call Coinitialize and there is tell of badness
+ // occurring if CoInitialize is called on a metro thread.
+ globals.metro_command_line_switches =
+ winrt_utils::ReadArgumentsFromPinnedTaskbarShortcut();
+
+ globals.g_core_proc = reinterpret_cast<WNDPROC>(
+ ::SetWindowLong(globals.core_window, GWL_WNDPROC,
+ reinterpret_cast<long>(ChromeAppView::CoreWindowProc)));
+ DWORD exit_code = globals.host_main(globals.host_context);
+ DVLOG(1) << "host thread done, exit_code=" << exit_code;
+ globals.app_exit->Exit();
+ return exit_code;
+}
+
+ChromeAppView::ChromeAppView()
+ : osk_visible_notification_received_(false),
+ osk_offset_adjustment_(0) {
+ globals.previous_state =
+ winapp::Activation::ApplicationExecutionState_NotRunning;
+}
+
+ChromeAppView::~ChromeAppView() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+IFACEMETHODIMP
+ChromeAppView::Initialize(winapp::Core::ICoreApplicationView* view) {
+ view_ = view;
+ DVLOG(1) << __FUNCTION__;
+ globals.main_thread_id = ::GetCurrentThreadId();
+
+ HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
+ this, &ChromeAppView::OnActivate).Get(),
+ &activated_token_);
+ CheckHR(hr);
+ return hr;
+}
+
+IFACEMETHODIMP
+ChromeAppView::SetWindow(winui::Core::ICoreWindow* window) {
+ window_ = window;
+ DVLOG(1) << __FUNCTION__;
+
+ HRESULT hr = url_launch_handler_.Initialize();
+ CheckHR(hr, "Failed to initialize url launch handler.");
+
+ // Register for size notifications.
+ hr = window_->add_SizeChanged(mswr::Callback<SizeChangedHandler>(
+ this, &ChromeAppView::OnSizeChanged).Get(),
+ &sizechange_token_);
+ CheckHR(hr);
+
+ // Register for edge gesture notifications.
+ mswr::ComPtr<winui::Input::IEdgeGestureStatics> edge_gesture_statics;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_Input_EdgeGesture,
+ edge_gesture_statics.GetAddressOf());
+ CheckHR(hr, "Failed to activate IEdgeGestureStatics.");
+
+ mswr::ComPtr<winui::Input::IEdgeGesture> edge_gesture;
+ hr = edge_gesture_statics->GetForCurrentView(&edge_gesture);
+ CheckHR(hr);
+
+ hr = edge_gesture->add_Completed(mswr::Callback<EdgeEventHandler>(
+ this, &ChromeAppView::OnEdgeGestureCompleted).Get(),
+ &edgeevent_token_);
+ CheckHR(hr);
+
+ // Register for share notifications.
+ mswr::ComPtr<winapp::DataTransfer::IDataTransferManagerStatics>
+ data_mgr_statics;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager,
+ data_mgr_statics.GetAddressOf());
+ CheckHR(hr, "Failed to activate IDataTransferManagerStatics.");
+
+ mswr::ComPtr<winapp::DataTransfer::IDataTransferManager> data_transfer_mgr;
+ hr = data_mgr_statics->GetForCurrentView(&data_transfer_mgr);
+ CheckHR(hr, "Failed to get IDataTransferManager for current view.");
+
+ hr = data_transfer_mgr->add_DataRequested(
+ mswr::Callback<ShareDataRequestedHandler>(
+ this, &ChromeAppView::OnShareDataRequested).Get(),
+ &share_data_requested_token_);
+ CheckHR(hr);
+
+ // TODO(ananta)
+ // The documented InputPane notifications don't fire on Windows 8 in metro
+ // chrome. Uncomment this once we figure out why they don't fire.
+ // RegisterInputPaneNotifications();
+
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_ViewManagement_ApplicationView,
+ app_view_.GetAddressOf());
+ CheckHR(hr);
+
+ DVLOG(1) << "Created appview instance.";
+
+ hr = devices_handler_.Initialize(window);
+ // Don't check or return the failure here, we need to let the app
+ // initialization succeed. Even if we won't be able to access devices
+ // we still want to allow the app to start.
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to initialize devices handler.";
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ChromeAppView::Load(HSTRING entryPoint) {
+ DVLOG(1) << __FUNCTION__;
+ return S_OK;
+}
+
+void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) {
+ // We're entering a nested message loop, let's allow dispatching
+ // tasks while we're in there.
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+
+ // Enter main core message loop. There are several ways to exit it
+ // Nicely:
+ // 1 - User action like ALT-F4.
+ // 2 - Calling ICoreApplicationExit::Exit().
+ // 3- Posting WM_CLOSE to the core window.
+ HRESULT hr = dispatcher->ProcessEvents(
+ winui::Core::CoreProcessEventsOption
+ ::CoreProcessEventsOption_ProcessUntilQuit);
+
+ // Wind down the thread's chrome message loop.
+ MessageLoop::current()->Quit();
+}
+
+void ChromeAppView::CheckForOSKActivation() {
+ // Hack for checking if the OSK was displayed while we are in the foreground.
+ // The input pane notifications which are supposed to fire when the OSK is
+ // shown and hidden don't seem to be firing in Windows 8 metro for us.
+ // The current hack is supposed to workaround that issue till we figure it
+ // out. Logic is to find the OSK window and see if we are the foreground
+ // process. If yes then fire the notification once for when the OSK is shown
+ // and once for when it is hidden.
+ // TODO(ananta)
+ // Take this out when the documented input pane notifcation issues are
+ // addressed.
+ HWND osk = ::FindWindow(kOSKClassName, NULL);
+ if (::IsWindow(osk)) {
+ HWND foreground_window = ::GetForegroundWindow();
+ if (globals.host_windows.size() > 0 &&
+ foreground_window == globals.host_windows.front().first) {
+ RECT osk_rect = {0};
+ ::GetWindowRect(osk, &osk_rect);
+
+ if (::IsWindowVisible(osk) && ::IsWindowEnabled(osk)) {
+ if (!globals.view->osk_visible_notification_received()) {
+ DVLOG(1) << "Found KB window while we are in the forground.";
+ HandleInputPaneVisible(osk_rect);
+ }
+ } else if (osk_visible_notification_received()) {
+ DVLOG(1) << "KB window hidden while we are in the foreground.";
+ HandleInputPaneHidden(osk_rect);
+ }
+ }
+ }
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppView::CheckForOSKActivation,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
+}
+
+IFACEMETHODIMP
+ChromeAppView::Run() {
+ DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(window_.Get());
+ mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
+ HRESULT hr = window_->get_Dispatcher(&dispatcher);
+ CheckHR(hr, "Dispatcher failed.");
+
+ hr = window_->Activate();
+ if (SUCCEEDED(hr)) {
+ // TODO(cpu): Draw something here.
+ } else {
+ DVLOG(1) << "Activate failed, hr=" << hr;
+ }
+
+ // Create a message loop to allow message passing into this thread.
+ MessageLoop msg_loop(MessageLoop::TYPE_UI);
+
+ // Announce our message loop to the world.
+ globals.appview_msg_loop = msg_loop.message_loop_proxy();
+
+ // And post the task that'll do the inner Metro message pumping to it.
+ msg_loop.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get()));
+
+ // Post the recurring task which checks for OSK activation in metro chrome.
+ // Please refer to the comments in the CheckForOSKActivation function for why
+ // this is needed.
+ // TODO(ananta)
+ // Take this out when the documented OSK notifications start working.
+ msg_loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppView::CheckForOSKActivation,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
+
+ msg_loop.Run();
+
+ globals.appview_msg_loop = NULL;
+
+ DVLOG(0) << "ProcessEvents done, hr=" << hr;
+
+ // We join here with chrome's main thread so that the chrome is not killed
+ // while a critical operation is still in progress. Now, if there are host
+ // windows active it is possible we end up stuck on the wait below therefore
+ // we tell chrome to close its windows.
+ if (!globals.host_windows.empty()) {
+ DVLOG(1) << "Chrome still has windows open!";
+ EndChromeSession();
+ }
+ DWORD wr = ::WaitForSingleObject(globals.host_thread, INFINITE);
+ if (wr != WAIT_OBJECT_0) {
+ DVLOG(1) << "Waiting for host thread failed : " << wr;
+ }
+ ::CloseHandle(globals.host_thread);
+ globals.host_thread = NULL;
+
+ return hr;
+}
+
+IFACEMETHODIMP
+ChromeAppView::Uninitialize() {
+ DVLOG(1) << __FUNCTION__;
+ window_ = nullptr;
+ view_ = nullptr;
+ base::AutoLock lock(notification_lock_);
+ notification_map_.clear();
+ return S_OK;
+}
+
+HRESULT ChromeAppView::RegisterInputPaneNotifications() {
+ DVLOG(1) << __FUNCTION__;
+
+ mswr::ComPtr<winui::ViewManagement::IInputPaneStatics>
+ input_pane_statics;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_ViewManagement_InputPane,
+ input_pane_statics.GetAddressOf());
+ CheckHR(hr);
+
+ hr = input_pane_statics->GetForCurrentView(&input_pane_);
+ CheckHR(hr);
+ DVLOG(1) << "Got input pane.";
+
+ hr = input_pane_->add_Showing(
+ mswr::Callback<InputPaneEventHandler>(
+ this, &ChromeAppView::OnInputPaneVisible).Get(),
+ &input_pane_visible_token_);
+ CheckHR(hr);
+
+ DVLOG(1) << "Added showing event handler for input pane",
+ input_pane_visible_token_.value;
+
+ hr = input_pane_->add_Hiding(
+ mswr::Callback<InputPaneEventHandler>(
+ this, &ChromeAppView::OnInputPaneHiding).Get(),
+ &input_pane_hiding_token_);
+ CheckHR(hr);
+
+ DVLOG(1) << "Added hiding event handler for input pane, value="
+ << input_pane_hiding_token_.value;
+ return hr;
+}
+
+HRESULT ChromeAppView::OnActivate(winapp::Core::ICoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+
+ args->get_PreviousExecutionState(&globals.previous_state);
+ DVLOG(1) << "Previous Execution State: " << globals.previous_state;
+
+ window_->Activate();
+ url_launch_handler_.Activate(args);
+
+ if (globals.previous_state ==
+ winapp::Activation::ApplicationExecutionState_Running &&
+ globals.host_thread) {
+ DVLOG(1) << "Already running. Skipping rest of OnActivate.";
+ return S_OK;
+ }
+
+ do {
+ ::Sleep(10);
+ ::EnumThreadWindows(globals.main_thread_id, &CoreWindowFinder, 0);
+ } while (globals.core_window == NULL);
+
+ DVLOG(1) << "CoreWindow found: " << std::hex << globals.core_window;
+
+ if (!globals.host_thread) {
+ globals.host_thread =
+ ::CreateThread(NULL, 0, HostMainThreadProc, NULL, 0, NULL);
+
+ if (!globals.host_thread) {
+ NOTREACHED() << "thread creation failed.";
+ return E_UNEXPECTED;
+ }
+ }
+
+ if (RegisterHotKey(globals.core_window, kFlipWindowsHotKeyId,
+ MOD_CONTROL, VK_F12)) {
+ DVLOG(1) << "Registered flip window hotkey.";
+ } else {
+ VPLOG(1) << "Failed to register flip window hotkey.";
+ }
+ HRESULT hr = settings_handler_.Initialize();
+ CheckHR(hr,"Failed to initialize settings handler.");
+ return hr;
+}
+
+// We subclass the core window for moving the associated chrome window when the
+// core window is moved around, typically in the snap view operation. The
+// size changes are handled in the documented size changed event.
+LRESULT CALLBACK ChromeAppView::CoreWindowProc(
+ HWND window, UINT message, WPARAM wp, LPARAM lp) {
+
+ static const UINT kBrowserClosingMessage =
+ ::RegisterWindowMessage(L"DefaultBrowserClosing");
+
+ if (message == WM_WINDOWPOSCHANGED) {
+ WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lp);
+ if (!(pos->flags & SWP_NOMOVE)) {
+ DVLOG(1) << "WM_WINDOWPOSCHANGED. Moving the chrome window.";
+ globals.view->OnPositionChanged(pos->x, pos->y);
+ }
+ } else if (message == WM_HOTKEY && wp == kFlipWindowsHotKeyId) {
+ FlipFrameWindows();
+ } else if (message == kBrowserClosingMessage) {
+ DVLOG(1) << "Received DefaultBrowserClosing window message.";
+ // Ensure that the view is uninitialized. The kBrowserClosingMessage
+ // means that the app is going to be terminated, i.e. the proper
+ // uninitialization sequence does not occur.
+ globals.view->Uninitialize();
+ if (!globals.host_windows.empty()) {
+ EndChromeSession();
+ }
+ }
+ return CallWindowProc(globals.g_core_proc, window, message, wp, lp);
+}
+
+HRESULT ChromeAppView::OnSizeChanged(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowSizeChangedEventArgs* args) {
+ if (!globals.host_windows.size()) {
+ return S_OK;
+ }
+
+ winfoundtn::Size size;
+ args->get_Size(&size);
+
+ int cx = static_cast<int>(size.Width);
+ int cy = static_cast<int>(size.Height);
+
+ if (!::SetWindowPos(globals.host_windows.front().first, NULL, 0, 0, cx, cy,
+ SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED)) {
+ DVLOG(1) << "SetWindowPos failed.";
+ }
+ DVLOG(1) << "size changed cx=" << cx;
+ DVLOG(1) << "size changed cy=" << cy;
+
+ winui::ViewManagement::ApplicationViewState view_state =
+ winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
+ app_view_->get_Value(&view_state);
+
+ HWND top_level_frame = globals.host_windows.front().first;
+ if (view_state == winui::ViewManagement::ApplicationViewState_Snapped) {
+ DVLOG(1) << "Enabling metro snap mode.";
+ ::PostMessageW(top_level_frame, WM_SYSCOMMAND, IDC_METRO_SNAP_ENABLE, 0);
+ } else {
+ ::PostMessageW(top_level_frame, WM_SYSCOMMAND, IDC_METRO_SNAP_DISABLE, 0);
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppView::OnPositionChanged(int x, int y) {
+ DVLOG(1) << __FUNCTION__;
+
+ ::SetWindowPos(globals.host_windows.front().first, NULL, x, y, 0, 0,
+ SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE);
+ return S_OK;
+}
+
+HRESULT ChromeAppView::OnEdgeGestureCompleted(
+ winui::Input::IEdgeGesture* gesture,
+ winui::Input::IEdgeGestureEventArgs* args) {
+ DVLOG(1) << "edge gesture completed.";
+
+ winui::ViewManagement::ApplicationViewState view_state =
+ winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
+ app_view_->get_Value(&view_state);
+ // We don't want fullscreen chrome unless we are fullscreen metro.
+ if ((view_state == winui::ViewManagement::ApplicationViewState_Filled) ||
+ (view_state == winui::ViewManagement::ApplicationViewState_Snapped)) {
+ DVLOG(1) << "No full screen in snapped view state:" << view_state;
+ return S_OK;
+ }
+
+ // Deactivate anything pending, e.g., the wrench or a context menu.
+ BOOL success = ::ReleaseCapture();
+ DCHECK(success) << "Couldn't ReleaseCapture() before going full screen";
+
+ DVLOG(1) << "Going full screen.";
+ ::PostMessageW(globals.host_windows.front().first, WM_SYSCOMMAND,
+ IDC_FULLSCREEN, 0);
+ return S_OK;
+}
+
+HRESULT ChromeAppView::OnShareDataRequested(
+ winapp::DataTransfer::IDataTransferManager* data_transfer_mgr,
+ winapp::DataTransfer::IDataRequestedEventArgs* event_args) {
+
+ DVLOG(1) << "Share data requested.";
+
+ // The current tab info is retrieved from Chrome via a registered window
+ // message.
+
+ static const UINT get_current_tab_info =
+ RegisterWindowMessage(kMetroGetCurrentTabInfoMessage);
+
+ static const int kGetTabInfoTimeoutMs = 1000;
+
+ mswr::ComPtr<winapp::DataTransfer::IDataRequest> data_request;
+ HRESULT hr = event_args->get_Request(&data_request);
+ CheckHR(hr);
+
+ mswr::ComPtr<winapp::DataTransfer::IDataPackage> data_package;
+ hr = data_request->get_Data(&data_package);
+ CheckHR(hr);
+
+ base::win::CurrentTabInfo current_tab_info;
+ current_tab_info.title = NULL;
+ current_tab_info.url = NULL;
+
+ DWORD_PTR result = 0;
+
+ if (!SendMessageTimeout(globals.host_windows.front().first,
+ get_current_tab_info,
+ reinterpret_cast<WPARAM>(&current_tab_info),
+ 0,
+ SMTO_ABORTIFHUNG,
+ kGetTabInfoTimeoutMs,
+ &result)) {
+ VPLOG(1) << "Failed to retrieve tab info from chrome.";
+ return E_FAIL;
+ }
+
+ if (!current_tab_info.title || !current_tab_info.url) {
+ DVLOG(1) << "Failed to retrieve tab info from chrome.";
+ return E_FAIL;
+ }
+
+ string16 current_title(current_tab_info.title);
+ string16 current_url(current_tab_info.url);
+
+ LocalFree(current_tab_info.title);
+ LocalFree(current_tab_info.url);
+
+ mswr::ComPtr<winapp::DataTransfer::IDataPackagePropertySet> data_properties;
+ hr = data_package->get_Properties(&data_properties);
+
+ mswrw::HString title;
+ title.Attach(MakeHString(current_title));
+ data_properties->put_Title(title.Get());
+
+ mswr::ComPtr<winfoundtn::IUriRuntimeClassFactory> uri_factory;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Foundation_Uri,
+ uri_factory.GetAddressOf());
+ CheckHR(hr);
+
+ mswrw::HString url;
+ url.Attach(MakeHString(current_url));
+ mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
+ hr = uri_factory->CreateUri(url.Get(), &uri);
+ CheckHR(hr);
+
+ hr = data_package->SetUri(uri.Get());
+ CheckHR(hr);
+
+ return S_OK;
+}
+
+void ChromeAppView::HandleInputPaneVisible(const RECT& osk_rect) {
+ DCHECK(!osk_visible_notification_received_);
+
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "OSK width:" << osk_rect.right - osk_rect.left;
+ DVLOG(1) << "OSK height:" << osk_rect.bottom - osk_rect.top;
+
+ globals.host_windows.front().second = false;
+
+ POINT cursor_pos = {0};
+ GetCursorPos(&cursor_pos);
+
+ osk_offset_adjustment_ = 0;
+
+ if (::PtInRect(&osk_rect, cursor_pos)) {
+ DVLOG(1) << "OSK covering focus point.";
+ int osk_height = osk_rect.bottom - osk_rect.top;
+
+ osk_offset_adjustment_ = osk_height + kOSKAdjustmentOffset;
+
+ DVLOG(1) << "Scrolling window by offset: " << osk_offset_adjustment_;
+ ::ScrollWindowEx(globals.host_windows.front().first,
+ 0,
+ -osk_offset_adjustment_,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SW_INVALIDATE | SW_SCROLLCHILDREN);
+
+ globals.host_windows.front().second = true;
+ }
+ osk_visible_notification_received_ = true;
+}
+
+void ChromeAppView::HandleInputPaneHidden(const RECT& osk_rect) {
+ DCHECK(osk_visible_notification_received_);
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "OSK width:" << osk_rect.right - osk_rect.left;
+ DVLOG(1) << "OSK height:" << osk_rect.bottom - osk_rect.top;
+ osk_visible_notification_received_ = false;
+ if (globals.host_windows.front().second == true) {
+
+ if (osk_offset_adjustment_) {
+ DVLOG(1) << "Restoring scrolled window offset: "
+ << osk_offset_adjustment_;
+
+ ::ScrollWindowEx(globals.host_windows.front().first,
+ 0,
+ osk_offset_adjustment_,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SW_INVALIDATE | SW_SCROLLCHILDREN);
+ }
+
+ globals.host_windows.front().second = false;
+ }
+}
+
+HRESULT ChromeAppView::OnInputPaneVisible(
+ winui::ViewManagement::IInputPane* input_pane,
+ winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) {
+ DVLOG(1) << __FUNCTION__;
+ return S_OK;
+}
+
+HRESULT ChromeAppView::OnInputPaneHiding(
+ winui::ViewManagement::IInputPane* input_pane,
+ winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) {
+ DVLOG(1) << __FUNCTION__;
+ return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ChromeAppViewFactory::ChromeAppViewFactory(
+ winapp::Core::ICoreApplication* icore_app,
+ LPTHREAD_START_ROUTINE host_main,
+ void* host_context) {
+ globals.host_main = host_main;
+ globals.host_context = host_context;
+ mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app);
+ mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit;
+ CheckHR(core_app.As(&app_exit));
+ globals.app_exit = app_exit.Detach();
+}
+
+IFACEMETHODIMP
+ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) {
+ globals.view = mswr::Make<ChromeAppView>().Detach();
+ *view = globals.view;
+ return (*view) ? S_OK : E_OUTOFMEMORY;
+}
diff --git a/win8/metro_driver/chrome_app_view.h b/win8/metro_driver/chrome_app_view.h
new file mode 100644
index 0000000..80f309d
--- /dev/null
+++ b/win8/metro_driver/chrome_app_view.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_CHROME_APP_VIEW_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_CHROME_APP_VIEW_H_
+
+#include <windows.applicationmodel.core.h>
+#include <windows.ui.core.h>
+#include <windows.ui.input.h>
+#include <windows.ui.viewmanagement.h>
+
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "win8/metro_driver/chrome_url_launch_handler.h"
+#include "win8/metro_driver/devices_handler.h"
+#include "win8/metro_driver/metro_dialog_box.h"
+#include "win8/metro_driver/settings_handler.h"
+#include "win8/metro_driver/toast_notification_handler.h"
+
+class ChromeAppView
+ : public mswr::RuntimeClass<winapp::Core::IFrameworkView> {
+ public:
+ ChromeAppView();
+ ~ChromeAppView();
+
+ // IViewProvider overrides.
+ IFACEMETHOD(Initialize)(winapp::Core::ICoreApplicationView* view);
+ IFACEMETHOD(SetWindow)(winui::Core::ICoreWindow* window);
+ IFACEMETHOD(Load)(HSTRING entryPoint);
+ IFACEMETHOD(Run)();
+ IFACEMETHOD(Uninitialize)();
+
+ static LRESULT CALLBACK CoreWindowProc(HWND window, UINT message, WPARAM wp,
+ LPARAM lp);
+
+ HRESULT TileRequestCreateDone(winfoundtn::IAsyncOperation<bool>* async,
+ AsyncStatus status);
+
+ bool osk_visible_notification_received() const {
+ return osk_visible_notification_received_;
+ }
+
+ // Displays the notification.
+ void DisplayNotification(
+ const ToastNotificationHandler::DesktopNotification& notification);
+
+ // Cancels the notification.
+ void CancelNotification(const std::string& notification);
+
+ // Returns true if the notification passed in is valid.
+ bool IsValidNotification(const std::string& notification);
+
+ // Displays a dialog box.
+ void ShowDialogBox(const MetroDialogBox::DialogBoxInfo& dialog_box_info);
+ // Dismisses the dialog box.
+ void DismissDialogBox();
+
+ // Helper function to unsnap the chrome metro app if it is snapped.
+ // Returns S_OK on success.
+ static HRESULT Unsnap();
+
+ // Notification from chrome that a full screen operation is being performed.
+ void SetFullscreen(bool fullscreen);
+
+ private:
+ HRESULT OnActivate(winapp::Core::ICoreApplicationView* view,
+ winapp::Activation::IActivatedEventArgs* args);
+
+ HRESULT OnSizeChanged(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowSizeChangedEventArgs* args);
+
+ HRESULT OnEdgeGestureCompleted(winui::Input::IEdgeGesture* gesture,
+ winui::Input::IEdgeGestureEventArgs* args);
+
+ HRESULT OnShareDataRequested(
+ winapp::DataTransfer::IDataTransferManager* data_transfer_mgr,
+ winapp::DataTransfer::IDataRequestedEventArgs* event_args);
+
+ HRESULT OnInputPaneVisible(
+ winui::ViewManagement::IInputPane* input_pane,
+ winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args);
+
+ HRESULT OnInputPaneHiding(
+ winui::ViewManagement::IInputPane* input_pane,
+ winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args);
+
+ HRESULT OnPositionChanged(int x, int y);
+
+ void CheckForOSKActivation();
+
+ HRESULT RegisterInputPaneNotifications();
+
+ void HandleInputPaneVisible(const RECT& osk_rect);
+ void HandleInputPaneHidden(const RECT& osk_rect);
+
+ mswr::ComPtr<winui::Core::ICoreWindow> window_;
+ mswr::ComPtr<winapp::Core::ICoreApplicationView> view_;
+ EventRegistrationToken activated_token_;
+ EventRegistrationToken edgeevent_token_;
+ EventRegistrationToken sizechange_token_;
+ EventRegistrationToken share_data_requested_token_;
+ EventRegistrationToken input_pane_visible_token_;
+ EventRegistrationToken input_pane_hiding_token_;
+ EventRegistrationToken app_exit_token_;
+
+ ChromeUrlLaunchHandler url_launch_handler_;
+ metro_driver::DevicesHandler devices_handler_;
+ SettingsHandler settings_handler_;
+ mswr::ComPtr<winui::ViewManagement::IInputPane> input_pane_;
+ mswr::ComPtr<winui::ViewManagement::IApplicationViewStatics> app_view_;
+
+ bool osk_visible_notification_received_;
+
+ // map of notification id to the ToastNotificationHandler instance.
+ typedef std::map<std::string, scoped_ptr<ToastNotificationHandler> >
+ NotificationMap;
+ NotificationMap notification_map_;
+
+ // Synchronizes access to the notification_map_ member.
+ base::Lock notification_lock_;
+
+ // If the OSK covers the input area we scroll the window by the height of the
+ // OSK + an additional offset. This member holds this offset. Set to 0 if the
+ // window was not scrolled.
+ int osk_offset_adjustment_;
+
+ MetroDialogBox dialog_box_;
+};
+
+class ChromeAppViewFactory
+ : public mswr::RuntimeClass<winapp::Core::IFrameworkViewSource> {
+ public:
+ ChromeAppViewFactory(winapp::Core::ICoreApplication* icore_app,
+ LPTHREAD_START_ROUTINE host_main,
+ void* host_context);
+ IFACEMETHOD(CreateView)(winapp::Core::IFrameworkView** view);
+};
+
+// This function is exported by chrome.exe.
+typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);
+
+// Global information used across the metro driver.
+struct Globals {
+ LPTHREAD_START_ROUTINE host_main;
+ void* host_context;
+ HWND core_window;
+ // The pair below contains the HWND and a bool which indicates whether the
+ // window was displaced to ensure that the focused region is visible when
+ // the OSK is displayed.
+ std::list<std::pair<HWND, bool> > host_windows;
+ HANDLE host_thread;
+ DWORD main_thread_id;
+ ChromeAppView* view;
+ WNDPROC g_core_proc;
+ string16 navigation_url;
+ string16 search_string;
+ winapp::Activation::ApplicationExecutionState previous_state;
+ winapp::Activation::ActivationKind initial_activation_kind;
+ bool is_initial_activation;
+ // This message loop lives in the app view's thread. Some operations have
+ // to be initiated from that thread, notably spawning file pickers.
+ base::MessageLoopProxy* appview_msg_loop;
+ winapp::Core::ICoreApplicationExit* app_exit;
+ BreakpadExceptionHandler breakpad_exception_handler;
+ string16 metro_command_line_switches;
+};
+
+extern Globals globals;
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_CHROME_APP_VIEW_H_
diff --git a/win8/metro_driver/chrome_url_launch_handler.cc b/win8/metro_driver/chrome_url_launch_handler.cc
new file mode 100644
index 0000000..866bccc
--- /dev/null
+++ b/win8/metro_driver/chrome_url_launch_handler.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "chrome_url_launch_handler.h"
+#include "chrome_app_view.h"
+
+#include <assert.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <string>
+
+#include "base/string_tokenizer.h"
+
+#include "winrt_utils.h"
+
+typedef winfoundtn::ITypedEventHandler<
+ winapp::Search::SearchPane*,
+ winapp::Search::SearchPaneQuerySubmittedEventArgs*> QuerySubmittedHandler;
+
+ChromeUrlLaunchHandler::ChromeUrlLaunchHandler() {
+ globals.is_initial_activation = true;
+ globals.initial_activation_kind = winapp::Activation::ActivationKind_Launch;
+ DVLOG(1) << __FUNCTION__;
+}
+
+// TODO(ananta)
+// Remove this once we consolidate metro driver with chrome.
+const wchar_t kMetroNavigationAndSearchMessage[] =
+ L"CHROME_METRO_NAV_SEARCH_REQUEST";
+
+ChromeUrlLaunchHandler::~ChromeUrlLaunchHandler() {
+ DVLOG(1) << __FUNCTION__;
+ search_pane_->remove_QuerySubmitted(query_submitted_token_);
+}
+
+HRESULT ChromeUrlLaunchHandler::Initialize() {
+ mswr::ComPtr<winapp::Search::ISearchPaneStatics> search_pane_statics;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_ApplicationModel_Search_SearchPane,
+ search_pane_statics.GetAddressOf());
+ CheckHR(hr, "Failed to activate ISearchPaneStatics");
+
+ hr = search_pane_statics->GetForCurrentView(&search_pane_);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get search pane for current view";
+ return hr;
+ }
+
+ hr = search_pane_->add_QuerySubmitted(mswr::Callback<QuerySubmittedHandler>(
+ this,
+ &ChromeUrlLaunchHandler::OnQuerySubmitted).Get(),
+ &query_submitted_token_);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to register for Query Submitted event";
+ return hr;
+ }
+ return hr;
+}
+
+HRESULT ChromeUrlLaunchHandler::OnQuerySubmitted(
+ winapp::Search::ISearchPane* search_pane,
+ winapp::Search::ISearchPaneQuerySubmittedEventArgs* args) {
+ DVLOG(1) << "OnQuerySubmitted";
+ HandleSearchRequest(args);
+ return S_OK;
+}
+
+template<class T>
+void ChromeUrlLaunchHandler::HandleSearchRequest(T* args) {
+ DVLOG(1) << __FUNCTION__;
+ mswrw::HString search_string;
+ args->get_QueryText(search_string.GetAddressOf());
+ string16 search_text(MakeStdWString(search_string.Get()));
+ globals.search_string = search_text;
+ DVLOG(1) << search_text.c_str();
+ // If this is the initial activation then we wait for Chrome to initiate the
+ // navigation. In all other cases navigate right away.
+ if (!globals.is_initial_activation)
+ InitiateNavigationOrSearchRequest(NULL, globals.search_string.c_str());
+}
+
+void ChromeUrlLaunchHandler::HandleProtocolLaunch(
+ winapp::Activation::IProtocolActivatedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
+ args->get_Uri(&uri);
+ mswrw::HString url;
+ uri->get_AbsoluteUri(url.GetAddressOf());
+ string16 actual_url(MakeStdWString(url.Get()));
+ globals.navigation_url = actual_url;
+
+ // If this is the initial activation then we wait for Chrome to initiate the
+ // navigation. In all other cases navigate right away.
+ if (!globals.is_initial_activation)
+ InitiateNavigationOrSearchRequest(globals.navigation_url.c_str(), 0);
+}
+
+// The LaunchArgs are in a semi-color separated key_name=key_value list. At
+// the moment the only key_name understaood is "url".
+string16 ChromeUrlLaunchHandler::GetUrlFromLaunchArgs(
+ const string16& launch_args) {
+ WStringTokenizer tokenizer(launch_args, L";=");
+ bool next_is_url = false;
+ while (tokenizer.GetNext()) {
+ if (next_is_url)
+ return tokenizer.token();
+ if (tokenizer.token() == L"url")
+ next_is_url = true;
+ }
+ if (launch_args == L"opennewwindow") {
+ DVLOG(1) << "Returning new tab url";
+ return L"chrome://newtab";
+ }
+ return string16();
+}
+
+void ChromeUrlLaunchHandler::HandleLaunch(
+ winapp::Activation::ILaunchActivatedEventArgs* args) {
+ mswrw::HString launch_args;
+ args->get_Arguments(launch_args.GetAddressOf());
+ string16 actual_launch_args(MakeStdWString(launch_args.Get()));
+ globals.navigation_url = GetUrlFromLaunchArgs(actual_launch_args);
+ DVLOG(1) << __FUNCTION__ << ", launch_args=" << actual_launch_args
+ << ", url=" << globals.navigation_url
+ << ", is_initial_activation=" << globals.is_initial_activation;
+
+ // If this is the initial launch then we wait for Chrome to initiate the
+ // navigation. In all other cases navigate right away.
+ if (!globals.is_initial_activation)
+ InitiateNavigationOrSearchRequest(globals.navigation_url.c_str(), 0);
+}
+
+void ChromeUrlLaunchHandler::Activate(
+ winapp::Activation::IActivatedEventArgs* args) {
+ winapp::Activation::ActivationKind activation_kind;
+ CheckHR(args->get_Kind(&activation_kind));
+
+ DVLOG(1) << __FUNCTION__ << ", activation_kind=" << activation_kind;
+
+ if (globals.is_initial_activation)
+ globals.initial_activation_kind = activation_kind;
+
+ if (activation_kind == winapp::Activation::ActivationKind_Launch) {
+ mswr::ComPtr<winapp::Activation::ILaunchActivatedEventArgs> launch_args;
+ if (args->QueryInterface(winapp::Activation::IID_ILaunchActivatedEventArgs,
+ &launch_args) == S_OK) {
+ DVLOG(1) << "Activate: ActivationKind_Launch";
+ HandleLaunch(launch_args.Get());
+ }
+ } else if (activation_kind ==
+ winapp::Activation::ActivationKind_Search) {
+ mswr::ComPtr<winapp::Activation::ISearchActivatedEventArgs> search_args;
+ if (args->QueryInterface(winapp::Activation::IID_ISearchActivatedEventArgs,
+ &search_args) == S_OK) {
+ DVLOG(1) << "Activate: ActivationKind_Search";
+ HandleSearchRequest(search_args.Get());
+ }
+ } else if (activation_kind ==
+ winapp::Activation::ActivationKind_Protocol) {
+ mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs>
+ protocol_args;
+ if (args->QueryInterface(
+ winapp::Activation::IID_IProtocolActivatedEventArgs,
+ &protocol_args) == S_OK) {
+ DVLOG(1) << "Activate: ActivationKind_Protocol";
+ HandleProtocolLaunch(protocol_args.Get());
+ }
+ } else {
+ DVLOG(1) << "Activate: Unhandled mode: " << activation_kind;
+ }
+}
+
+void ChromeUrlLaunchHandler::InitiateNavigationOrSearchRequest(
+ const wchar_t* url, const wchar_t* search_string) {
+ DVLOG(1) << __FUNCTION__;
+ if (!url && !search_string) {
+ NOTREACHED();
+ return;
+ }
+
+ DVLOG(1) << (url ? url : L"NULL url");
+ DVLOG(1) << (search_string ? search_string : L"NULL search string");
+
+ // Custom registered message to navigate or search in chrome. WPARAM
+ // points to the URL and LPARAM contains the search string. They are
+ // mutually exclusive.
+ static const UINT navigation_search_message =
+ RegisterWindowMessage(kMetroNavigationAndSearchMessage);
+
+ if (url) {
+ VLOG(1) << "Posting url:" << url;
+ PostMessage(globals.host_windows.front().first, navigation_search_message,
+ reinterpret_cast<WPARAM>(url), 0);
+ } else {
+ VLOG(1) << "Posting search string:" << search_string;
+ PostMessage(globals.host_windows.front().first, navigation_search_message,
+ 0, reinterpret_cast<LPARAM>(search_string));
+ }
+}
diff --git a/win8/metro_driver/chrome_url_launch_handler.h b/win8/metro_driver/chrome_url_launch_handler.h
new file mode 100644
index 0000000..d8c7ed0
--- /dev/null
+++ b/win8/metro_driver/chrome_url_launch_handler.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_CHROME_URL_LAUNCH_HANDLER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_CHROME_URL_LAUNCH_HANDLER_H_
+
+#include <string>
+#include <windows.applicationmodel.core.h>
+#include <Windows.applicationModel.search.h>
+#include <windows.ui.core.h>
+
+#include "winrt_utils.h"
+
+// This class handles the various flavors of URL launches in metro, i.e.
+// via the search charm, via a url being navigated from a metro app, etc.
+class ChromeUrlLaunchHandler {
+ public:
+ ChromeUrlLaunchHandler();
+ ~ChromeUrlLaunchHandler();
+
+ HRESULT Initialize();
+
+ // If metro chrome was launched due to a URL navigation/search request then
+ // the navigation should be done when the frame window is initialized. This
+ // function is called to complete the pending navigation when we receive a
+ // notification from chrome that the frame window is initialized.
+ void PerformPendingNavigation();
+
+ void Activate(winapp::Activation::IActivatedEventArgs* args);
+
+ private:
+ // Invoked when we receive search notifications in metro chrome.
+ template<class T> void HandleSearchRequest(T* args);
+
+ HRESULT OnQuerySubmitted(
+ winapp::Search::ISearchPane* search_pane,
+ winapp::Search::ISearchPaneQuerySubmittedEventArgs* args);
+
+ string16 GetUrlFromLaunchArgs(const string16& launch_args);
+
+ // Invoked when a url is navigated from a metro app or in the metro
+ // shelf.
+ void HandleProtocolLaunch(
+ winapp::Activation::IProtocolActivatedEventArgs* args);
+
+ // Invoked when the app is launched normally
+ void HandleLaunch(winapp::Activation::ILaunchActivatedEventArgs* args);
+
+ // Helper function to initiate a navigation or search request in chrome.
+ void InitiateNavigationOrSearchRequest(const wchar_t* url,
+ const wchar_t* search_string);
+
+ Microsoft::WRL::ComPtr<winapp::Search::ISearchPane> search_pane_;
+ EventRegistrationToken query_submitted_token_;
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_CHROME_URL_LAUNCH_HANDLER_H_
diff --git a/win8/metro_driver/devices_handler.cc b/win8/metro_driver/devices_handler.cc
new file mode 100644
index 0000000..20fd413
--- /dev/null
+++ b/win8/metro_driver/devices_handler.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "win8/metro_driver/devices_handler.h"
+
+#include "base/logging.h"
+
+namespace metro_driver {
+
+DevicesHandler::DevicesHandler() {
+}
+
+DevicesHandler::~DevicesHandler() {
+}
+
+HRESULT DevicesHandler::Initialize(winui::Core::ICoreWindow* window) {
+ HRESULT hr = print_handler_.Initialize(window);
+ return hr;
+}
+
+} // namespace metro_driver
diff --git a/win8/metro_driver/devices_handler.h b/win8/metro_driver/devices_handler.h
new file mode 100644
index 0000000..fdb2226
--- /dev/null
+++ b/win8/metro_driver/devices_handler.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_DEVICES_HANDLER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_DEVICES_HANDLER_H_
+
+#include <windows.ui.core.h>
+
+#include "base/basictypes.h"
+#include "win8/metro_driver/print_handler.h"
+
+namespace metro_driver {
+
+// This class handles the devices charm.
+class DevicesHandler {
+ public:
+ DevicesHandler();
+ ~DevicesHandler();
+
+ HRESULT Initialize(winui::Core::ICoreWindow* window);
+
+ private:
+ PrintHandler print_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevicesHandler);
+};
+
+} // namespace metro_driver
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_DEVICES_HANDLER_H_
diff --git a/win8/metro_driver/file_picker.cc b/win8/metro_driver/file_picker.cc
new file mode 100644
index 0000000..4977be6
--- /dev/null
+++ b/win8/metro_driver/file_picker.cc
@@ -0,0 +1,618 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "win8/metro_driver/file_picker.h"
+
+#include <windows.storage.pickers.h>
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/metro.h"
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+namespace winstorage = ABI::Windows::Storage;
+typedef winfoundtn::Collections::IVector<HSTRING> StringVectorItf;
+
+// TODO(siggi): Complete this implementation and move it to a common place.
+class StringVectorImpl : public mswr::RuntimeClass<StringVectorItf> {
+ public:
+ ~StringVectorImpl() {
+ std::for_each(strings_.begin(), strings_.end(), ::WindowsDeleteString);
+ }
+
+ HRESULT RuntimeClassInitialize(const std::vector<string16>& list) {
+ for (size_t i = 0; i < list.size(); ++i)
+ strings_.push_back(MakeHString(list[i]));
+
+ return S_OK;
+ }
+
+ // IVector<HSTRING> implementation.
+ STDMETHOD(GetAt)(unsigned index, HSTRING* item) {
+ if (index >= strings_.size())
+ return E_INVALIDARG;
+
+ return ::WindowsDuplicateString(strings_[index], item);
+ }
+ STDMETHOD(get_Size)(unsigned *size) {
+ *size = strings_.size();
+ return S_OK;
+ }
+ STDMETHOD(GetView)(winfoundtn::Collections::IVectorView<HSTRING> **view) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(IndexOf)(HSTRING value, unsigned *index, boolean *found) {
+ return E_NOTIMPL;
+ }
+
+ // write methods
+ STDMETHOD(SetAt)(unsigned index, HSTRING item) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(InsertAt)(unsigned index, HSTRING item) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(RemoveAt)(unsigned index) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(Append)(HSTRING item) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(RemoveAtEnd)() {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(Clear)() {
+ return E_NOTIMPL;
+ }
+
+ private:
+ std::vector<HSTRING> strings_;
+};
+
+class FilePickerSessionBase {
+ public:
+ // Creates a file picker for open_file_name.
+ explicit FilePickerSessionBase(OPENFILENAME* open_file_name);
+
+ // Runs the picker, returns true on success.
+ bool Run();
+
+ protected:
+ // Creates, configures and starts a file picker.
+ // If the HRESULT returned is a failure code the file picker has not started,
+ // so no callbacks should be expected.
+ virtual HRESULT StartFilePicker() = 0;
+
+ // The parameters to our picker.
+ OPENFILENAME* open_file_name_;
+ // The event Run waits on.
+ base::WaitableEvent event_;
+ // True iff a file picker has successfully finished.
+ bool success_;
+
+ private:
+ // Initiate a file picker, must be called on the metro dispatcher's thread.
+ void DoFilePicker();
+
+ DISALLOW_COPY_AND_ASSIGN(FilePickerSessionBase);
+};
+
+class OpenFilePickerSession : public FilePickerSessionBase {
+ public:
+ explicit OpenFilePickerSession(OPENFILENAME* open_file_name);
+
+ private:
+ virtual HRESULT StartFilePicker() OVERRIDE;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
+ SingleFileAsyncOp;
+ typedef winfoundtn::Collections::IVectorView<
+ winstorage::StorageFile*> StorageFileVectorCollection;
+ typedef winfoundtn::IAsyncOperation<StorageFileVectorCollection*>
+ MultiFileAsyncOp;
+
+ // Called asynchronously when a single file picker is done.
+ HRESULT SinglePickerDone(SingleFileAsyncOp* async, AsyncStatus status);
+
+ // Called asynchronously when a multi file picker is done.
+ HRESULT MultiPickerDone(MultiFileAsyncOp* async, AsyncStatus status);
+
+ // Composes a multi-file result string suitable for returning to a
+ // from a storage file collection.
+ static HRESULT ComposeMultiFileResult(StorageFileVectorCollection* files,
+ string16* result);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OpenFilePickerSession);
+};
+
+class SaveFilePickerSession : public FilePickerSessionBase {
+ public:
+ explicit SaveFilePickerSession(OPENFILENAME* open_file_name);
+
+ private:
+ virtual HRESULT StartFilePicker() OVERRIDE;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
+ SaveFileAsyncOp;
+
+ // Called asynchronously when the save file picker is done.
+ HRESULT FilePickerDone(SaveFileAsyncOp* async, AsyncStatus status);
+};
+
+FilePickerSessionBase::FilePickerSessionBase(OPENFILENAME* open_file_name)
+ : open_file_name_(open_file_name),
+ event_(true, false),
+ success_(false) {
+}
+
+bool FilePickerSessionBase::Run() {
+ DCHECK(globals.appview_msg_loop != NULL);
+
+ // Post the picker request over to the metro thread.
+ bool posted = globals.appview_msg_loop->PostTask(FROM_HERE,
+ base::Bind(&FilePickerSessionBase::DoFilePicker, base::Unretained(this)));
+ if (!posted)
+ return false;
+
+ // Wait for the file picker to complete.
+ event_.Wait();
+
+ return success_;
+}
+
+void FilePickerSessionBase::DoFilePicker() {
+ // The file picker will fail if spawned from a snapped application,
+ // so let's attempt to unsnap first if we're in that state.
+ HRESULT hr = ChromeAppView::Unsnap();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to unsnap for file picker, error 0x" << hr;
+ }
+
+ if (SUCCEEDED(hr))
+ hr = StartFilePicker();
+
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to start file picker, error 0x"
+ << std::hex << hr;
+
+ event_.Signal();
+ }
+}
+
+OpenFilePickerSession::OpenFilePickerSession(OPENFILENAME* open_file_name)
+ : FilePickerSessionBase(open_file_name) {
+}
+
+HRESULT OpenFilePickerSession::SinglePickerDone(SingleFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ HRESULT hr = async->GetResults(file.GetAddressOf());
+
+ if (file) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = file.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ size_t path_len = 0;
+ const wchar_t* path_str =
+ ::WindowsGetStringRawBuffer(file_path.Get(), &path_len);
+
+ // If the selected file name is longer than the supplied buffer,
+ // we return false as per GetOpenFileName documentation.
+ if (path_len < open_file_name_->nMaxFile) {
+ base::wcslcpy(open_file_name_->lpstrFile,
+ path_str,
+ open_file_name_->nMaxFile);
+ success_ = true;
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << status;
+ }
+
+ event_.Signal();
+
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::MultiPickerDone(MultiFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<StorageFileVectorCollection> files;
+ HRESULT hr = async->GetResults(files.GetAddressOf());
+
+ if (files) {
+ string16 result;
+ if (SUCCEEDED(hr))
+ hr = ComposeMultiFileResult(files.Get(), &result);
+
+ if (SUCCEEDED(hr)) {
+ if (result.size() + 1 < open_file_name_->nMaxFile) {
+ // Because the result has embedded nulls, we must memcpy.
+ memcpy(open_file_name_->lpstrFile,
+ result.c_str(),
+ (result.size() + 1) * sizeof(result[0]));
+ success_ = true;
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL StorageFileVectorCollection";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << status;
+ }
+
+ event_.Signal();
+
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::StartFilePicker() {
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
+ DCHECK(open_file_name_ != NULL);
+
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FileOpenPicker);
+
+ // Create the file picker.
+ mswr::ComPtr<winstorage::Pickers::IFileOpenPicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ // Set the file type filter
+ mswr::ComPtr<winfoundtn::Collections::IVector<HSTRING>> filter;
+ hr = picker->get_FileTypeFilter(filter.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ if (open_file_name_->lpstrFilter == NULL) {
+ hr = filter->Append(mswrw::HStringReference(L"*").Get());
+ if (FAILED(hr))
+ return hr;
+ } else {
+ // The filter is a concatenation of zero terminated string pairs,
+ // where each pair is {description, extension}. The concatenation ends
+ // with a zero length string - e.g. a double zero terminator.
+ const wchar_t* walk = open_file_name_->lpstrFilter;
+ while (*walk != L'\0') {
+ // Walk past the description.
+ walk += wcslen(walk) + 1;
+
+ // We should have an extension, but bail on malformed filters.
+ if (*walk == L'\0')
+ break;
+
+ // There can be a single extension, or a list of semicolon-separated ones.
+ std::vector<string16> extensions_win32_style;
+ size_t extension_count = Tokenize(walk, L";", &extensions_win32_style);
+ DCHECK_EQ(extension_count, extensions_win32_style.size());
+
+ // Metro wants suffixes only, not patterns.
+ mswrw::HString extension;
+ std::vector<string16> extensions;
+ for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
+ if (extensions_win32_style[i] == L"*.*") {
+ // The wildcard filter is "*" for Metro. The string "*.*" produces
+ // an "invalid parameter" error.
+ hr = extension.Set(L"*");
+ } else {
+ // Metro wants suffixes only, not patterns.
+ string16 ext = FilePath(extensions_win32_style[i]).Extension();
+ if ((ext.size() < 2) ||
+ (ext.find_first_of(L"*?") != string16::npos)) {
+ continue;
+ }
+ hr = extension.Set(ext.c_str());
+ }
+ if (SUCCEEDED(hr))
+ hr = filter->Append(extension.Get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ // Walk past the extension.
+ walk += wcslen(walk) + 1;
+ }
+ }
+
+ // Spin up a single or multi picker as appropriate.
+ if (open_file_name_->Flags & OFN_ALLOWMULTISELECT) {
+ mswr::ComPtr<MultiFileAsyncOp> completion;
+ hr = picker->PickMultipleFilesAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ StorageFileVectorCollection*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &OpenFilePickerSession::MultiPickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+ } else {
+ mswr::ComPtr<SingleFileAsyncOp> completion;
+ hr = picker->PickSingleFileAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFile*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &OpenFilePickerSession::SinglePickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+ }
+}
+
+HRESULT OpenFilePickerSession::ComposeMultiFileResult(
+ StorageFileVectorCollection* files, string16* result) {
+ DCHECK(files != NULL);
+ DCHECK(result != NULL);
+
+ // Empty the output string.
+ result->clear();
+
+ size_t num_files = 0;
+ HRESULT hr = files->get_Size(&num_files);
+ if (FAILED(hr))
+ return hr;
+
+ // Make sure we return an error on an empty collection.
+ if (num_files == 0) {
+ DLOG(ERROR) << "Empty collection on input.";
+ return E_UNEXPECTED;
+ }
+
+ // This stores the base path that should be the parent of all the files.
+ FilePath base_path;
+
+ // Iterate through the collection and append the file paths to the result.
+ for (size_t i = 0; i < num_files; ++i) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ hr = files->GetAt(i, file.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ hr = file.As(&storage_item);
+ if (FAILED(hr))
+ return hr;
+
+ mswrw::HString file_path_str;
+ hr = storage_item->get_Path(file_path_str.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ FilePath file_path(MakeStdWString(file_path_str.Get()));
+ if (base_path.empty()) {
+ DCHECK(result->empty());
+ base_path = file_path.DirName();
+
+ // Append the path, including the terminating zero.
+ // We do this only for the first file.
+ result->append(base_path.value().c_str(), base_path.value().size() + 1);
+ }
+ DCHECK(!result->empty());
+ DCHECK(!base_path.empty());
+ DCHECK(base_path == file_path.DirName());
+
+ // Append the base name, including the terminating zero.
+ FilePath base_name = file_path.BaseName();
+ result->append(base_name.value().c_str(), base_name.value().size() + 1);
+ }
+
+ DCHECK(!result->empty());
+
+ return S_OK;
+}
+
+SaveFilePickerSession::SaveFilePickerSession(OPENFILENAME* open_file_name)
+ : FilePickerSessionBase(open_file_name) {
+}
+
+HRESULT SaveFilePickerSession::StartFilePicker() {
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
+ DCHECK(open_file_name_ != NULL);
+
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FileSavePicker);
+
+ // Create the file picker.
+ mswr::ComPtr<winstorage::Pickers::IFileSavePicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ typedef winfoundtn::Collections::IMap<HSTRING, StringVectorItf*>
+ StringVectorMap;
+ mswr::ComPtr<StringVectorMap> choices;
+ hr = picker->get_FileTypeChoices(choices.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ if (open_file_name_->lpstrFilter) {
+ // The filter is a concatenation of zero terminated string pairs,
+ // where each pair is {description, extension list}. The concatenation ends
+ // with a zero length string - e.g. a double zero terminator.
+ const wchar_t* walk = open_file_name_->lpstrFilter;
+ while (*walk != L'\0') {
+ mswrw::HString description;
+ hr = description.Set(walk);
+ if (FAILED(hr))
+ return hr;
+
+ // Walk past the description.
+ walk += wcslen(walk) + 1;
+
+ // We should have an extension, but bail on malformed filters.
+ if (*walk == L'\0')
+ break;
+
+ // There can be a single extension, or a list of semicolon-separated ones.
+ std::vector<string16> extensions_win32_style;
+ size_t extension_count = Tokenize(walk, L";", &extensions_win32_style);
+ DCHECK_EQ(extension_count, extensions_win32_style.size());
+
+ // Metro wants suffixes only, not patterns. Also, metro does not support
+ // the all files ("*") pattern in the save picker.
+ std::vector<string16> extensions;
+ for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
+ string16 ext = FilePath(extensions_win32_style[i]).Extension();
+ if ((ext.size() < 2) ||
+ (ext.find_first_of(L"*?") != string16::npos))
+ continue;
+ extensions.push_back(ext);
+ }
+
+ if (!extensions.empty()) {
+ // Convert to a Metro collection class.
+ mswr::ComPtr<StringVectorItf> list;
+ hr = mswr::MakeAndInitialize<StringVectorImpl>(
+ list.GetAddressOf(), extensions);
+ if (FAILED(hr))
+ return hr;
+
+ // Finally set the filter.
+ boolean replaced = FALSE;
+ hr = choices->Insert(description.Get(), list.Get(), &replaced);
+ if (FAILED(hr))
+ return hr;
+ DCHECK_EQ(FALSE, replaced);
+ }
+
+ // Walk past the extension(s).
+ walk += wcslen(walk) + 1;
+ }
+ }
+
+ // The save picker requires at least one choice. Callers are strongly advised
+ // to provide sensible choices. If none were given, fallback to .dat.
+ uint32 num_choices = 0;
+ hr = choices->get_Size(&num_choices);
+ if (FAILED(hr))
+ return hr;
+
+ if (num_choices == 0) {
+ mswrw::HString description;
+ // TODO(grt): Get a properly translated string. This can't be done from
+ // within metro_driver. Consider preprocessing the filter list in Chrome
+ // land to ensure it has this entry if all others are patterns. In that
+ // case, this whole block of code can be removed.
+ hr = description.Set(L"Data File");
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<StringVectorItf> list;
+ hr = mswr::MakeAndInitialize<StringVectorImpl>(
+ list.GetAddressOf(), std::vector<string16>(1, L".dat"));
+ if (FAILED(hr))
+ return hr;
+
+ boolean replaced = FALSE;
+ hr = choices->Insert(description.Get(), list.Get(), &replaced);
+ if (FAILED(hr))
+ return hr;
+ DCHECK_EQ(FALSE, replaced);
+ }
+
+ if (open_file_name_->lpstrFile != NULL) {
+ hr = picker->put_SuggestedFileName(
+ mswrw::HStringReference(
+ const_cast<const wchar_t*>(open_file_name_->lpstrFile)).Get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ mswr::ComPtr<SaveFileAsyncOp> completion;
+ hr = picker->PickSaveFileAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFile*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &SaveFilePickerSession::FilePickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+}
+
+HRESULT SaveFilePickerSession::FilePickerDone(SaveFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ HRESULT hr = async->GetResults(file.GetAddressOf());
+
+ if (file) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = file.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ string16 path_str = MakeStdWString(file_path.Get());
+
+ // If the selected file name is longer than the supplied buffer,
+ // we return false as per GetOpenFileName documentation.
+ if (path_str.size() < open_file_name_->nMaxFile) {
+ base::wcslcpy(open_file_name_->lpstrFile,
+ path_str.c_str(),
+ open_file_name_->nMaxFile);
+ success_ = true;
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << status;
+ }
+
+ event_.Signal();
+
+ return S_OK;
+}
+
+} // namespace
+
+BOOL MetroGetOpenFileName(OPENFILENAME* open_file_name) {
+ OpenFilePickerSession session(open_file_name);
+
+ return session.Run();
+}
+
+BOOL MetroGetSaveFileName(OPENFILENAME* open_file_name) {
+ SaveFilePickerSession session(open_file_name);
+
+ return session.Run();
+}
diff --git a/win8/metro_driver/file_picker.h b/win8/metro_driver/file_picker.h
new file mode 100644
index 0000000..ef56cb3
--- /dev/null
+++ b/win8/metro_driver/file_picker.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_FILE_PICKER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_H_
+
+#include <commdlg.h>
+
+// This function behaves similarly to GetOpenFileName, except it uses a
+// Metro file picker to pick a single or multiple file names.
+extern "C" __declspec(dllexport)
+BOOL MetroGetOpenFileName(OPENFILENAME* open_file_name);
+
+extern "C" __declspec(dllexport)
+BOOL MetroGetSaveFileName(OPENFILENAME* open_file_name);
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_H_
+
diff --git a/win8/metro_driver/metro_dialog_box.cc b/win8/metro_driver/metro_dialog_box.cc
new file mode 100644
index 0000000..25e7082
--- /dev/null
+++ b/win8/metro_driver/metro_dialog_box.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 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 "win8/metro_driver/stdafx.h"
+
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/metro_dialog_box.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+typedef winfoundtn::Collections::
+ IVector<ABI::Windows::UI::Popups::IUICommand*> WindowsUICommands;
+
+typedef winfoundtn::IAsyncOperation<ABI::Windows::UI::Popups::IUICommand*>
+ AsyncCommandStatus;
+
+MetroDialogBox::MetroDialogBox() {
+ DVLOG(1) << __FUNCTION__;
+ dialog_box_info_.button1_handler = NULL;
+ dialog_box_info_.button2_handler = NULL;
+}
+
+MetroDialogBox::~MetroDialogBox() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+void MetroDialogBox::Show(
+ const DialogBoxInfo& dialog_box_info) {
+ DVLOG(1) << __FUNCTION__;
+
+ // Only one dialog can be displayed at a given time.
+ DCHECK(dialog_box_.Get() == NULL);
+
+ // The message dialog display does not work correctly in snapped mode.
+ mswr::ComPtr<winui::Popups::IMessageDialogFactory> message_dialog_factory;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_Popups_MessageDialog,
+ message_dialog_factory.GetAddressOf());
+ CheckHR(hr, "Failed to activate IMessageDialogFactory");
+
+ mswrw::HString message_title;
+ message_title.Attach(MakeHString(dialog_box_info.title));
+
+ mswrw::HString message_content;
+ message_content.Attach(MakeHString(dialog_box_info.content));
+
+ hr = message_dialog_factory->CreateWithTitle(
+ message_content.Get(),
+ message_title.Get(),
+ dialog_box_.GetAddressOf());
+ CheckHR(hr, "Failed to create message dialog");
+
+ mswr::ComPtr<WindowsUICommands> commands;
+ hr = dialog_box_->get_Commands(commands.GetAddressOf());
+ CheckHR(hr, "Failed to create ui command collection");
+
+ mswr::ComPtr<winui::Popups::IUICommandFactory> ui_command_factory;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_Popups_UICommand,
+ ui_command_factory.GetAddressOf());
+ CheckHR(hr, "Failed to activate IUICommandFactory");
+
+ mswrw::HString label1;
+ label1.Attach(MakeHString(dialog_box_info.button1_label));
+
+ mswr::ComPtr<winui::Popups::IUICommand> label1_command;
+ hr = ui_command_factory->CreateWithHandler(
+ label1.Get(), this, label1_command.GetAddressOf());
+ CheckHR(hr, "Failed to add button1");
+
+ mswrw::HString label2;
+ label2.Attach(MakeHString(dialog_box_info.button2_label));
+
+ mswr::ComPtr<winui::Popups::IUICommand> label2_command;
+ hr = ui_command_factory->CreateWithHandler(label2.Get(), this,
+ label2_command.GetAddressOf());
+ CheckHR(hr, "Failed to add button2");
+
+ commands->Append(label1_command.Get());
+ commands->Append(label2_command.Get());
+
+ mswr::ComPtr<AsyncCommandStatus> ret;
+ hr = dialog_box_->ShowAsync(ret.GetAddressOf());
+ CheckHR(hr, "Failed to show dialog");
+
+ dialog_box_info_ = dialog_box_info;
+}
+
+// The dialog box displayed via the MessageDialog interface has the class name
+// 'Shell_Dialog'. The dialog box is top level window. To find it we enumerate
+// all top level windows and compare the class names. If we find a matching
+// window class we compare its process id with ours and return the same.
+BOOL CALLBACK DialogBoxFinder(HWND hwnd, LPARAM lparam) {
+ char classname[MAX_PATH] = {0};
+
+ if (::GetClassNameA(hwnd, classname, ARRAYSIZE(classname))) {
+ if (lstrcmpiA("Shell_Dialog", classname) == 0) {
+ if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) {
+ DVLOG(1) << "Found top most dialog box: " << classname;
+ DVLOG(1) << "HWND: " << hwnd;
+ DWORD window_pid = 0;
+ DWORD window_tid = GetWindowThreadProcessId(hwnd, &window_pid);
+ DVLOG(1) << "Window tid: " << window_tid;
+ DVLOG(1) << "Window pid: " << window_pid;
+
+ if (window_pid == ::GetCurrentProcessId()) {
+ HWND* dialog_window = reinterpret_cast<HWND*>(lparam);
+ *dialog_window = hwnd;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+void MetroDialogBox::Dismiss() {
+ DVLOG(1) << __FUNCTION__;
+ if (!dialog_box_)
+ return;
+
+ dialog_box_info_.button1_handler = NULL;
+ dialog_box_info_.button2_handler = NULL;
+ dialog_box_info_.button1_label.clear();
+ dialog_box_info_.button2_label.clear();
+ dialog_box_.Reset();
+
+ // We don't have a good way to dismiss the dialog box. Hack for now is to
+ // find the dialog box class in our process and close it via the WM_CLOSE
+ // message.
+ HWND dialog_box = NULL;
+ ::EnumWindows(&DialogBoxFinder, reinterpret_cast<LPARAM>(&dialog_box));
+ if (::IsWindow(dialog_box))
+ PostMessage(dialog_box, WM_CLOSE, 0, 0);
+}
+
+HRESULT STDMETHODCALLTYPE MetroDialogBox::Invoke(
+ winui::Popups::IUICommand* command) {
+ DVLOG(1) << __FUNCTION__;
+
+ mswrw::HString label;
+ command->get_Label(label.GetAddressOf());
+
+ string16 button_label = MakeStdWString(label.Get());
+ DVLOG(1) << "Clicked button label is : " << button_label;
+ if (button_label == dialog_box_info_.button1_label) {
+ DVLOG(1) << "Button1 clicked";
+ DCHECK(dialog_box_info_.button1_handler);
+ dialog_box_info_.button1_handler();
+ } else if (button_label == dialog_box_info_.button2_label) {
+ DVLOG(1) << "Button2 clicked";
+ DCHECK(dialog_box_info_.button2_handler);
+ dialog_box_info_.button2_handler();
+ }
+ // The dialog box is destroyed once we return from invoke. Go ahead and
+ // dismiss it.
+ Dismiss();
+ return S_OK;
+}
+
diff --git a/win8/metro_driver/metro_dialog_box.h b/win8/metro_driver/metro_dialog_box.h
new file mode 100644
index 0000000..5a6a94d
--- /dev/null
+++ b/win8/metro_driver/metro_dialog_box.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_METRO_DIALOG_BOX_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_METRO_DIALOG_BOX_H_
+
+#include <windows.ui.popups.h>
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/win/metro.h"
+
+// Provides functionality to display a dialog box
+class MetroDialogBox : public winui::Popups::IUICommandInvokedHandler {
+ public:
+ struct DialogBoxInfo {
+ string16 title;
+ string16 content;
+ string16 button1_label;
+ string16 button2_label;
+ base::win::MetroDialogButtonPressedHandler button1_handler;
+ base::win::MetroDialogButtonPressedHandler button2_handler;
+ };
+
+ MetroDialogBox();
+ ~MetroDialogBox();
+
+ // Displays the dialog box.
+ void Show(const DialogBoxInfo& dialog_box_info);
+
+ // Dismisses the dialog box.
+ void Dismiss();
+
+ // IUICommandInvokedHandler implementation.
+ // Dummy implementation of IUnknown. This is fine as the lifetime of this
+ // class is tied to the lifetime of the ChromeAppView instance.
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** object) {
+ DVLOG(1) << __FUNCTION__;
+ CHECK(false);
+ return E_NOINTERFACE;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE AddRef(void) {
+ DVLOG(1) << __FUNCTION__;
+ return 1;
+ }
+
+ virtual ULONG STDMETHODCALLTYPE Release(void) {
+ DVLOG(1) << __FUNCTION__;
+ return 1;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Invoke(winui::Popups::IUICommand* command);
+
+ private:
+ // The actual dialog box.
+ mswr::ComPtr<winui::Popups::IMessageDialog> dialog_box_;
+ DialogBoxInfo dialog_box_info_;
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_METRO_DIALOG_BOX_H_
+
diff --git a/win8/metro_driver/metro_driver.cc b/win8/metro_driver/metro_driver.cc
new file mode 100644
index 0000000..89bd35a
--- /dev/null
+++ b/win8/metro_driver/metro_driver.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2012 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 "stdafx.h"
+
+#include <roerrorapi.h>
+#include <shobjidl.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/logging_win.h"
+#include "base/win/scoped_comptr.h"
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/winrt_utils.h"
+#include "sandbox/win/src/sidestep/preamble_patcher.h"
+
+// TODO(siggi): Move this to GYP.
+#pragma comment(lib, "runtimeobject.lib")
+
+namespace {
+
+LONG WINAPI ErrorReportingHandler(EXCEPTION_POINTERS* ex_info) {
+ // See roerrorapi.h for a description of the
+ // exception codes and parameters.
+ DWORD code = ex_info->ExceptionRecord->ExceptionCode;
+ ULONG_PTR* info = ex_info->ExceptionRecord->ExceptionInformation;
+ if (code == EXCEPTION_RO_ORIGINATEERROR) {
+ string16 msg(reinterpret_cast<wchar_t*>(info[2]), info[1]);
+ LOG(ERROR) << "VEH: Metro error 0x" << std::hex << info[0] << ": " << msg;
+ } else if (code == EXCEPTION_RO_TRANSFORMERROR) {
+ string16 msg(reinterpret_cast<wchar_t*>(info[3]), info[2]);
+ LOG(ERROR) << "VEH: Metro old error 0x" << std::hex << info[0]
+ << " new error 0x" << info[1] << ": " << msg;
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// TODO(robertshield): This GUID is hard-coded in a bunch of places that
+// don't allow explicit includes. Find a single place for it to live.
+// {7FE69228-633E-4f06-80C1-527FEA23E3A7}
+const GUID kChromeTraceProviderName = {
+ 0x7fe69228, 0x633e, 0x4f06,
+ { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
+
+}
+// Required for base initialization.
+// TODO(siggi): This should be handled better, as this way our at exit
+// registrations will run under the loader's lock. However,
+// once metro_driver is merged into Chrome.dll, this will go away anyhow.
+base::AtExitManager at_exit;
+
+namespace Hacks {
+
+typedef BOOL (WINAPI* IsImmersiveFunctionPtr)(HANDLE process);
+char* g_real_is_immersive_proc_stub = NULL;
+
+HMODULE g_webrtc_quartz_dll_handle = NULL;
+bool g_fake_is_immersive_process_ret = false;
+
+BOOL WINAPI MetroChromeIsImmersiveIntercept(HANDLE process) {
+ if (g_fake_is_immersive_process_ret && process == ::GetCurrentProcess())
+ return FALSE;
+
+ IsImmersiveFunctionPtr real_proc =
+ reinterpret_cast<IsImmersiveFunctionPtr>(
+ static_cast<char*>(g_real_is_immersive_proc_stub));
+ return real_proc(process);
+}
+
+void MetroSpecificHacksInitialize() {
+ // The quartz dll which is used by the webrtc code in Chrome fails in a metro
+ // app. It checks this via the IsImmersiveProcess export in user32. We
+ // intercept the same and spoof the value as false. This is ok as technically
+ // a metro browser is not a real metro application. The webrtc functionality
+ // works fine with this hack.
+ // TODO(tommi)
+ // BUG:- https://code.google.com/p/chromium/issues/detail?id=140545
+ // We should look into using media foundation on windows 8 in metro chrome.
+ IsImmersiveFunctionPtr is_immersive_func_address =
+ reinterpret_cast<IsImmersiveFunctionPtr>(::GetProcAddress(
+ ::GetModuleHandle(L"user32.dll"), "IsImmersiveProcess"));
+ DCHECK(is_immersive_func_address);
+
+ // Allow the function to be patched by changing the protections on the page.
+ DWORD old_protect = 0;
+ ::VirtualProtect(is_immersive_func_address, 5, PAGE_EXECUTE_READWRITE,
+ &old_protect);
+
+ DCHECK(g_real_is_immersive_proc_stub == NULL);
+ g_real_is_immersive_proc_stub = reinterpret_cast<char*>(VirtualAllocEx(
+ ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+ DCHECK(g_real_is_immersive_proc_stub);
+
+ sidestep::SideStepError patch_result =
+ sidestep::PreamblePatcher::Patch(
+ is_immersive_func_address, MetroChromeIsImmersiveIntercept,
+ g_real_is_immersive_proc_stub, sidestep::kMaxPreambleStubSize);
+
+ DCHECK(patch_result == sidestep::SIDESTEP_SUCCESS);
+
+ // Restore the permissions on the page in user32 containing the
+ // IsImmersiveProcess function code.
+ DWORD dummy = 0;
+ ::VirtualProtect(is_immersive_func_address, 5, old_protect, &dummy);
+
+ // Mimic the original page permissions from the IsImmersiveProcess page
+ // on our stub.
+ ::VirtualProtect(g_real_is_immersive_proc_stub,
+ sidestep::kMaxPreambleStubSize,
+ old_protect,
+ &old_protect);
+
+ g_fake_is_immersive_process_ret = true;
+ g_webrtc_quartz_dll_handle = LoadLibrary(L"quartz.dll");
+ g_fake_is_immersive_process_ret = false;
+
+ DCHECK(g_webrtc_quartz_dll_handle);
+ if (!g_webrtc_quartz_dll_handle) {
+ DVLOG(1) << "Quartz dll load failed with error: " << GetLastError();
+ } else {
+ // Pin the quartz module to protect against it being inadvarently unloaded.
+ ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN, L"quartz.dll",
+ &g_webrtc_quartz_dll_handle);
+ }
+}
+
+} // namespace Hacks
+
+extern "C" __declspec(dllexport)
+int InitMetro(LPTHREAD_START_ROUTINE thread_proc, void* context) {
+ // Initialize the command line.
+ CommandLine::Init(0, NULL);
+ logging::InitLogging(
+ NULL,
+ logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE,
+ logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
+
+#if defined(NDEBUG)
+ logging::SetMinLogLevel(logging::LOG_ERROR);
+ // Bind to the breakpad handling function.
+ globals.breakpad_exception_handler =
+ reinterpret_cast<BreakpadExceptionHandler>(
+ ::GetProcAddress(::GetModuleHandle(NULL),
+ "CrashForException"));
+ if (!globals.breakpad_exception_handler) {
+ DVLOG(0) << "CrashForException export not found";
+ }
+#else
+ logging::SetMinLogLevel(logging::LOG_VERBOSE);
+ // Set the error reporting flags to always raise an exception,
+ // which is then processed by our vectored exception handling
+ // above to log the error message.
+ winfoundtn::Diagnostics::SetErrorReportingFlags(
+ winfoundtn::Diagnostics::UseSetErrorInfo |
+ winfoundtn::Diagnostics::ForceExceptions);
+
+ HANDLE registration =
+ ::AddVectoredExceptionHandler(TRUE, ErrorReportingHandler);
+#endif
+
+ // Enable trace control and transport through event tracing for Windows.
+ logging::LogEventProvider::Initialize(kChromeTraceProviderName);
+
+ DVLOG(1) << "InitMetro";
+
+ mswrw::RoInitializeWrapper ro_initializer(RO_INIT_MULTITHREADED);
+ CheckHR(ro_initializer, "RoInitialize failed");
+
+ mswr::ComPtr<winapp::Core::ICoreApplication> core_app;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_ApplicationModel_Core_CoreApplication,
+ core_app.GetAddressOf());
+ CheckHR(hr, "Failed to create app factory");
+ if (FAILED(hr))
+ return 1;
+
+ // The metro specific hacks code assumes that there is only one thread active
+ // at the moment. This better be the case or we may have race conditions.
+ Hacks::MetroSpecificHacksInitialize();
+
+ auto view_factory = mswr::Make<ChromeAppViewFactory>(
+ core_app.Get(), thread_proc, context);
+ hr = core_app->Run(view_factory.Get());
+ DVLOG(1) << "exiting InitMetro, hr=" << hr;
+
+#if !defined(NDEBUG)
+ ::RemoveVectoredExceptionHandler(registration);
+#endif
+
+ return hr;
+}
+
+// Activates the application known by |app_id|. Returns, among other things,
+// E_APPLICATION_NOT_REGISTERED if |app_id| identifies Chrome and Chrome is not
+// the default browser.
+extern "C" __declspec(dllexport)
+HRESULT ActivateApplication(const wchar_t* app_id) {
+ base::win::ScopedComPtr<IApplicationActivationManager> activator;
+ HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
+ if (SUCCEEDED(hr)) {
+ DWORD pid = 0;
+ hr = activator->ActivateApplication(app_id, L"", AO_NONE, &pid);
+ }
+ return hr;
+}
diff --git a/win8/metro_driver/metro_driver.gyp b/win8/metro_driver/metro_driver.gyp
new file mode 100644
index 0000000..186d2bd
--- /dev/null
+++ b/win8/metro_driver/metro_driver.gyp
@@ -0,0 +1,98 @@
+# Copyright (c) 2012 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.
+{
+ 'conditions': [
+ ['OS=="win" and (MSVS_VERSION=="2010" or MSVS_VERSION=="2010e")', {
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../../build/win_precompile.gypi',
+ ],
+ 'target_defaults': {
+ 'defines': [
+ # This define is required to pull in the new Win8 interfaces from
+ # system headers like ShObjIdl.h
+ 'NTDDI_VERSION=0x06020000',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'AdditionalDependencies': [
+ 'D2D1.lib',
+ 'D3D11.lib',
+ ],
+ },
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'metro_driver',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../crypto/crypto.gyp:crypto',
+ '../../sandbox/sandbox.gyp:sandbox',
+ '../../google_update/google_update.gyp:google_update',
+ '../win8.gyp:check_sdk_patch',
+ ],
+ 'sources': [
+ 'chrome_app_view.cc',
+ 'chrome_app_view.h',
+ 'chrome_url_launch_handler.cc',
+ 'chrome_url_launch_handler.h',
+ '../delegate_execute/chrome_util.cc',
+ '../delegate_execute/chrome_util.h',
+ 'devices_handler.cc',
+ 'devices_handler.h',
+ 'file_picker.h',
+ 'file_picker.cc',
+ 'metro_dialog_box.cc',
+ 'metro_dialog_box.h',
+ 'metro_driver.cc',
+ 'print_handler.cc',
+ 'print_handler.h',
+ 'print_document_source.cc',
+ 'print_document_source.h',
+ 'secondary_tile.h',
+ 'secondary_tile.cc',
+ 'settings_handler.cc',
+ 'settings_handler.h',
+ 'stdafx.h',
+ 'toast_notification_handler.cc',
+ 'toast_notification_handler.h',
+ 'winrt_utils.cc',
+ 'winrt_utils.h',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [
+ 'resources/Logo.png',
+ 'resources/SecondaryTile.png',
+ 'resources/SmallLogo.png',
+ 'resources/splash-620x300.png',
+ 'resources/VisualElementsManifest.xml',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'metro_driver_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../testing/gtest.gyp:gtest',
+ 'metro_driver',
+ ],
+ 'sources': [
+ 'run_all_unittests.cc',
+ 'winrt_utils.cc',
+ 'winrt_utils.h',
+ 'winrt_utils_unittest.cc',
+ ],
+ },
+ ],
+ },],
+ ],
+}
diff --git a/win8/metro_driver/metro_driver_win7.cc b/win8/metro_driver/metro_driver_win7.cc
new file mode 100644
index 0000000..253e527
--- /dev/null
+++ b/win8/metro_driver/metro_driver_win7.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 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 "stdafx.h"
+
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+
+struct Globals {
+ LPTHREAD_START_ROUTINE host_main;
+ void* host_context;
+ HWND core_window;
+ HWND host_window;
+ HANDLE host_thread;
+ DWORD main_thread_id;
+} globals;
+
+
+void ODS(const char* str, LONG_PTR val = 0) {
+ char buf[80];
+ size_t len = strlen(str);
+ if (len > 50) {
+ ::OutputDebugStringA("ODS: buffer too long");
+ return;
+ }
+
+ if (str[0] == '!') {
+ // Fatal error.
+ DWORD gle = ::GetLastError();
+ if (::IsDebuggerPresent())
+ __debugbreak();
+ wsprintfA(buf, "ODS:fatal %s (%p) gle=0x%x", str, val, gle);
+ ::MessageBoxA(NULL, buf, "!!!", MB_OK);
+ ::ExitProcess(gle);
+ } else {
+ // Just information.
+ wsprintfA(buf, "ODS:%s (%p)\n", str, val);
+ ::OutputDebugStringA(buf);
+ }
+}
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ PAINTSTRUCT ps;
+ HDC hdc;
+ switch (message) {
+ case WM_PAINT:
+ hdc = BeginPaint(hwnd, &ps);
+ EndPaint(hwnd, &ps);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ ODS("Metro WM_DESTROY received");
+ break;
+ default:
+ return DefWindowProc(hwnd, message, wparam, lparam);
+ }
+ return 0;
+}
+
+HWND CreateMetroTopLevelWindow() {
+ HINSTANCE hInst = reinterpret_cast<HINSTANCE>(&__ImageBase);
+ WNDCLASSEXW wcex;
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInst;
+ wcex.hIcon = 0;
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_INACTIVECAPTION+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"Windows.UI.Core.CoreWindow";
+ wcex.hIconSm = 0;
+
+ HWND hwnd = ::CreateWindowExW(0,
+ MAKEINTATOM(::RegisterClassExW(&wcex)),
+ L"metro_metro",
+ WS_POPUP,
+ 0, 0, 0, 0,
+ NULL, NULL, hInst, NULL);
+ return hwnd;
+}
+
+DWORD WINAPI HostThread(void*) {
+ // The sleeps simulates the delay we have in the actual metro code
+ // which takes in account the corewindow being created and some other
+ // unknown machinations of metro.
+ ODS("Chrome main thread", ::GetCurrentThreadId());
+ ::Sleep(30);
+ return globals.host_main(globals.host_context);
+}
+
+extern "C" __declspec(dllexport)
+int InitMetro(LPTHREAD_START_ROUTINE thread_proc, void* context) {
+ ODS("InitMetro [Win7 emulation]");
+ HWND window = CreateMetroTopLevelWindow();
+ if (!window)
+ return 1;
+ // This magic incatation tells windows that the window is going fullscreen
+ // so the taskbar gets out of the wait automatically.
+ ::SetWindowPos(window,
+ HWND_TOP,
+ 0,0,
+ GetSystemMetrics(SM_CXSCREEN),
+ GetSystemMetrics(SM_CYSCREEN),
+ SWP_SHOWWINDOW);
+
+ // Ready to start our caller.
+ globals.core_window = window;
+ globals.host_main = thread_proc;
+ globals.host_context = context;
+ HANDLE thread = ::CreateThread(NULL, 0, &HostThread, NULL, 0, NULL);
+
+ // Main message loop.
+ MSG msg = {0};
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ return (int) msg.wParam;
+}
+
+extern "C" _declspec(dllexport) HWND GetRootWindow() {
+ ODS("GetRootWindow", ULONG_PTR(globals.core_window));
+ return globals.core_window;
+}
+
+extern "C" _declspec(dllexport) void SetFrameWindow(HWND window) {
+ ODS("SetFrameWindow", ULONG_PTR(window));
+ globals.host_window = window;
+}
+
+extern "C" __declspec(dllexport) const wchar_t* GetInitialUrl() {
+ return L"";
+}
+
diff --git a/win8/metro_driver/print_document_source.cc b/win8/metro_driver/print_document_source.cc
new file mode 100644
index 0000000..7469597
--- /dev/null
+++ b/win8/metro_driver/print_document_source.cc
@@ -0,0 +1,525 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "win8/metro_driver/print_document_source.h"
+
+#include <windows.graphics.display.h>
+
+#include "base/logging.h"
+
+
+namespace {
+
+class D2DFactoryAutoLock {
+ public:
+ explicit D2DFactoryAutoLock(ID2D1Factory* d2d_factory) {
+ HRESULT hr = d2d_factory->QueryInterface(IID_PPV_ARGS(&d2d_multithread_));
+ if (d2d_multithread_.Get())
+ d2d_multithread_->Enter();
+ else
+ NOTREACHED() << "Failed to QI for ID2D1Multithread " << std::hex << hr;
+ }
+
+ ~D2DFactoryAutoLock() {
+ if (d2d_multithread_.Get())
+ d2d_multithread_->Leave();
+ }
+
+ private:
+ mswr::ComPtr<ID2D1Multithread> d2d_multithread_;
+};
+
+// TODO(mad): remove once we don't run mixed SDK/OS anymore.
+const GUID kOldPackageTargetGuid =
+ {0xfb2a33c0, 0x8c35, 0x465f,
+ {0xbe, 0xd5, 0x9f, 0x36, 0x89, 0x51, 0x77, 0x52}};
+const GUID kNewPackageTargetGuid =
+ {0x1a6dd0ad, 0x1e2a, 0x4e99,
+ {0xa5, 0xba, 0x91, 0xf1, 0x78, 0x18, 0x29, 0x0e}};
+
+
+} // namespace
+
+namespace metro_driver {
+
+PrintDocumentSource::PrintDocumentSource()
+ : page_count_ready_(true, false),
+ parent_lock_(NULL),
+ height_(0),
+ width_(0),
+ dpi_(96),
+ aborted_(false),
+ using_old_preview_interface_(false) {
+}
+
+HRESULT PrintDocumentSource::RuntimeClassInitialize(
+ const DirectXContext& directx_context,
+ base::Lock* parent_lock) {
+ DCHECK(parent_lock != NULL);
+ DCHECK(directx_context.d2d_context.Get() != NULL);
+ DCHECK(directx_context.d2d_device.Get() != NULL);
+ DCHECK(directx_context.d2d_factory.Get() != NULL);
+ DCHECK(directx_context.d3d_device.Get() != NULL);
+ DCHECK(directx_context.wic_factory.Get() != NULL);
+ directx_context_ = directx_context;
+
+ // No other method can be called before RuntimeClassInitialize which is called
+ // during the construction via mswr::MakeAndInitialize(), so it's safe for all
+ // other methods to use the parent_lock_ without checking if it's NULL.
+ DCHECK(parent_lock_ == NULL);
+ parent_lock_ = parent_lock;
+
+ return S_OK;
+}
+
+void PrintDocumentSource::Abort() {
+ base::AutoLock lock(*parent_lock_);
+ aborted_ = true;
+ if (page_count_ready_.IsSignaled()) {
+ pages_.clear();
+ for (size_t i = 0; i < pages_ready_state_.size(); ++i)
+ pages_ready_state_[i]->Broadcast();
+ } else {
+ DCHECK(pages_.empty() && pages_ready_state_.empty());
+ }
+}
+
+STDMETHODIMP PrintDocumentSource::GetPreviewPageCollection(
+ IPrintDocumentPackageTarget* package_target,
+ IPrintPreviewPageCollection** page_collection) {
+ DVLOG(1) << __FUNCTION__;
+ DCHECK(package_target != NULL);
+ DCHECK(page_collection != NULL);
+
+ HRESULT hr = package_target->GetPackageTarget(
+ __uuidof(IPrintPreviewDxgiPackageTarget),
+ IID_PPV_ARGS(&dxgi_preview_target_));
+ if (FAILED(hr)) {
+ // TODO(mad): remove once we don't run mixed SDK/OS anymore.
+ // The IID changed from one version of the SDK to another, so try the other
+ // one in case we are running a build from a different SDK than the one
+ // related to the OS version we are running.
+ GUID package_target_uuid = kNewPackageTargetGuid;
+ if (package_target_uuid == __uuidof(IPrintPreviewDxgiPackageTarget)) {
+ package_target_uuid = kOldPackageTargetGuid;
+ using_old_preview_interface_ = true;
+ }
+ hr = package_target->GetPackageTarget(package_target_uuid,
+ package_target_uuid,
+ &dxgi_preview_target_);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get IPrintPreviewDXGIPackageTarget " << std::hex
+ << hr;
+ return hr;
+ }
+ } else {
+ using_old_preview_interface_ = (__uuidof(IPrintPreviewDxgiPackageTarget) ==
+ kOldPackageTargetGuid);
+ }
+
+ mswr::ComPtr<IPrintPreviewPageCollection> preview_page_collection;
+ mswr::ComPtr<PrintDocumentSource> print_document_source(this);
+ hr = print_document_source.As(&preview_page_collection);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get preview_page_collection " << std::hex << hr;
+ return hr;
+ }
+
+ hr = preview_page_collection.CopyTo(page_collection);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to copy preview_page_collection " << std::hex << hr;
+ return hr;
+ }
+ return hr;
+}
+
+STDMETHODIMP PrintDocumentSource::MakeDocument(
+ IInspectable* options,
+ IPrintDocumentPackageTarget* package_target) {
+ DVLOG(1) << __FUNCTION__;
+ DCHECK(options != NULL);
+ DCHECK(package_target != NULL);
+
+ mswr::ComPtr<wingfx::Printing::IPrintTaskOptionsCore> print_task_options;
+ HRESULT hr = options->QueryInterface(
+ wingfx::Printing::IID_IPrintTaskOptionsCore,
+ reinterpret_cast<void**>(print_task_options.GetAddressOf()));
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to QI for IPrintTaskOptionsCore " << std::hex << hr;
+ return hr;
+ }
+
+ // Use the first page's description for the whole document. Page numbers
+ // are 1-based in this context.
+ // TODO(mad): Check if it would be useful to use per page descriptions.
+ wingfx::Printing::PrintPageDescription page_desc = {};
+ hr = print_task_options->GetPageDescription(1 /* page */, &page_desc);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to GetPageDescription " << std::hex << hr;
+ return hr;
+ }
+
+ D2D1_PRINT_CONTROL_PROPERTIES print_control_properties;
+ if (page_desc.DpiX > page_desc.DpiY)
+ print_control_properties.rasterDPI = static_cast<float>(page_desc.DpiY);
+ else
+ print_control_properties.rasterDPI = static_cast<float>(page_desc.DpiX);
+
+ // Color space for vector graphics in D2D print control.
+ print_control_properties.colorSpace = D2D1_COLOR_SPACE_SRGB;
+ print_control_properties.fontSubset = D2D1_PRINT_FONT_SUBSET_MODE_DEFAULT;
+
+ mswr::ComPtr<ID2D1PrintControl> print_control;
+ hr = directx_context_.d2d_device->CreatePrintControl(
+ directx_context_.wic_factory.Get(),
+ package_target,
+ print_control_properties,
+ print_control.GetAddressOf());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to CreatePrintControl " << std::hex << hr;
+ return hr;
+ }
+
+ D2D1_SIZE_F page_size = D2D1::SizeF(page_desc.PageSize.Width,
+ page_desc.PageSize.Height);
+
+ // Wait for the number of pages to be available.
+ // If an abort occured, we'll get 0 and won't enter the loop below.
+ size_t page_count = WaitAndGetPageCount();
+
+ mswr::ComPtr<ID2D1GdiMetafile> gdi_metafile;
+ for (size_t page = 0; page < page_count; ++page) {
+ gdi_metafile.Reset();
+ hr = WaitAndGetPage(page, gdi_metafile.GetAddressOf());
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to get page's metafile " << std::hex
+ << hr;
+ // S_FALSE means we got aborted.
+ if (hr == S_FALSE || FAILED(hr))
+ break;
+ hr = PrintPage(print_control.Get(), gdi_metafile.Get(), page_size);
+ if (FAILED(hr))
+ break;
+ }
+
+ HRESULT close_hr = print_control->Close();
+ if (FAILED(close_hr) && SUCCEEDED(hr))
+ return close_hr;
+ else
+ return hr;
+}
+
+STDMETHODIMP PrintDocumentSource::Paginate(uint32 page,
+ IInspectable* options) {
+ DVLOG(1) << __FUNCTION__ << ", page = " << page;
+ DCHECK(options != NULL);
+ // GetPreviewPageCollection must have been successfuly called.
+ DCHECK(dxgi_preview_target_.Get() != NULL);
+
+ // Get print settings from PrintTaskOptions for preview, such as page
+ // description, which contains page size, imageable area, DPI.
+ // TODO(mad): obtain other print settings in the same way, such as ColorMode,
+ // NumberOfCopies, etc...
+ mswr::ComPtr<wingfx::Printing::IPrintTaskOptionsCore> print_options;
+ HRESULT hr = options->QueryInterface(
+ wingfx::Printing::IID_IPrintTaskOptionsCore,
+ reinterpret_cast<void**>(print_options.GetAddressOf()));
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to QI for IPrintTaskOptionsCore " << std::hex << hr;
+ return hr;
+ }
+
+ wingfx::Printing::PrintPageDescription page_desc = {};
+ hr = print_options->GetPageDescription(1 /* page */, &page_desc);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to GetPageDescription " << std::hex << hr;
+ return hr;
+ }
+
+ width_ = page_desc.PageSize.Width;
+ height_ = page_desc.PageSize.Height;
+
+ hr = dxgi_preview_target_->InvalidatePreview();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to InvalidatePreview " << std::hex << hr;
+ return hr;
+ }
+
+ size_t page_count = WaitAndGetPageCount();
+ // A page_count of 0 means abort...
+ if (page_count == 0)
+ return S_FALSE;
+ hr = dxgi_preview_target_->SetJobPageCount(PageCountType::FinalPageCount,
+ page_count);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to SetJobPageCount " << std::hex << hr;
+ return hr;
+ }
+ return hr;
+}
+
+STDMETHODIMP PrintDocumentSource::MakePage(uint32 job_page,
+ float width,
+ float height) {
+ DVLOG(1) << __FUNCTION__ << ", width: " << width << ", height: " << height
+ << ", job_page: " << job_page;
+ DCHECK(width > 0 && height > 0);
+ // Paginate must have been called before this.
+ if (width_ <= 0.0 || height_ <= 0.0)
+ return S_FALSE;
+
+ // When job_page is JOB_PAGE_APPLICATION_DEFINED, it means a new preview
+ // begins. TODO(mad): Double check if we need to cancel pending resources.
+ if (job_page == JOB_PAGE_APPLICATION_DEFINED)
+ job_page = 1;
+
+ winfoundtn::Size preview_size;
+ preview_size.Width = width;
+ preview_size.Height = height;
+ float scale = width_ / width;
+
+ mswr::ComPtr<ID2D1Factory> factory;
+ directx_context_.d2d_device->GetFactory(&factory);
+
+ mswr::ComPtr<ID2D1GdiMetafile> gdi_metafile;
+ HRESULT hr = WaitAndGetPage(job_page - 1, gdi_metafile.GetAddressOf());
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to get page's metafile " << std::hex
+ << hr;
+ // Again, S_FALSE means we got aborted.
+ if (hr == S_FALSE || FAILED(hr))
+ return hr;
+
+ // We are accessing D3D resources directly without D2D's knowledge, so we
+ // must manually acquire the D2D factory lock.
+ D2DFactoryAutoLock factory_lock(directx_context_.d2d_factory.Get());
+
+ CD3D11_TEXTURE2D_DESC texture_desc(
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ static_cast<UINT32>(ceil(width * dpi_ / 96)),
+ static_cast<UINT32>(ceil(height * dpi_ / 96)),
+ 1,
+ 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE
+ );
+ mswr::ComPtr<ID3D11Texture2D> texture;
+ hr = directx_context_.d3d_device->CreateTexture2D(
+ &texture_desc, NULL, &texture);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create a 2D texture " << std::hex << hr;
+ return hr;
+ }
+
+ mswr::ComPtr<IDXGISurface> dxgi_surface;
+ hr = texture.As<IDXGISurface>(&dxgi_surface);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to QI for IDXGISurface " << std::hex << hr;
+ return hr;
+ }
+
+ // D2D device contexts are stateful, and hence a unique device context must
+ // be used on each call.
+ mswr::ComPtr<ID2D1DeviceContext> d2d_context;
+ hr = directx_context_.d2d_device->CreateDeviceContext(
+ D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d_context);
+
+ d2d_context->SetDpi(dpi_, dpi_);
+
+ mswr::ComPtr<ID2D1Bitmap1> d2dSurfaceBitmap;
+ hr = d2d_context->CreateBitmapFromDxgiSurface(dxgi_surface.Get(),
+ NULL, // default properties.
+ &d2dSurfaceBitmap);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to CreateBitmapFromDxgiSurface " << std::hex << hr;
+ return hr;
+ }
+
+ d2d_context->SetTarget(d2dSurfaceBitmap.Get());
+ d2d_context->BeginDraw();
+ d2d_context->Clear();
+ d2d_context->SetTransform(D2D1::Matrix3x2F(1/scale, 0, 0, 1/scale, 0, 0));
+ d2d_context->DrawGdiMetafile(gdi_metafile.Get());
+
+ hr = d2d_context->EndDraw();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to EndDraw " << std::hex << hr;
+ return hr;
+ }
+
+// TODO(mad): remove once we don't run mixed SDK/OS anymore.
+#ifdef __IPrintPreviewDxgiPackageTarget_FWD_DEFINED__
+ FLOAT dpi = dpi_;
+ if (using_old_preview_interface_) {
+ // We compiled with the new API but run on the old OS, so we must cheat
+ // and send something that looks like a float but has a UINT32 value.
+ *reinterpret_cast<UINT32*>(&dpi) = static_cast<UINT32>(dpi_);
+ }
+#else
+ UINT32 dpi = static_cast<UINT32>(dpi_);
+ if (!using_old_preview_interface_) {
+ // We compiled with the old API but run on the new OS, so we must cheat
+ // and send something that looks like a UINT32 but has a float value.
+ *reinterpret_cast<FLOAT*>(&dpi) = dpi_;
+ }
+#endif // __IPrintPreviewDxgiPackageTarget_FWD_DEFINED__
+ hr = dxgi_preview_target_->DrawPage(job_page, dxgi_surface.Get(), dpi, dpi);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to DrawPage " << std::hex << hr;
+ return hr;
+ }
+ return hr;
+}
+
+void PrintDocumentSource::ResetDpi(float dpi) {
+ {
+ base::AutoLock lock(*parent_lock_);
+ if (dpi == dpi_)
+ return;
+ dpi_ = dpi;
+ }
+ directx_context_.d2d_context->SetDpi(dpi, dpi);
+}
+
+void PrintDocumentSource::SetPageCount(size_t page_count) {
+ DCHECK(page_count > 0);
+ {
+ base::AutoLock lock(*parent_lock_);
+ DCHECK(!page_count_ready_.IsSignaled());
+ DCHECK(pages_.empty() && pages_ready_state_.empty());
+
+ pages_.resize(page_count);
+ pages_ready_state_.resize(page_count);
+
+ for (size_t i = 0; i < page_count; ++i)
+ pages_ready_state_[i].reset(new base::ConditionVariable(parent_lock_));
+ }
+ page_count_ready_.Signal();
+}
+
+void PrintDocumentSource::AddPage(size_t page_number,
+ IStream* metafile_stream) {
+ DCHECK(metafile_stream != NULL);
+ base::AutoLock lock(*parent_lock_);
+
+ DCHECK(page_count_ready_.IsSignaled());
+ DCHECK(page_number < pages_.size());
+
+ pages_[page_number] = metafile_stream;
+ pages_ready_state_[page_number]->Signal();
+}
+
+HRESULT PrintDocumentSource::PrintPage(ID2D1PrintControl* print_control,
+ ID2D1GdiMetafile* gdi_metafile,
+ D2D1_SIZE_F page_size) {
+ DVLOG(1) << __FUNCTION__ << ", page_size: (" << page_size.width << ", "
+ << page_size.height << ")";
+ DCHECK(print_control != NULL);
+ DCHECK(gdi_metafile != NULL);
+
+ // D2D device contexts are stateful, and hence a unique device context must
+ // be used on each call.
+ mswr::ComPtr<ID2D1DeviceContext> d2d_context;
+ HRESULT hr = directx_context_.d2d_device->CreateDeviceContext(
+ D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d_context);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to CreateDeviceContext " << std::hex << hr;
+ return hr;
+ }
+
+ mswr::ComPtr<ID2D1CommandList> print_command_list;
+ hr = d2d_context->CreateCommandList(&print_command_list);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to CreateCommandList " << std::hex << hr;
+ return hr;
+ }
+
+ d2d_context->SetTarget(print_command_list.Get());
+
+ d2d_context->BeginDraw();
+ d2d_context->DrawGdiMetafile(gdi_metafile);
+ hr = d2d_context->EndDraw();
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to EndDraw " << std::hex << hr;
+
+ // Make sure to always close the command list.
+ HRESULT close_hr = print_command_list->Close();
+ LOG_IF(ERROR, FAILED(close_hr)) << "Failed to close command list " << std::hex
+ << hr;
+ if (SUCCEEDED(hr) && SUCCEEDED(close_hr))
+ hr = print_control->AddPage(print_command_list.Get(), page_size, NULL);
+ if (FAILED(hr))
+ return hr;
+ else
+ return close_hr;
+}
+
+size_t PrintDocumentSource::WaitAndGetPageCount() {
+ // Properly protect the wait/access to the page count.
+ {
+ base::AutoLock lock(*parent_lock_);
+ if (aborted_)
+ return 0;
+ DCHECK(pages_.size() == pages_ready_state_.size());
+ if (!pages_.empty())
+ return pages_.size();
+ }
+ page_count_ready_.Wait();
+ {
+ base::AutoLock lock(*parent_lock_);
+ if (!aborted_) {
+ DCHECK(pages_.size() == pages_ready_state_.size());
+ return pages_.size();
+ }
+ }
+ // A page count of 0 means abort.
+ return 0;
+}
+
+HRESULT PrintDocumentSource::WaitAndGetPage(size_t page_number,
+ ID2D1GdiMetafile** gdi_metafile) {
+ // Properly protect the wait/access to the page data.
+ base::AutoLock lock(*parent_lock_);
+ // Make sure we weren't canceled before getting here.
+ // And the page count should have been received before we get here too.
+ if (aborted_)
+ return S_FALSE;
+
+ // We shouldn't be asked for a page until we got the page count.
+ DCHECK(page_count_ready_.IsSignaled());
+ DCHECK(page_number <= pages_ready_state_.size());
+ DCHECK(pages_.size() == pages_ready_state_.size());
+ while (!aborted_ && pages_[page_number].Get() == NULL)
+ pages_ready_state_[page_number]->Wait();
+
+ // Make sure we weren't aborted while we waited unlocked.
+ if (aborted_)
+ return S_FALSE;
+ DCHECK(page_number < pages_.size());
+
+ mswr::ComPtr<ID2D1Factory> factory;
+ directx_context_.d2d_device->GetFactory(&factory);
+
+ mswr::ComPtr<ID2D1Factory1> factory1;
+ HRESULT hr = factory.As(&factory1);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to QI for ID2D1Factory1 " << std::hex << hr;
+ return hr;
+ }
+
+ ULARGE_INTEGER result;
+ LARGE_INTEGER seek_pos;
+ seek_pos.QuadPart = 0;
+ hr = pages_[page_number]->Seek(seek_pos, STREAM_SEEK_SET, &result);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to Seek page stream " << std::hex << hr;
+ return hr;
+ }
+
+ hr = factory1->CreateGdiMetafile(pages_[page_number].Get(), gdi_metafile);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to CreateGdiMetafile " << std::hex << hr;
+ return hr;
+ }
+ return hr;
+}
+
+} // namespace metro_driver
diff --git a/win8/metro_driver/print_document_source.h b/win8/metro_driver/print_document_source.h
new file mode 100644
index 0000000..fed333e
--- /dev/null
+++ b/win8/metro_driver/print_document_source.h
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_PRINT_DOCUMENT_SOURCE_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_PRINT_DOCUMENT_SOURCE_H_
+
+#include <documentsource.h>
+#include <printpreview.h>
+#include <windows.graphics.printing.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/waitable_event.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+// Hack to be removed once we don't need to build with an SDK earlier than
+// 8441 where the name of the interface has been changed.
+// TODO(mad): remove once we don't run mixed SDK/OS anymore.
+#ifndef __IPrintPreviewDxgiPackageTarget_FWD_DEFINED__
+typedef IPrintPreviewDXGIPackageTarget IPrintPreviewDxgiPackageTarget;
+#endif
+
+
+namespace base {
+class Lock;
+}; // namespace base
+
+namespace metro_driver {
+
+// This class is given to Metro as a source for print documents.
+// The methodless IPrintDocumentSource interface is used to identify it as such.
+// Then, the other interfaces are used to generate preview and print documents.
+// It also exposes a few methods for the print handler to control the document.
+class PrintDocumentSource
+ : public mswr::RuntimeClass<
+ mswr::RuntimeClassFlags<mswr::WinRtClassicComMix>,
+ wingfx::Printing::IPrintDocumentSource,
+ IPrintDocumentPageSource,
+ IPrintPreviewPageCollection> {
+ public:
+ // A set of interfaces for the DirectX context that our parent owns
+ // and that don't need to change from document to document.
+ struct DirectXContext {
+ DirectXContext() {}
+ DirectXContext(ID3D11Device1* device_3d,
+ ID2D1Factory1* factory_2d,
+ ID2D1Device* device_2d,
+ ID2D1DeviceContext* context_2d,
+ IWICImagingFactory2* factory_wic)
+ : d3d_device(device_3d),
+ d2d_factory(factory_2d),
+ d2d_device(device_2d),
+ d2d_context(context_2d),
+ wic_factory(factory_wic) {
+ }
+ DirectXContext(const DirectXContext& other)
+ : d3d_device(other.d3d_device),
+ d2d_factory(other.d2d_factory),
+ d2d_device(other.d2d_device),
+ d2d_context(other.d2d_context),
+ wic_factory(other.wic_factory) {
+ }
+ mswr::ComPtr<ID3D11Device1> d3d_device;
+ mswr::ComPtr<ID2D1Factory1> d2d_factory;
+ mswr::ComPtr<ID2D1Device> d2d_device;
+ mswr::ComPtr<ID2D1DeviceContext> d2d_context;
+ mswr::ComPtr<IWICImagingFactory2> wic_factory;
+ };
+
+ // Construction / Initialization.
+ explicit PrintDocumentSource();
+ HRESULT RuntimeClassInitialize(const DirectXContext& directx_context,
+ base::Lock* parent_lock);
+ // Aborts any pending asynchronous operation.
+ void Abort();
+
+ // classic COM interface IPrintDocumentPageSource methods
+ STDMETHOD(GetPreviewPageCollection) (
+ IPrintDocumentPackageTarget* package_target,
+ IPrintPreviewPageCollection** page_collection);
+ STDMETHOD(MakeDocument)(IInspectable* options,
+ IPrintDocumentPackageTarget* package_target);
+
+ // classic COM interface IPrintPreviewPageCollection methods
+ STDMETHOD(Paginate)(uint32 page, IInspectable* options);
+ STDMETHOD(MakePage)(uint32 desired_page, float width, float height);
+
+ // If the screen DPI changes, we must be warned here.
+ void ResetDpi(float dpi);
+
+ // When the page count is known, this is called and we can setup our data.
+ void SetPageCount(size_t page_count);
+
+ // Every time a page is ready, this is called and we can read the data if
+ // we were waiting for it, or store it for later use.
+ void AddPage(size_t page_number, IStream* metafile_stream);
+
+ private:
+ // Print the page given in the metafile stream to the given print control.
+ HRESULT PrintPage(ID2D1PrintControl* print_control,
+ ID2D1GdiMetafile* metafile_stream,
+ D2D1_SIZE_F pageSize);
+
+ // Helper function to wait for the page count to be ready.
+ // Returns 0 when aborted.
+ size_t WaitAndGetPageCount();
+
+ // Helper function to wait for a given page to be ready.
+ // Returns S_FALSE when aborted.
+ HRESULT WaitAndGetPage(size_t page_number,
+ ID2D1GdiMetafile** metafile_stream);
+
+ DirectXContext directx_context_;
+
+ // Once page data is available, it's added to this vector.
+ std::vector<mswr::ComPtr<IStream>> pages_;
+
+ // When page count is set, the size of this vector is set to that number.
+ // Then, every time page data is added to pages_, the associated condition
+ // variable in this vector is signaled. This is only filled when we receive
+ // the page count, so we must wait on page_count_ready_ before accessing
+ // the content of this vector.
+ std::vector<scoped_ptr<base::ConditionVariable> > pages_ready_state_;
+
+ // This event is signaled when we receive a page count from Chrome. We should
+ // not receive any page data before the count, so we can check this event
+ // while waiting for pages too, in case we ask for page data before we got
+ // the count, so before we properly initialized pages_ready_state_.
+ base::WaitableEvent page_count_ready_;
+
+ // The preview target interface set from within GetPreviewPageCollection
+ // but used from within MakePage.
+ mswr::ComPtr<IPrintPreviewDxgiPackageTarget> dxgi_preview_target_;
+
+ // Our parent's lock (to make sure it is initialized and destroyed early
+ // enough), which we use to protect access to our data members.
+ base::Lock* parent_lock_;
+
+ // The width/height requested in Paginate and used in MakePage.
+ // TODO(mad): Height is currently not used, and width is only used for
+ // scaling. We need to add a way to specify width and height when we request
+ // pages from Chrome.
+ float height_;
+ float width_;
+
+ // The DPI is set by Windows and we need to give it to DirectX.
+ float dpi_;
+
+ // A flag identiying that we have been aborted. Needed to properly handle
+ // asynchronous callbacks.
+ bool aborted_;
+
+ // TODO(mad): remove once we don't run mixed SDK/OS anymore.
+ bool using_old_preview_interface_;
+};
+
+} // namespace metro_driver
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_PRINT_DOCUMENT_SOURCE_H_
diff --git a/win8/metro_driver/print_handler.cc b/win8/metro_driver/print_handler.cc
new file mode 100644
index 0000000..dce321a
--- /dev/null
+++ b/win8/metro_driver/print_handler.cc
@@ -0,0 +1,487 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "win8/metro_driver/print_handler.h"
+
+#include <windows.graphics.display.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+typedef winfoundtn::ITypedEventHandler<
+ wingfx::Printing::PrintManager*,
+ wingfx::Printing::PrintTaskRequestedEventArgs*> PrintRequestedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ wingfx::Printing::PrintTask*,
+ wingfx::Printing::PrintTaskCompletedEventArgs*> PrintTaskCompletedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ wingfx::Printing::PrintTask*, IInspectable*> PrintTaskInspectableHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ wingfx::Printing::PrintTask*,
+ wingfx::Printing::PrintTaskProgressingEventArgs*>
+ PrintTaskProgressingHandler;
+
+} // namespace
+
+namespace metro_driver {
+
+mswr::ComPtr<PrintDocumentSource> PrintHandler::current_document_source_;
+bool PrintHandler::printing_enabled_ = false;
+base::Lock* PrintHandler::lock_ = NULL;
+base::Thread* PrintHandler::thread_ = NULL;
+
+PrintHandler::PrintHandler() {
+ DCHECK(lock_ == NULL);
+ lock_ = new base::Lock();
+
+ DCHECK(thread_ == NULL);
+ thread_ = new base::Thread("Metro Print Handler");
+ thread_->Start();
+}
+
+PrintHandler::~PrintHandler() {
+ ClearPrintTask();
+ DCHECK(current_document_source_.Get() == NULL);
+
+ // Get all pending tasks to complete cleanly by Stopping the thread.
+ // They should complete quickly since current_document_source_ is NULL.
+ DCHECK(thread_ != NULL);
+ DCHECK(thread_->IsRunning());
+ thread_->Stop();
+ delete thread_;
+ thread_ = NULL;
+
+ DCHECK(lock_ != NULL);
+ delete lock_;
+ lock_ = NULL;
+}
+
+HRESULT PrintHandler::Initialize(winui::Core::ICoreWindow* window) {
+ // Register for Print notifications.
+ mswr::ComPtr<wingfx::Printing::IPrintManagerStatic> print_mgr_static;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Graphics_Printing_PrintManager,
+ print_mgr_static.GetAddressOf());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create PrintManagerStatic " << std::hex << hr;
+ return hr;
+ }
+
+ mswr::ComPtr<wingfx::Printing::IPrintManager> print_mgr;
+ hr = print_mgr_static->GetForCurrentView(&print_mgr);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get PrintManager for current view " << std::hex
+ << hr;
+ return hr;
+ }
+
+ hr = print_mgr->add_PrintTaskRequested(
+ mswr::Callback<PrintRequestedHandler>(
+ this, &PrintHandler::OnPrintRequested).Get(),
+ &print_requested_token_);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to register PrintTaskRequested "
+ << std::hex << hr;
+
+ mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Graphics_Display_DisplayProperties,
+ display_properties.GetAddressOf());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create DisplayPropertiesStatics " << std::hex
+ << hr;
+ return hr;
+ }
+
+ hr = display_properties->add_LogicalDpiChanged(
+ mswr::Callback<
+ wingfx::Display::IDisplayPropertiesEventHandler,
+ PrintHandler>(this, &PrintHandler::LogicalDpiChanged).Get(),
+ &dpi_change_token_);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to register LogicalDpiChanged "
+ << std::hex << hr;
+
+ // This flag adds support for surfaces with a different color channel
+ // ordering than the API default. It is recommended usage, and is required
+ // for compatibility with Direct2D.
+ UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+#if defined(_DEBUG)
+ creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
+#endif
+
+ // This array defines the set of DirectX hardware feature levels we support.
+ // The ordering MUST be preserved. All applications are assumed to support
+ // 9.1 unless otherwise stated by the application, which is not our case.
+ D3D_FEATURE_LEVEL feature_levels[] = {
+ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL_9_1 };
+
+ mswr::ComPtr<ID3D11Device> device;
+ mswr::ComPtr<ID3D11DeviceContext> context;
+ hr = D3D11CreateDevice(
+ NULL, // Specify null to use the default adapter.
+ D3D_DRIVER_TYPE_HARDWARE,
+ 0, // Leave as 0 unless software device.
+ creation_flags,
+ feature_levels,
+ ARRAYSIZE(feature_levels),
+ D3D11_SDK_VERSION, // Must always use this value in Metro apps.
+ &device,
+ NULL, // Returns feature level of device created.
+ &context);
+ if (hr == DXGI_ERROR_UNSUPPORTED) {
+ // The hardware is not supported, try a reference driver instead.
+ hr = D3D11CreateDevice(
+ NULL, // Specify null to use the default adapter.
+ D3D_DRIVER_TYPE_REFERENCE,
+ 0, // Leave as 0 unless software device.
+ creation_flags,
+ feature_levels,
+ ARRAYSIZE(feature_levels),
+ D3D11_SDK_VERSION, // Must always use this value in Metro apps.
+ &device,
+ NULL, // Returns feature level of device created.
+ &context);
+ }
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create D3D11 device/context " << std::hex << hr;
+ return hr;
+ }
+
+ hr = device.As(&directx_context_.d3d_device);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to QI D3D11 device " << std::hex << hr;
+ return hr;
+ }
+
+ D2D1_FACTORY_OPTIONS options;
+ ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
+
+#if defined(_DEBUG)
+ options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
+#endif
+
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
+ __uuidof(ID2D1Factory1),
+ &options,
+ &directx_context_.d2d_factory);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create D2D1 factory " << std::hex << hr;
+ return hr;
+ }
+
+ mswr::ComPtr<IDXGIDevice> dxgi_device;
+ hr = directx_context_.d3d_device.As(&dxgi_device);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to QI for IDXGIDevice " << std::hex << hr;
+ return hr;
+ }
+
+ hr = directx_context_.d2d_factory->CreateDevice(
+ dxgi_device.Get(), &directx_context_.d2d_device);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to Create D2DDevice " << std::hex << hr;
+ return hr;
+ }
+
+ hr = directx_context_.d2d_device->CreateDeviceContext(
+ D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
+ &directx_context_.d2d_context);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to Create D2DDeviceContext " << std::hex << hr;
+ return hr;
+ }
+
+ hr = CoCreateInstance(CLSID_WICImagingFactory,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&directx_context_.wic_factory));
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to CoCreate WICImagingFactory " << std::hex << hr;
+ return hr;
+ }
+ return hr;
+}
+
+void PrintHandler::EnablePrinting(bool printing_enabled) {
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrintHandler::OnEnablePrinting, printing_enabled));
+}
+
+void PrintHandler::SetPageCount(size_t page_count) {
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrintHandler::OnSetPageCount, page_count));
+}
+
+void PrintHandler::AddPage(size_t page_number, IStream* metafile_stream) {
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrintHandler::OnAddPage,
+ page_number,
+ mswr::ComPtr<IStream>(metafile_stream)));
+}
+
+void PrintHandler::ShowPrintUI() {
+ // Post the print UI request over to the metro thread.
+ DCHECK(globals.appview_msg_loop != NULL);
+ bool posted = globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&metro_driver::PrintHandler::OnShowPrintUI));
+ DCHECK(posted);
+}
+
+HRESULT PrintHandler::OnPrintRequested(
+ wingfx::Printing::IPrintManager* print_mgr,
+ wingfx::Printing::IPrintTaskRequestedEventArgs* event_args) {
+ DVLOG(1) << __FUNCTION__;
+
+ HRESULT hr = S_OK;
+ if (printing_enabled_) {
+ mswr::ComPtr<wingfx::Printing::IPrintTaskRequest> print_request;
+ hr = event_args->get_Request(print_request.GetAddressOf());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get the Print Task request " << std::hex << hr;
+ return hr;
+ }
+
+ mswrw::HString title;
+ title.Attach(MakeHString(L"Printing"));
+ hr = print_request->CreatePrintTask(
+ title.Get(),
+ mswr::Callback<
+ wingfx::Printing::IPrintTaskSourceRequestedHandler,
+ PrintHandler>(this, &PrintHandler::OnPrintTaskSourceRequest).Get(),
+ print_task_.GetAddressOf());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create the Print Task " << std::hex << hr;
+ return hr;
+ }
+
+ hr = print_task_->add_Completed(
+ mswr::Callback<PrintTaskCompletedHandler>(
+ this, &PrintHandler::OnCompleted).Get(), &print_completed_token_);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to create the Print Task " << std::hex
+ << hr;
+ }
+ return hr;
+}
+
+HRESULT PrintHandler::OnPrintTaskSourceRequest(
+ wingfx::Printing::IPrintTaskSourceRequestedArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ mswr::ComPtr<PrintDocumentSource> print_document_source;
+ HRESULT hr = mswr::MakeAndInitialize<PrintDocumentSource>(
+ &print_document_source, directx_context_, lock_);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create document source " << std::hex << hr;
+ return hr;
+ }
+
+ print_document_source->ResetDpi(GetLogicalDpi());
+
+ mswr::ComPtr<wingfx::Printing::IPrintDocumentSource> print_source;
+ hr = print_document_source.As(&print_source);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to cast document Source " << std::hex << hr;
+ return hr;
+ }
+
+ hr = args->SetSource(print_source.Get());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to set document Source " << std::hex << hr;
+ return hr;
+ }
+
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrintHandler::SetPrintDocumentSource,
+ print_document_source));
+
+ return hr;
+}
+
+HRESULT PrintHandler::OnCompleted(
+ wingfx::Printing::IPrintTask* task,
+ wingfx::Printing::IPrintTaskCompletedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
+ ClearPrintTask();
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrintHandler::ReleasePrintDocumentSource));
+
+ return S_OK;
+}
+
+void PrintHandler::ClearPrintTask() {
+ if (!print_task_.Get())
+ return;
+
+ HRESULT hr = print_task_->remove_Completed(print_completed_token_);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to remove completed event from Task "
+ << std::hex << hr;
+ print_task_.Reset();
+}
+
+float PrintHandler::GetLogicalDpi() {
+ mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Graphics_Display_DisplayProperties,
+ display_properties.GetAddressOf());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get display properties " << std::hex << hr;
+ return 0.0;
+ }
+
+ FLOAT dpi = 0.0;
+ hr = display_properties->get_LogicalDpi(&dpi);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to get Logical DPI " << std::hex << hr;
+
+ return dpi;
+}
+
+HRESULT PrintHandler::LogicalDpiChanged(IInspectable *sender) {
+ DVLOG(1) << __FUNCTION__;
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&PrintHandler::OnLogicalDpiChanged, GetLogicalDpi()));
+ return S_OK;
+}
+
+void PrintHandler::OnLogicalDpiChanged(float dpi) {
+ DCHECK(MessageLoop::current() == thread_->message_loop());
+ // No need to protect the access to the static variable,
+ // since it's set/released in this same thread.
+ if (current_document_source_.Get() != NULL)
+ current_document_source_->ResetDpi(dpi);
+}
+
+void PrintHandler::SetPrintDocumentSource(
+ const mswr::ComPtr<PrintDocumentSource>& print_document_source) {
+ DCHECK(MessageLoop::current() == thread_->message_loop());
+ DCHECK(current_document_source_.Get() == NULL);
+ {
+ // Protect against the other thread which might try to access it.
+ base::AutoLock lock(*lock_);
+ current_document_source_ = print_document_source;
+ }
+ // Start generating the images to print.
+ // TODO(mad): Use a registered message with more information about the print
+ // request, and at a more appropriate time too, and maybe one page at a time.
+ ::PostMessageW(globals.host_windows.front().first,
+ WM_SYSCOMMAND,
+ IDC_PRINT_TO_DESTINATION,
+ 0);
+}
+
+void PrintHandler::ReleasePrintDocumentSource() {
+ DCHECK(MessageLoop::current() == thread_->message_loop());
+ mswr::ComPtr<PrintDocumentSource> print_document_source;
+ {
+ // Must wait for other thread to be done with the pointer first.
+ base::AutoLock lock(*lock_);
+ current_document_source_.Swap(print_document_source);
+ }
+ // This may happen before we get a chance to set the value.
+ if (print_document_source.Get() != NULL)
+ print_document_source->Abort();
+}
+
+void PrintHandler::OnEnablePrinting(bool printing_enabled) {
+ DCHECK(MessageLoop::current() == thread_->message_loop());
+ base::AutoLock lock(*lock_);
+ printing_enabled_ = printing_enabled;
+ // Don't abort if we are being disabled since we may be finishing a previous
+ // print request which was valid and should be finished. We just need to
+ // prevent any new print requests. And don't assert that we are NOT printing
+ // if we are becoming enabled since we may be finishing a long print while
+ // we got disabled and then enabled again...
+}
+
+void PrintHandler::OnSetPageCount(size_t page_count) {
+ DCHECK(MessageLoop::current() == thread_->message_loop());
+ // No need to protect the access to the static variable,
+ // since it's set/released in this same thread.
+ if (current_document_source_.Get() != NULL)
+ current_document_source_->SetPageCount(page_count);
+}
+
+void PrintHandler::OnAddPage(size_t page_number,
+ mswr::ComPtr<IStream> metafile_stream) {
+ DCHECK(MessageLoop::current() == thread_->message_loop());
+ // No need to protect the access to the static variable,
+ // since it's set/released in this same thread.
+ if (current_document_source_.Get() != NULL)
+ current_document_source_->AddPage(page_number, metafile_stream.Get());
+}
+
+void PrintHandler::OnShowPrintUI() {
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
+ mswr::ComPtr<wingfx::Printing::IPrintManagerStatic> print_mgr_static;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Graphics_Printing_PrintManager,
+ print_mgr_static.GetAddressOf());
+ if (SUCCEEDED(hr)) {
+ DCHECK(print_mgr_static.Get() != NULL);
+ // Note that passing NULL to ShowPrintUIAsync crashes,
+ // so we need to setup a temp pointer.
+ mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> unused_async_op;
+ hr = print_mgr_static->ShowPrintUIAsync(unused_async_op.GetAddressOf());
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to ShowPrintUIAsync "
+ << std::hex << std::showbase << hr;
+ } else {
+ LOG(ERROR) << "Failed to create PrintManagerStatic "
+ << std::hex << std::showbase << hr;
+ }
+}
+
+} // namespace metro_driver
+
+void MetroEnablePrinting(BOOL printing_enabled) {
+ metro_driver::PrintHandler::EnablePrinting(!!printing_enabled);
+}
+
+void MetroSetPrintPageCount(size_t page_count) {
+ DVLOG(1) << __FUNCTION__ << " Page count: " << page_count;
+ metro_driver::PrintHandler::SetPageCount(page_count);
+}
+
+void MetroSetPrintPageContent(size_t page_number,
+ void* data,
+ size_t data_size) {
+ DVLOG(1) << __FUNCTION__ << " Page number: " << page_number;
+ DCHECK(data != NULL);
+ DCHECK(data_size > 0);
+ mswr::ComPtr<IStream> metafile_stream;
+ HRESULT hr = ::CreateStreamOnHGlobal(
+ NULL, TRUE, metafile_stream.GetAddressOf());
+ if (metafile_stream.Get() != NULL) {
+ ULONG bytes_written = 0;
+ hr = metafile_stream->Write(data, data_size, &bytes_written);
+ LOG_IF(ERROR, FAILED(hr)) << "Failed to Write to Stream " << std::hex << hr;
+ DCHECK(bytes_written == data_size);
+
+ metro_driver::PrintHandler::AddPage(page_number, metafile_stream.Get());
+ } else {
+ NOTREACHED() << "Failed to CreateStreamOnHGlobal " << std::hex << hr;
+ }
+}
+
+void MetroShowPrintUI() {
+ metro_driver::PrintHandler::ShowPrintUI();
+}
diff --git a/win8/metro_driver/print_handler.h b/win8/metro_driver/print_handler.h
new file mode 100644
index 0000000..f0779cf
--- /dev/null
+++ b/win8/metro_driver/print_handler.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_PRINT_HANDLER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_PRINT_HANDLER_H_
+
+#include <windows.media.playto.h>
+#include <windows.graphics.printing.h>
+#include <windows.ui.core.h>
+
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "win8/metro_driver/print_document_source.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace base {
+
+class Lock;
+
+} // namespace base
+
+namespace metro_driver {
+
+// This class handles the print aspect of the devices charm.
+class PrintHandler {
+ public:
+ PrintHandler();
+ ~PrintHandler();
+
+ HRESULT Initialize(winui::Core::ICoreWindow* window);
+
+ // Called by the exported C functions.
+ static void EnablePrinting(bool printing_enabled);
+ static void SetPageCount(size_t page_count);
+ static void AddPage(size_t page_number, IStream* metafile_stream);
+ static void ShowPrintUI();
+
+ private:
+ // Callbacks from Metro.
+ HRESULT OnPrintRequested(
+ wingfx::Printing::IPrintManager* print_mgr,
+ wingfx::Printing::IPrintTaskRequestedEventArgs* event_args);
+
+ HRESULT OnPrintTaskSourceRequest(
+ wingfx::Printing::IPrintTaskSourceRequestedArgs* args);
+
+ HRESULT OnCompleted(wingfx::Printing::IPrintTask* task,
+ wingfx::Printing::IPrintTaskCompletedEventArgs* args);
+ // Utility methods.
+ void ClearPrintTask();
+ float GetLogicalDpi();
+
+ // Callback from Metro and entry point called on lockable thread.
+ HRESULT LogicalDpiChanged(IInspectable *sender);
+ static void OnLogicalDpiChanged(float dpi);
+
+ // Called on the lockable thread to set/release the doc.
+ static void SetPrintDocumentSource(
+ const mswr::ComPtr<PrintDocumentSource>& print_document_source);
+ static void ReleasePrintDocumentSource();
+
+ // Called on the lockable thread for the exported C functions.
+ static void OnEnablePrinting(bool printing_enabled);
+ static void OnSetPageCount(size_t page_count);
+ static void OnAddPage(size_t page_number,
+ mswr::ComPtr<IStream> metafile_stream);
+
+ // Opens the prit device charm. Must be called from the metro thread.
+ static void OnShowPrintUI();
+
+ mswr::ComPtr<wingfx::Printing::IPrintTask> print_task_;
+ EventRegistrationToken print_requested_token_;
+ EventRegistrationToken print_completed_token_;
+ EventRegistrationToken dpi_change_token_;
+
+ mswr::ComPtr<wingfx::Printing::IPrintManager> print_manager_;
+ PrintDocumentSource::DirectXContext directx_context_;
+
+ // Hack to give access to the Print Document from the C style entry points.
+ // This will go away once we can pass a pointer to this interface down to
+ // the Chrome Browser as we send the command to print.
+ static mswr::ComPtr<PrintDocumentSource> current_document_source_;
+
+ // Another hack to enable/disable printing from an exported C function.
+ // TODO(mad): Find a better way to do this...
+ static bool printing_enabled_;
+
+ // This is also a temporary hack until we can pass down the print document
+ // to Chrome so it can call directly into it. We need to lock the access to
+ // current_document_source_.
+ static base::Lock* lock_;
+
+ // This thread is used to send blocking jobs
+ // out of threads we don't want to block.
+ static base::Thread* thread_;
+};
+
+} // namespace metro_driver
+
+// Exported C functions for Chrome to call into the Metro module.
+extern "C" __declspec(dllexport)
+void MetroEnablePrinting(BOOL printing_enabled);
+
+extern "C" __declspec(dllexport)
+void MetroSetPrintPageCount(size_t page_count);
+
+extern "C" __declspec(dllexport)
+void MetroSetPrintPageContent(size_t current_page,
+ void* data,
+ size_t data_size);
+
+extern "C" __declspec(dllexport)
+void MetroShowPrintUI();
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_PRINT_HANDLER_H_
diff --git a/win8/metro_driver/run_all_unittests.cc b/win8/metro_driver/run_all_unittests.cc
new file mode 100644
index 0000000..4c6a548
--- /dev/null
+++ b/win8/metro_driver/run_all_unittests.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 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 "stdafx.h"
+
+#include "base/at_exit.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#pragma comment(lib, "runtimeobject.lib")
+
+base::AtExitManager at_exit;
+
+int main(int argc, char** argv) {
+ mswrw::RoInitializeWrapper ro_init(RO_INIT_SINGLETHREADED);
+
+ testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/win8/metro_driver/secondary_tile.cc b/win8/metro_driver/secondary_tile.cc
new file mode 100644
index 0000000..8a54bd7
--- /dev/null
+++ b/win8/metro_driver/secondary_tile.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "secondary_tile.h"
+
+#include <windows.ui.startscreen.h>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "crypto/sha2.h"
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+string16 GenerateTileId(const string16& url_str) {
+ uint8 hash[crypto::kSHA256Length];
+ crypto::SHA256HashString(UTF16ToUTF8(url_str), hash, sizeof(hash));
+ std::string hash_str = base::HexEncode(hash, sizeof(hash));
+ return UTF8ToUTF16(hash_str);
+}
+
+string16 GetLogoUrlString() {
+ FilePath module_path;
+ PathService::Get(base::DIR_MODULE, &module_path);
+ string16 scheme(L"ms-appx:///");
+ return scheme.append(module_path.BaseName().value())
+ .append(L"/SecondaryTile.png");
+}
+
+BOOL IsPinnedToStartScreen(const string16& url_str) {
+ mswr::ComPtr<winui::StartScreen::ISecondaryTileStatics> tile_statics;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
+ tile_statics.GetAddressOf());
+ CheckHR(hr, "Failed to create instance of ISecondaryTileStatics");
+
+ boolean exists;
+ hr = tile_statics->Exists(MakeHString(GenerateTileId(url_str)), &exists);
+ CheckHR(hr, "ISecondaryTileStatics.Exists failed");
+ return exists;
+}
+
+void DeleteTileFromStartScreen(const string16& url_str) {
+ DVLOG(1) << __FUNCTION__;
+ mswr::ComPtr<winui::StartScreen::ISecondaryTileFactory> tile_factory;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
+ tile_factory.GetAddressOf());
+ CheckHR(hr, "Failed to create instance of ISecondaryTileFactory");
+
+ mswrw::HString id;
+ id.Attach(MakeHString(GenerateTileId(url_str)));
+
+ mswr::ComPtr<winui::StartScreen::ISecondaryTile> tile;
+ hr = tile_factory->CreateWithId(id.Get(), tile.GetAddressOf());
+ CheckHR(hr, "Failed to create tile");
+
+ mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> completion;
+ hr = tile->RequestDeleteAsync(completion.GetAddressOf());
+ CheckHR(hr, "RequestDeleteAsync failed");
+
+ typedef winfoundtn::IAsyncOperationCompletedHandler<bool> RequestDoneType;
+ mswr::ComPtr<RequestDoneType> handler(mswr::Callback<RequestDoneType>(
+ globals.view, &ChromeAppView::TileRequestCreateDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+ CheckHR(hr, "Failed to put_Completed");
+}
+
+void CreateTileOnStartScreen(const string16& title_str,
+ const string16& url_str) {
+ VLOG(1) << __FUNCTION__;
+ mswr::ComPtr<winui::StartScreen::ISecondaryTileFactory> tile_factory;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
+ tile_factory.GetAddressOf());
+ CheckHR(hr, "Failed to create instance of ISecondaryTileFactory");
+
+ winui::StartScreen::TileOptions options =
+ winui::StartScreen::TileOptions_ShowNameOnLogo;
+ mswrw::HString title;
+ title.Attach(MakeHString(title_str));
+ mswrw::HString id;
+ id.Attach(MakeHString(GenerateTileId(url_str)));
+ mswrw::HString args;
+ args.Attach(MakeHString(string16(L"url=").append(url_str)));
+
+ mswr::ComPtr<winfoundtn::IUriRuntimeClassFactory> uri_factory;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Foundation_Uri,
+ uri_factory.GetAddressOf());
+ CheckHR(hr, "Failed to create URIFactory");
+
+ mswrw::HString logo_url;
+ logo_url.Attach(MakeHString(GetLogoUrlString()));
+ mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
+ hr = uri_factory->CreateUri(logo_url.Get(), &uri);
+ CheckHR(hr, "Failed to create URI");
+
+ mswr::ComPtr<winui::StartScreen::ISecondaryTile> tile;
+ hr = tile_factory->CreateTile(id.Get(),
+ title.Get(),
+ title.Get(),
+ args.Get(),
+ options,
+ uri.Get(),
+ tile.GetAddressOf());
+ CheckHR(hr, "Failed to create tile");
+
+ hr = tile->put_ForegroundText(winui::StartScreen::ForegroundText_Light);
+ CheckHR(hr, "Failed to change foreground text color");
+
+ mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> completion;
+ hr = tile->RequestCreateAsync(completion.GetAddressOf());
+ CheckHR(hr, "RequestCreateAsync failed");
+
+ typedef winfoundtn::IAsyncOperationCompletedHandler<bool> RequestDoneType;
+ mswr::ComPtr<RequestDoneType> handler(mswr::Callback<RequestDoneType>(
+ globals.view, &ChromeAppView::TileRequestCreateDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+ CheckHR(hr, "Failed to put_Completed");
+}
+
+void TogglePinnedToStartScreen(const string16& title_str,
+ const string16& url_str) {
+ if (IsPinnedToStartScreen(url_str)) {
+ DeleteTileFromStartScreen(url_str);
+ return;
+ }
+
+ CreateTileOnStartScreen(title_str, url_str);
+}
+
+} // namespace
+
+BOOL MetroIsPinnedToStartScreen(const string16& url) {
+ VLOG(1) << __FUNCTION__ << " url: " << url;
+ return IsPinnedToStartScreen(url);
+}
+
+void MetroTogglePinnedToStartScreen(const string16& title,
+ const string16& url) {
+ DVLOG(1) << __FUNCTION__ << " title:" << title << " url: " << url;
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&TogglePinnedToStartScreen, title, url));
+}
diff --git a/win8/metro_driver/secondary_tile.h b/win8/metro_driver/secondary_tile.h
new file mode 100644
index 0000000..796e881
--- /dev/null
+++ b/win8/metro_driver/secondary_tile.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_SECONDARY_TILE_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_SECONDARY_TILE_H_
+
+#include "base/string16.h"
+
+extern "C" __declspec(dllexport)
+BOOL MetroIsPinnedToStartScreen(const string16& url);
+
+extern "C" __declspec(dllexport)
+void MetroTogglePinnedToStartScreen(const string16& title, const string16& url);
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_SECONDARY_TILE_H_
+
diff --git a/win8/metro_driver/settings_handler.cc b/win8/metro_driver/settings_handler.cc
new file mode 100644
index 0000000..6feae24
--- /dev/null
+++ b/win8/metro_driver/settings_handler.cc
@@ -0,0 +1,175 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "settings_handler.h"
+
+// This include allows to send WM_SYSCOMMANDs to chrome.
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome_app_view.h"
+#include "winrt_utils.h"
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::ApplicationSettings::SettingsPane*,
+ winui::ApplicationSettings::SettingsPaneCommandsRequestedEventArgs*>
+ CommandsRequestedHandler;
+
+namespace {
+
+// String identifiers for the settings pane commands.
+const wchar_t* kSettingsId = L"settings";
+const wchar_t* kHelpId = L"help";
+const wchar_t* kAboutId = L"about";
+
+}
+
+SettingsHandler::SettingsHandler() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+SettingsHandler::~SettingsHandler() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+HRESULT SettingsHandler::Initialize() {
+ mswr::ComPtr<winui::ApplicationSettings::ISettingsPaneStatics>
+ settings_pane_statics;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_ApplicationSettings_SettingsPane,
+ settings_pane_statics.GetAddressOf());
+ CheckHR(hr, "Failed to activate ISettingsPaneStatics");
+
+ mswr::ComPtr<winui::ApplicationSettings::ISettingsPane> settings_pane;
+ hr = settings_pane_statics->GetForCurrentView(&settings_pane);
+ CheckHR(hr, "Failed to get ISettingsPane");
+
+ hr = settings_pane->add_CommandsRequested(
+ mswr::Callback<CommandsRequestedHandler>(
+ this,
+ &SettingsHandler::OnSettingsCommandsRequested).Get(),
+ &settings_token_);
+ CheckHR(hr, "Failed to add CommandsRequested");
+
+ return hr;
+}
+
+HRESULT SettingsHandler::OnSettingsCommandsRequested(
+ winui::ApplicationSettings::ISettingsPane* settings_pane,
+ winui::ApplicationSettings::ISettingsPaneCommandsRequestedEventArgs* args) {
+ mswr::ComPtr<winui::ApplicationSettings::ISettingsCommandFactory>
+ settings_command_factory;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_ApplicationSettings_SettingsCommand,
+ settings_command_factory.GetAddressOf());
+ CheckHR(hr, "Failed to activate ISettingsCommandFactory");
+
+ mswr::ComPtr<winui::ApplicationSettings::ISettingsPaneCommandsRequest>
+ settings_command_request;
+ hr = args->get_Request(&settings_command_request);
+ CheckHR(hr, "Failed to get_Request");
+
+ mswr::ComPtr<SettingsHandler::ISettingsCommandVector> application_commands;
+ hr = settings_command_request->get_ApplicationCommands(&application_commands);
+ CheckHR(hr, "Failed to get_ApplicationCommands");
+
+ // TODO(mad): Internationalize the hard coded user visible strings.
+ hr = AppendNewSettingsCommand(
+ kSettingsId, L"Settings", settings_command_factory.Get(),
+ application_commands.Get());
+ CheckHR(hr, "Failed to append new settings command");
+
+ hr = AppendNewSettingsCommand(
+ kHelpId, L"Help", settings_command_factory.Get(),
+ application_commands.Get());
+ CheckHR(hr, "Failed to append new help command");
+
+ hr = AppendNewSettingsCommand(
+ kAboutId, L"About", settings_command_factory.Get(),
+ application_commands.Get());
+ CheckHR(hr, "Failed to append new about command");
+
+ return hr;
+}
+
+HRESULT SettingsHandler::AppendNewSettingsCommand(
+ const wchar_t* id,
+ const wchar_t* name,
+ winui::ApplicationSettings::ISettingsCommandFactory*
+ settings_command_factory,
+ SettingsHandler::ISettingsCommandVector* settings_command_vector) {
+ mswr::ComPtr<winfoundtn::IPropertyValue> settings_id;
+ HRESULT hr = GetSettingsId(id, &settings_id);
+ CheckHR(hr, "Can't get settings id");
+
+ mswrw::HString settings_name;
+ settings_name.Attach(MakeHString(name));
+ mswr::ComPtr<winui::Popups::IUICommand> command;
+ hr = settings_command_factory->CreateSettingsCommand(
+ settings_id.Get(),
+ settings_name.Get(),
+ mswr::Callback<winui::Popups::IUICommandInvokedHandler>(
+ &SettingsHandler::OnSettings).Get(),
+ command.GetAddressOf());
+ CheckHR(hr, "Can't create settings command");
+
+ hr = settings_command_vector->Append(command.Get());
+ CheckHR(hr, "Failed to append settings command");
+
+ return hr;
+}
+
+HRESULT SettingsHandler::OnSettings(winui::Popups::IUICommand* command) {
+ mswr::ComPtr<winfoundtn::IPropertyValue> settings_id;
+ HRESULT hr = GetSettingsId(kSettingsId, &settings_id);
+ CheckHR(hr, "Failed to get settings id");
+
+ mswr::ComPtr<winfoundtn::IPropertyValue> help_id;
+ hr = GetSettingsId(kHelpId, &help_id);
+ CheckHR(hr, "Failed to get settings id");
+
+ mswr::ComPtr<winfoundtn::IPropertyValue> about_id;
+ hr = GetSettingsId(kAboutId, &about_id);
+ CheckHR(hr, "Failed to get settings id");
+
+ mswr::ComPtr<winfoundtn::IPropertyValue> command_id;
+ hr = command->get_Id(&command_id);
+ CheckHR(hr, "Failed to get command id");
+
+ INT32 result = -1;
+ hr = winrt_utils::CompareProperties(
+ command_id.Get(), settings_id.Get(), &result);
+ CheckHR(hr, "Failed to compare ids");
+
+ HWND chrome_window = globals.host_windows.front().first;
+
+ if (result == 0) {
+ ::PostMessageW(chrome_window, WM_SYSCOMMAND, IDC_OPTIONS, 0);
+ return S_OK;
+ }
+
+ hr = winrt_utils::CompareProperties(command_id.Get(), help_id.Get(), &result);
+ CheckHR(hr, "Failed to compare ids");
+ if (result == 0) {
+ ::PostMessageW(chrome_window, WM_SYSCOMMAND, IDC_HELP_PAGE_VIA_MENU, 0);
+ return S_OK;
+ }
+
+ hr = winrt_utils::CompareProperties(
+ command_id.Get(), about_id.Get(), &result);
+ CheckHR(hr, "Failed to compare ids");
+ if (result == 0) {
+ ::PostMessageW(chrome_window, WM_SYSCOMMAND, IDC_ABOUT, 0);
+ return S_OK;
+ }
+
+ return S_OK;
+}
+
+HRESULT SettingsHandler::GetSettingsId(
+ const wchar_t* value, winfoundtn::IPropertyValue** settings_id) {
+ mswrw::HString property_value_string;
+ property_value_string.Attach(MakeHString(value));
+ return winrt_utils::CreateStringProperty(property_value_string.Get(),
+ settings_id);
+}
diff --git a/win8/metro_driver/settings_handler.h b/win8/metro_driver/settings_handler.h
new file mode 100644
index 0000000..5b234ad
--- /dev/null
+++ b/win8/metro_driver/settings_handler.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_SETTINGS_HANDLER_H_
+
+#include <windows.ui.applicationsettings.h>
+#include <windows.ui.popups.h>
+
+#include "winrt_utils.h"
+
+// This class handles the settings charm.
+class SettingsHandler {
+ public:
+ SettingsHandler();
+ ~SettingsHandler();
+
+ HRESULT Initialize();
+
+ private:
+ typedef winfoundtn::Collections::IVector<
+ winui::ApplicationSettings::SettingsCommand*> ISettingsCommandVector;
+
+ HRESULT OnSettingsCommandsRequested(
+ winui::ApplicationSettings::ISettingsPane* settings_pane,
+ winui::ApplicationSettings::
+ ISettingsPaneCommandsRequestedEventArgs* args);
+
+ HRESULT AppendNewSettingsCommand(
+ const wchar_t* id,
+ const wchar_t* name,
+ winui::ApplicationSettings::ISettingsCommandFactory*
+ settings_command_factory,
+ ISettingsCommandVector* settings_command_vector);
+
+ static HRESULT OnSettings(winui::Popups::IUICommand* command);
+ static HRESULT GetSettingsId(const wchar_t* value,
+ winfoundtn::IPropertyValue** settings_id);
+
+ EventRegistrationToken settings_token_;
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_SETTINGS_HANDLER_H_
diff --git a/win8/metro_driver/stdafx.h b/win8/metro_driver/stdafx.h
new file mode 100644
index 0000000..d8c1a01
--- /dev/null
+++ b/win8/metro_driver/stdafx.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_STDAFX_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_STDAFX_H_
+
+#include <wrl\implements.h>
+#include <wrl\module.h>
+#include <wrl\event.h>
+#include <wrl\wrappers\corewrappers.h>
+
+#include <activation.h>
+#include <d2d1_1.h>
+#include <d3d11_1.h>
+#include <roapi.h>
+#include <stdio.h>
+#include <wincodec.h>
+#include <windows.h>
+
+#include <windows.applicationmodel.core.h>
+#include <windows.applicationModel.datatransfer.h>
+#include <windows.graphics.printing.h>
+#include <windows.ui.notifications.h>
+
+namespace mswr = Microsoft::WRL;
+namespace mswrw = Microsoft::WRL::Wrappers;
+namespace winapp = ABI::Windows::ApplicationModel;
+namespace windata = ABI::Windows::Data;
+namespace winfoundtn = ABI::Windows::Foundation;
+namespace wingfx = ABI::Windows::Graphics;
+namespace winui = ABI::Windows::UI;
+namespace winxml = windata::Xml;
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_STDAFX_H_
diff --git a/win8/metro_driver/toast_notification_handler.cc b/win8/metro_driver/toast_notification_handler.cc
new file mode 100644
index 0000000..36bddd5
--- /dev/null
+++ b/win8/metro_driver/toast_notification_handler.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2012 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 <string>
+
+#include "win8/metro_driver/stdafx.h"
+#include "win8/metro_driver/toast_notification_handler.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/utf_string_conversions.h"
+// TODO(ananta)
+// Refactor the chrome_util and shell_util code from chrome into a common lib
+#include "win8/delegate_execute/chrome_util.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Notifications::ToastNotification*, IInspectable*>
+ ToastActivationHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Notifications::ToastNotification*,
+ winui::Notifications::ToastDismissedEventArgs*> ToastDismissedHandler;
+
+namespace {
+
+// Helper function to return the text node root identified by the index passed
+// in.
+HRESULT GetTextNodeRoot(
+ unsigned int index,
+ winxml::Dom::IXmlDocument* xml_doc,
+ winxml::Dom::IXmlNode** node) {
+ DCHECK(xml_doc);
+ DCHECK(node);
+
+ mswr::ComPtr<winxml::Dom::IXmlElement> document_element;
+ HRESULT hr = xml_doc->get_DocumentElement(&document_element);
+ CheckHR(hr);
+
+ mswr::ComPtr<winxml::Dom::IXmlNodeList> elements;
+ mswrw::HString tag_name;
+ tag_name.Attach(MakeHString(L"text"));
+ hr = document_element->GetElementsByTagName(tag_name.Get(),
+ &elements);
+ CheckHR(hr);
+
+ unsigned int count = 0;
+ elements->get_Length(&count);
+
+ if (index > count) {
+ DVLOG(1) << "Invalid text node index passed in : " << index;
+ return E_FAIL;
+ }
+ hr = elements->Item(index, node);
+ CheckHR(hr);
+ return hr;
+}
+
+// Helper function to append a text element to the text section in the
+// XML document passed in.
+// The index parameter identifies which text node we append to.
+HRESULT CreateTextNode(winxml::Dom::IXmlDocument* xml_doc,
+ int index,
+ const string16& text_string) {
+ DCHECK(xml_doc);
+
+ mswr::ComPtr<winxml::Dom::IXmlElement> document_element;
+ HRESULT hr = xml_doc->get_DocumentElement(&document_element);
+ CheckHR(hr);
+
+ mswr::ComPtr<winxml::Dom::IXmlText> xml_text_node;
+ mswrw::HString data_hstring;
+ data_hstring.Attach(MakeHString(text_string.c_str()));
+ hr = xml_doc->CreateTextNode(data_hstring.Get(), &xml_text_node);
+ CheckHR(hr);
+
+ mswr::ComPtr<winxml::Dom::IXmlNode> created_node;
+ hr = xml_text_node.CopyTo(
+ winxml::Dom::IID_IXmlNode,
+ reinterpret_cast<void**>(created_node.GetAddressOf()));
+ CheckHR(hr);
+
+ mswr::ComPtr<winxml::Dom::IXmlNode> text_node_root;
+ hr = GetTextNodeRoot(index, xml_doc, &text_node_root);
+ CheckHR(hr);
+
+ mswr::ComPtr<winxml::Dom::IXmlNode> appended_node;
+ hr = text_node_root->AppendChild(created_node.Get(), &appended_node);
+ CheckHR(hr);
+ return hr;
+}
+
+} // namespace
+
+ToastNotificationHandler::DesktopNotification::DesktopNotification(
+ const char* notification_origin,
+ const char* notification_icon,
+ const wchar_t* notification_title,
+ const wchar_t* notification_body,
+ const wchar_t* notification_display_source,
+ const char* notification_id)
+ : origin_url(notification_origin),
+ icon_url(notification_icon),
+ title(notification_title),
+ body(notification_body),
+ display_source(notification_display_source),
+ id(notification_id) {
+}
+
+
+ToastNotificationHandler::ToastNotificationHandler() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+ToastNotificationHandler::~ToastNotificationHandler() {
+ DVLOG(1) << __FUNCTION__;
+
+ if (notifier_ && notification_)
+ CancelNotification();
+}
+
+void ToastNotificationHandler::DisplayNotification(
+ const DesktopNotification& notification) {
+ DVLOG(1) << __FUNCTION__;
+
+ DCHECK(notifier_.Get() == NULL);
+ DCHECK(notification_.Get() == NULL);
+
+ mswr::ComPtr<winui::Notifications::IToastNotificationManagerStatics>
+ toast_manager;
+
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_Notifications_ToastNotificationManager,
+ toast_manager.GetAddressOf());
+ CheckHR(hr);
+
+ mswr::ComPtr<winxml::Dom::IXmlDocument> toast_xml;
+ hr = toast_manager->GetTemplateContent(
+ winui::Notifications::ToastTemplateType_ToastText02,
+ &toast_xml);
+ CheckHR(hr);
+
+ if (!toast_xml)
+ return;
+
+ mswr::ComPtr<winxml::Dom::IXmlElement> document_element;
+ hr = toast_xml->get_DocumentElement(&document_element);
+ CheckHR(hr);
+
+ if (!document_element)
+ return;
+
+ hr = CreateTextNode(toast_xml.Get(), 0, notification.title);
+ CheckHR(hr);
+
+ hr = CreateTextNode(toast_xml.Get(), 1, notification.body);
+ CheckHR(hr);
+
+ mswrw::HString duration_attribute_name;
+ duration_attribute_name.Attach(MakeHString(L"duration"));
+ mswrw::HString duration_attribute_value;
+ duration_attribute_value.Attach(MakeHString(L"long"));
+
+ hr = document_element->SetAttribute(duration_attribute_name.Get(),
+ duration_attribute_value.Get());
+ CheckHR(hr);
+
+ // TODO(ananta)
+ // We should set the image and launch params attribute in the notification
+ // XNL as described here: http://msdn.microsoft.com/en-us/library/hh465448
+ // To set the image we may have to extract the image and specify it in the
+ // following url form. ms-appx:///images/foo.png
+ // The launch params as described don't get passed back to us via the
+ // winapp::Activation::ILaunchActivatedEventArgs argument. Needs to be
+ // investigated.
+ mswr::ComPtr<winui::Notifications::IToastNotificationFactory>
+ toast_notification_factory;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_Notifications_ToastNotification,
+ toast_notification_factory.GetAddressOf());
+ CheckHR(hr);
+
+ hr = toast_notification_factory->CreateToastNotification(
+ toast_xml.Get(), &notification_);
+ CheckHR(hr);
+
+ FilePath chrome_path;
+ if (!PathService::Get(base::FILE_EXE, &chrome_path)) {
+ NOTREACHED() << "Failed to get chrome exe path";
+ return;
+ }
+ string16 appid = delegate_execute::GetAppId(chrome_path);
+ DVLOG(1) << "Chrome Appid is " << appid.c_str();
+
+ // TODO(ananta)
+ // We should probably use BrowserDistribution here to get the product name.
+ mswrw::HString app_user_model_id;
+ app_user_model_id.Attach(MakeHString(appid));
+
+ hr = toast_manager->CreateToastNotifierWithId(app_user_model_id.Get(),
+ &notifier_);
+ CheckHR(hr);
+
+ hr = notification_->add_Activated(
+ mswr::Callback<ToastActivationHandler>(
+ this, &ToastNotificationHandler::OnActivate).Get(),
+ &activated_token_);
+ CheckHR(hr);
+
+ hr = notifier_->Show(notification_.Get());
+ CheckHR(hr);
+}
+
+void ToastNotificationHandler::CancelNotification() {
+ DVLOG(1) << __FUNCTION__;
+
+ DCHECK(notifier_);
+ DCHECK(notification_);
+
+ notifier_->Hide(notification_.Get());
+}
+
+HRESULT ToastNotificationHandler::OnActivate(
+ winui::Notifications::IToastNotification* notification,
+ IInspectable* inspectable) {
+ // TODO(ananta)
+ // We should pass back information from the notification like the source url
+ // etc to ChromeAppView which would enable it to ensure that the
+ // correct tab in chrome is activated.
+ DVLOG(1) << __FUNCTION__;
+ return S_OK;
+}
diff --git a/win8/metro_driver/toast_notification_handler.h b/win8/metro_driver/toast_notification_handler.h
new file mode 100644
index 0000000..f560e34
--- /dev/null
+++ b/win8/metro_driver/toast_notification_handler.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_TOAST_NOTIFICATION_HANDLER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_TOAST_NOTIFICATION_HANDLER_H_
+
+#include <windows.ui.notifications.h>
+
+#include "base/string16.h"
+
+// Provides functionality to display a metro style toast notification.
+class ToastNotificationHandler {
+ public:
+ // Holds information about a desktop notification to be displayed.
+ struct DesktopNotification {
+ std::string origin_url;
+ std::string icon_url;
+ string16 title;
+ string16 body;
+ string16 display_source;
+ std::string id;
+
+ DesktopNotification(const char* notification_origin,
+ const char* notification_icon,
+ const wchar_t* notification_title,
+ const wchar_t* notification_body,
+ const wchar_t* notification_display_source,
+ const char* notification_id);
+ };
+
+ ToastNotificationHandler();
+ ~ToastNotificationHandler();
+
+ void DisplayNotification(const DesktopNotification& notification);
+ void CancelNotification();
+
+ HRESULT OnActivate(winui::Notifications::IToastNotification* notification,
+ IInspectable* inspectable);
+
+ private:
+ mswr::ComPtr<winui::Notifications::IToastNotifier> notifier_;
+ mswr::ComPtr<winui::Notifications::IToastNotification> notification_;
+
+ EventRegistrationToken activated_token_;
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_TOAST_NOTIFICATION_HANDLER_H_
diff --git a/win8/metro_driver/winrt_utils.cc b/win8/metro_driver/winrt_utils.cc
new file mode 100644
index 0000000..cfddc5e
--- /dev/null
+++ b/win8/metro_driver/winrt_utils.cc
@@ -0,0 +1,225 @@
+// Copyright (c) 2012 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 "stdafx.h"
+#include "winrt_utils.h"
+
+#include <shlobj.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_comptr.h"
+
+void CheckHR(HRESULT hr, const char* message) {
+ if (FAILED(hr)) {
+ if (message)
+ PLOG(DFATAL) << message << ", hr = " << std::hex << hr;
+ else
+ PLOG(DFATAL) << "COM ERROR" << ", hr = " << std::hex << hr;
+ }
+}
+
+HSTRING MakeHString(const string16& str) {
+ HSTRING hstr;
+ if (FAILED(::WindowsCreateString(str.c_str(), str.size(), &hstr))) {
+ PLOG(DFATAL) << "Hstring creation failed";
+ }
+ return hstr;
+}
+
+string16 MakeStdWString(HSTRING hstring) {
+ const wchar_t* str;
+ UINT32 size = 0;
+ str = ::WindowsGetStringRawBuffer(hstring, &size);
+ if (!size)
+ return string16();
+ return string16(str, size);
+}
+
+namespace {
+
+#define IMPLEMENT_CREATE_PROPERTY(Name, Type) \
+HRESULT Create ## Name ## Property(Type value, \
+ winfoundtn::IPropertyValue** prop) { \
+ mswr::ComPtr<winfoundtn::IPropertyValueStatics> property_value_statics; \
+ HRESULT hr = winrt_utils::CreateActivationFactory( \
+ RuntimeClass_Windows_Foundation_PropertyValue, \
+ property_value_statics.GetAddressOf()); \
+ CheckHR(hr, "Can't create IPropertyValueStatics"); \
+ hr = property_value_statics->Create ## Name ## ( \
+ value, \
+ reinterpret_cast<IInspectable**>(prop)); \
+ CheckHR(hr, "Failed to create Property"); \
+ return hr; \
+}
+
+#define COMPARE_ATOMIC_PROPERTY_VALUES(Name, Type) \
+ Type lhs_value; \
+ hr = lhs->Get ## Name ##(&lhs_value); \
+ CheckHR(hr, "Can't get value for lhs"); \
+ Type rhs_value; \
+ hr = rhs->Get ## Name ##(&rhs_value); \
+ CheckHR(hr, "Can't get value for rhs"); \
+ if (lhs_value < rhs_value) \
+ *result = -1; \
+ else if (lhs_value > rhs_value) \
+ *result = 1; \
+ else \
+ *result = 0; \
+ hr = S_OK
+
+} // namespace
+
+namespace winrt_utils {
+
+IMPLEMENT_CREATE_PROPERTY(String, HSTRING);
+IMPLEMENT_CREATE_PROPERTY(Int16, INT16);
+IMPLEMENT_CREATE_PROPERTY(Int32, INT32);
+IMPLEMENT_CREATE_PROPERTY(Int64, INT64);
+IMPLEMENT_CREATE_PROPERTY(UInt8, UINT8);
+IMPLEMENT_CREATE_PROPERTY(UInt16, UINT16);
+IMPLEMENT_CREATE_PROPERTY(UInt32, UINT32);
+IMPLEMENT_CREATE_PROPERTY(UInt64, UINT64);
+
+HRESULT CompareProperties(winfoundtn::IPropertyValue* lhs,
+ winfoundtn::IPropertyValue* rhs,
+ INT32* result) {
+ if (result == nullptr) {
+ PLOG(DFATAL) << "Invalid argument to CompareProperties.";
+ return E_INVALIDARG;
+ }
+
+ if (lhs == rhs) {
+ *result = 0;
+ return S_OK;
+ }
+
+ winfoundtn::PropertyType lhs_property_type;
+ HRESULT hr = lhs->get_Type(&lhs_property_type);
+ if (FAILED(hr)) {
+ PLOG(DFATAL) << "Can't get property type for lhs, hr=" << std::hex << hr;
+ }
+
+ winfoundtn::PropertyType rhs_property_type;
+ hr = rhs->get_Type(&rhs_property_type);
+ CheckHR(hr, "Can't get property type for rhs");
+
+ if (lhs_property_type != rhs_property_type)
+ return E_INVALIDARG;
+
+ switch (lhs_property_type) {
+ case winfoundtn::PropertyType::PropertyType_String: {
+ mswrw::HString lhs_string;
+ hr = lhs->GetString(lhs_string.GetAddressOf());
+ CheckHR(hr, "Can't get string for lhs");
+
+ mswrw::HString rhs_string;
+ hr = rhs->GetString(rhs_string.GetAddressOf());
+ CheckHR(hr, "Can't get string for rhs");
+
+ hr = WindowsCompareStringOrdinal(
+ lhs_string.Get(), rhs_string.Get(), result);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_Char16: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(Char16, wchar_t);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_Double: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(Double, double);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_Int16: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(Int16, INT16);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_Int32: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(Int32, INT32);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_Int64: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(Int64, INT64);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_UInt8: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(UInt8, UINT8);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_UInt16: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(UInt16, UINT16);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_UInt32: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(UInt32, UINT32);
+ break;
+ }
+ case winfoundtn::PropertyType::PropertyType_UInt64: {
+ COMPARE_ATOMIC_PROPERTY_VALUES(UInt64, UINT64);
+ break;
+ }
+ default: {
+ hr = E_NOTIMPL;
+ }
+ }
+ return hr;
+}
+
+bool GetArgumentsFromShortcut(const FilePath& shortcut,
+ string16* arguments) {
+ HRESULT result;
+ base::win::ScopedComPtr<IShellLink> i_shell_link;
+ bool is_resolved = false;
+
+
+ base::win::ScopedCOMInitializer sta_com_initializer;
+
+ // Get pointer to the IShellLink interface
+ result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(result)) {
+ base::win::ScopedComPtr<IPersistFile> persist;
+ // Query IShellLink for the IPersistFile interface
+ result = persist.QueryFrom(i_shell_link);
+ if (SUCCEEDED(result)) {
+ WCHAR temp_arguments[MAX_PATH];
+ // Load the shell link
+ result = persist->Load(shortcut.value().c_str(), STGM_READ);
+ if (SUCCEEDED(result)) {
+ result = i_shell_link->GetArguments(temp_arguments, MAX_PATH);
+ *arguments = temp_arguments;
+ is_resolved = true;
+ }
+ }
+ }
+
+ return is_resolved;
+}
+
+string16 ReadArgumentsFromPinnedTaskbarShortcut() {
+ wchar_t path_buffer[MAX_PATH] = {};
+
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, path_buffer))) {
+ FilePath shortcut(path_buffer);
+ shortcut = shortcut.Append(
+ L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar");
+
+ // TODO(robertshield): Get this stuff from BrowserDistribution.
+#if defined(GOOGLE_CHROME_BUILD)
+ shortcut = shortcut.Append(L"Google Chrome.lnk");
+#else
+ shortcut = shortcut.Append(L"Chromium.lnk");
+#endif
+
+ string16 arguments;
+ if (GetArgumentsFromShortcut(shortcut, &arguments)) {
+ return arguments;
+ }
+ }
+
+ return L"";
+}
+
+} // namespace winrt_utils
diff --git a/win8/metro_driver/winrt_utils.h b/win8/metro_driver/winrt_utils.h
new file mode 100644
index 0000000..28f1ead
--- /dev/null
+++ b/win8/metro_driver/winrt_utils.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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_UI_METRO_DRIVER_WINRT_UTILS_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_WINRT_UTILS_H_
+
+#include <string>
+
+#include <roapi.h>
+#include <windows.applicationmodel.core.h>
+
+#include "base/string16.h"
+
+void CheckHR(HRESULT hr, const char* str = nullptr);
+
+HSTRING MakeHString(const string16& str);
+
+string16 MakeStdWString(HSTRING hstring);
+
+namespace winrt_utils {
+
+template<unsigned int size, typename T>
+HRESULT CreateActivationFactory(wchar_t const (&class_name)[size], T** object) {
+ mswrw::HStringReference ref_class_name(class_name);
+ return winfoundtn::GetActivationFactory(ref_class_name.Get(), object);
+}
+
+#define DECLARE_CREATE_PROPERTY(Name, Type) \
+HRESULT Create ## Name ## Property( \
+ Type value, \
+ winfoundtn::IPropertyValue** prop);
+
+DECLARE_CREATE_PROPERTY(String, HSTRING);
+DECLARE_CREATE_PROPERTY(Int16, INT16);
+DECLARE_CREATE_PROPERTY(Int32, INT32);
+DECLARE_CREATE_PROPERTY(Int64, INT64);
+DECLARE_CREATE_PROPERTY(UInt8, UINT8);
+DECLARE_CREATE_PROPERTY(UInt16, UINT16);
+DECLARE_CREATE_PROPERTY(UInt32, UINT32);
+DECLARE_CREATE_PROPERTY(UInt64, UINT64);
+
+// Compares |lhs| with |rhs| and return the |result| as
+// WindowsCompareStringOrdinal would do, i.e.,
+// -1 if |lhs| is less than |rhs|, 0 if they are equal, and
+// +1 if |lhs| is greater than |rhs|.
+HRESULT CompareProperties(
+ winfoundtn::IPropertyValue* lhs, winfoundtn::IPropertyValue* rhs,
+ INT32* result);
+
+// Looks for a pinned taskbar shortcut in the current user's profile. If it
+// finds one, will return any arguments that have been appended to the
+// shortcut's command line. This is intended for scenarios where those shortcut
+// parameters are ordinarily ignored (i.e. metro apps on win8). Returns an
+// empty string on failure.
+string16 ReadArgumentsFromPinnedTaskbarShortcut();
+
+} // namespace winrt_utils
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_WINRT_UTILS_H_
diff --git a/win8/metro_driver/winrt_utils_unittest.cc b/win8/metro_driver/winrt_utils_unittest.cc
new file mode 100644
index 0000000..9ae869b
--- /dev/null
+++ b/win8/metro_driver/winrt_utils_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 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 "stdafx.h"
+
+#include "winrt_utils.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+template <typename Type>
+static HRESULT CreateProperty(Type value, winfoundtn::IPropertyValue** prop) {
+ return E_NOTIMPL;
+}
+
+template <>
+static HRESULT CreateProperty<const wchar_t*>(
+ const wchar_t* value, winfoundtn::IPropertyValue** prop) {
+ mswrw::HString string_value;
+ string_value.Attach(MakeHString(value));
+ return winrt_utils::CreateStringProperty(string_value.Get(), prop);
+}
+
+template <>
+static HRESULT CreateProperty<INT16>(INT16 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateInt16Property(value, prop);
+}
+
+template <>
+static HRESULT CreateProperty<INT32>(INT32 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateInt32Property(value, prop);
+}
+
+template <>
+static HRESULT CreateProperty<INT64>(INT64 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateInt64Property(value, prop);
+}
+
+template <>
+static HRESULT CreateProperty<UINT8>(UINT8 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateUInt8Property(value, prop);
+}
+
+template <>
+static HRESULT CreateProperty<UINT16>(UINT16 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateUInt16Property(value, prop);
+}
+
+template <>
+static HRESULT CreateProperty<UINT32>(UINT32 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateUInt32Property(value, prop);
+}
+
+template <>
+static HRESULT CreateProperty<UINT64>(UINT64 value,
+ winfoundtn::IPropertyValue** prop) {
+ return winrt_utils::CreateUInt64Property(value, prop);
+}
+
+template<typename Type>
+void TestCompareProperties(Type value1, Type value2) {
+ mswr::ComPtr<winfoundtn::IPropertyValue> property_1;
+ HRESULT hr = CreateProperty<Type>(value1, property_1.GetAddressOf());
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't create Property value 1";
+
+ mswr::ComPtr<winfoundtn::IPropertyValue> other_property_1;
+ hr = CreateProperty<Type>(value1, other_property_1.GetAddressOf());
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't create another Property value 1";
+
+ mswr::ComPtr<winfoundtn::IPropertyValue> property_2;
+ hr = CreateProperty<Type>(value2, property_2.GetAddressOf());
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't create Property value 2";
+
+ INT32 result = 42;
+ hr = winrt_utils::CompareProperties(
+ property_1.Get(), property_1.Get(), &result);
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't compare property_1 to itself";
+ EXPECT_EQ(0, result) << "Bad result value while comparing same property";
+
+ hr = winrt_utils::CompareProperties(
+ property_1.Get(), other_property_1.Get(), &result);
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't compare property_1 to other_property_1";
+ EXPECT_EQ(0, result) << "Bad result while comparing equal values";
+
+ hr = winrt_utils::CompareProperties(
+ property_1.Get(), property_2.Get(), &result);
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't compare property_1 to property_2";
+ EXPECT_EQ(-1, result) << "Bad result while comparing values for less than";
+
+ hr = winrt_utils::CompareProperties(
+ property_2.Get(), property_1.Get(), &result);
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Can't compare property_1 to property_2";
+ EXPECT_EQ(1, result) << "Bad result value while comparing for greater than";
+}
+
+TEST(PropertyValueCompareTest, CompareProperties) {
+ TestCompareProperties<INT16>(42, 43);
+ TestCompareProperties<INT32>(42, 43);
+ TestCompareProperties<INT64>(42, 43);
+ TestCompareProperties<UINT8>(42, 43);
+ TestCompareProperties<UINT16>(42, 43);
+ TestCompareProperties<UINT32>(42, 43);
+ TestCompareProperties<UINT64>(42, 43);
+ TestCompareProperties<const wchar_t*>(L"abc", L"bcd");
+}
+
+} // namespace
diff --git a/win8/win8.gyp b/win8/win8.gyp
new file mode 100644
index 0000000..bf64b1f
--- /dev/null
+++ b/win8/win8.gyp
@@ -0,0 +1,33 @@
+# Copyright (c) 2012 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.
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../build/win_precompile.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'check_sdk_patch',
+ 'type': 'none',
+ 'variables': {
+ 'check_sdk_script': '<(DEPTH)/chrome/tools/build/win/check_sdk_patch.py',
+ },
+ 'actions': [
+ {
+ 'action_name': 'check_sdk_patch_action',
+ 'inputs': [
+ '<@(windows_sdk_path)/Include/winrt/asyncinfo.h',
+ ],
+ 'outputs': [
+ # This keeps the ninja build happy.
+ 'dummy',
+ ],
+ 'action': ['python', '<(check_sdk_script)', '<@(windows_sdk_path)'],
+ },
+ ],
+ },
+ ],
+} \ No newline at end of file