summaryrefslogtreecommitdiff
path: root/chromium/win8
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/win8
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/win8')
-rw-r--r--chromium/win8/OWNERS5
-rw-r--r--chromium/win8/delegate_execute/chrome_util.cc164
-rw-r--r--chromium/win8/delegate_execute/chrome_util.h21
-rw-r--r--chromium/win8/delegate_execute/command_execute_impl.cc571
-rw-r--r--chromium/win8/delegate_execute/command_execute_impl.h108
-rw-r--r--chromium/win8/delegate_execute/command_execute_impl.rgs10
-rw-r--r--chromium/win8/delegate_execute/crash_server_init.cc96
-rw-r--r--chromium/win8/delegate_execute/crash_server_init.h23
-rw-r--r--chromium/win8/delegate_execute/delegate_execute.cc165
-rw-r--r--chromium/win8/delegate_execute/delegate_execute.gyp9
-rw-r--r--chromium/win8/delegate_execute/delegate_execute.rc60
-rw-r--r--chromium/win8/delegate_execute/delegate_execute_exe.ver2
-rw-r--r--chromium/win8/delegate_execute/delegate_execute_operation.cc58
-rw-r--r--chromium/win8/delegate_execute/delegate_execute_operation.h78
-rw-r--r--chromium/win8/delegate_execute/delegate_execute_util.cc50
-rw-r--r--chromium/win8/delegate_execute/delegate_execute_util.h32
-rw-r--r--chromium/win8/delegate_execute/delegate_execute_util_unittest.cc79
-rwxr-xr-xchromium/win8/delegate_execute/post_build.bat3
-rw-r--r--chromium/win8/delegate_execute/resource.h21
-rw-r--r--chromium/win8/metro_driver/DEPS5
-rw-r--r--chromium/win8/metro_driver/OWNERS5
-rw-r--r--chromium/win8/metro_driver/chrome_app_view.cc1216
-rw-r--r--chromium/win8/metro_driver/chrome_app_view.h172
-rw-r--r--chromium/win8/metro_driver/chrome_app_view_ash.cc1259
-rw-r--r--chromium/win8/metro_driver/chrome_app_view_ash.h208
-rw-r--r--chromium/win8/metro_driver/chrome_url_launch_handler.cc206
-rw-r--r--chromium/win8/metro_driver/chrome_url_launch_handler.h58
-rw-r--r--chromium/win8/metro_driver/devices_handler.cc23
-rw-r--r--chromium/win8/metro_driver/devices_handler.h31
-rw-r--r--chromium/win8/metro_driver/direct3d_helper.cc128
-rw-r--r--chromium/win8/metro_driver/direct3d_helper.h44
-rw-r--r--chromium/win8/metro_driver/display_properties.cc34
-rw-r--r--chromium/win8/metro_driver/file_picker.cc619
-rw-r--r--chromium/win8/metro_driver/file_picker.h18
-rw-r--r--chromium/win8/metro_driver/file_picker_ash.cc619
-rw-r--r--chromium/win8/metro_driver/file_picker_ash.h167
-rw-r--r--chromium/win8/metro_driver/ime/ime.gypi22
-rw-r--r--chromium/win8/metro_driver/ime/ime_popup_monitor.cc82
-rw-r--r--chromium/win8/metro_driver/ime/ime_popup_monitor.h19
-rw-r--r--chromium/win8/metro_driver/ime/ime_popup_observer.h27
-rw-r--r--chromium/win8/metro_driver/ime/input_scope.cc87
-rw-r--r--chromium/win8/metro_driver/ime/input_scope.h22
-rw-r--r--chromium/win8/metro_driver/ime/input_source.cc170
-rw-r--r--chromium/win8/metro_driver/ime/input_source.h36
-rw-r--r--chromium/win8/metro_driver/ime/input_source_observer.h26
-rw-r--r--chromium/win8/metro_driver/ime/text_service.cc493
-rw-r--r--chromium/win8/metro_driver/ime/text_service.h53
-rw-r--r--chromium/win8/metro_driver/ime/text_service_delegate.h40
-rw-r--r--chromium/win8/metro_driver/ime/text_store.cc897
-rw-r--r--chromium/win8/metro_driver/ime/text_store.h315
-rw-r--r--chromium/win8/metro_driver/ime/text_store_delegate.h55
-rw-r--r--chromium/win8/metro_driver/metro_dialog_box.cc160
-rw-r--r--chromium/win8/metro_driver/metro_dialog_box.h64
-rw-r--r--chromium/win8/metro_driver/metro_driver.cc124
-rw-r--r--chromium/win8/metro_driver/metro_driver.gyp6
-rw-r--r--chromium/win8/metro_driver/metro_driver.h19
-rw-r--r--chromium/win8/metro_driver/metro_driver_dll.ver2
-rw-r--r--chromium/win8/metro_driver/metro_driver_win7.cc139
-rw-r--r--chromium/win8/metro_driver/print_document_source.cc527
-rw-r--r--chromium/win8/metro_driver/print_document_source.h164
-rw-r--r--chromium/win8/metro_driver/print_handler.cc490
-rw-r--r--chromium/win8/metro_driver/print_handler.h116
-rw-r--r--chromium/win8/metro_driver/resources/Logo.pngbin0 -> 3976 bytes
-rw-r--r--chromium/win8/metro_driver/resources/SecondaryTile.pngbin0 -> 637 bytes
-rw-r--r--chromium/win8/metro_driver/resources/SmallLogo.pngbin0 -> 9285 bytes
-rw-r--r--chromium/win8/metro_driver/resources/VisualElementsManifest.xml17
-rw-r--r--chromium/win8/metro_driver/resources/splash-620x300.pngbin0 -> 10185 bytes
-rw-r--r--chromium/win8/metro_driver/run_all_unittests.cc19
-rw-r--r--chromium/win8/metro_driver/secondary_tile.cc228
-rw-r--r--chromium/win8/metro_driver/secondary_tile.h27
-rw-r--r--chromium/win8/metro_driver/settings_handler.cc175
-rw-r--r--chromium/win8/metro_driver/settings_handler.h44
-rw-r--r--chromium/win8/metro_driver/stdafx.h40
-rw-r--r--chromium/win8/metro_driver/toast_notification_handler.cc252
-rw-r--r--chromium/win8/metro_driver/toast_notification_handler.h55
-rw-r--r--chromium/win8/metro_driver/winrt_utils.cc226
-rw-r--r--chromium/win8/metro_driver/winrt_utils.h60
-rw-r--r--chromium/win8/metro_driver/winrt_utils_unittest.cc115
-rwxr-xr-xchromium/win8/util/check_sdk_patch.py48
-rw-r--r--chromium/win8/util/win8_util.cc19
-rw-r--r--chromium/win8/util/win8_util.h18
-rw-r--r--chromium/win8/viewer/metro_viewer_constants.cc13
-rw-r--r--chromium/win8/viewer/metro_viewer_constants.h20
-rw-r--r--chromium/win8/viewer/metro_viewer_process_host.cc113
-rw-r--r--chromium/win8/viewer/metro_viewer_process_host.h105
-rw-r--r--chromium/win8/win8.gyp14
86 files changed, 12127 insertions, 3 deletions
diff --git a/chromium/win8/OWNERS b/chromium/win8/OWNERS
new file mode 100644
index 00000000000..4c371fc00c7
--- /dev/null
+++ b/chromium/win8/OWNERS
@@ -0,0 +1,5 @@
+ananta@chromium.org
+cpu@chromium.org
+grt@chromium.org
+mad@chromium.org
+robertshield@chromium.org
diff --git a/chromium/win8/delegate_execute/chrome_util.cc b/chromium/win8/delegate_execute/chrome_util.cc
new file mode 100644
index 00000000000..3695b3331fb
--- /dev/null
+++ b/chromium/win8/delegate_execute/chrome_util.cc
@@ -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.
+
+#include "win8/delegate_execute/chrome_util.h"
+
+#include <windows.h>
+#include <atlbase.h>
+#include <shlobj.h>
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/md5.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string_util.h"
+#include "base/strings/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 "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/util_constants.h"
+#include "google_update/google_update_idl.h"
+
+namespace {
+
+#if defined(GOOGLE_CHROME_BUILD)
+
+// TODO(grt): These constants live in installer_util. Consider moving them
+// into common_constants to allow for reuse.
+const base::FilePath::CharType kNewChromeExe[] =
+ FILE_PATH_LITERAL("new_chrome.exe");
+const wchar_t kRenameCommandValue[] = L"cmd";
+const wchar_t kRegPathChromeClientBase[] =
+ L"Software\\Google\\Update\\Clients\\";
+
+// 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 base::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 base::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 base::FilePath& chrome_exe) {
+ base::FilePath new_chrome_exe(chrome_exe.DirName().Append(kNewChromeExe));
+ return base::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;
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ string16 reg_path_chrome_client = kRegPathChromeClientBase;
+ reg_path_chrome_client.append(dist->GetAppGuid());
+ base::win::RegKey key(root, reg_path_chrome_client.c_str(), KEY_QUERY_VALUE);
+
+ return key.ReadValue(kRenameCommandValue, update_command) == ERROR_SUCCESS;
+}
+
+#endif // GOOGLE_CHROME_BUILD
+
+} // namespace
+
+namespace delegate_execute {
+
+void UpdateChromeIfNeeded(const base::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::win::ScopedHandle process_handle;
+
+ if (InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) {
+ // 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());
+ }
+ }
+ } 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;
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ hr = process_launcher->LaunchCmdElevated(
+ dist->GetAppGuid().c_str(), kRenameCommandValue,
+ GetCurrentProcessId(), &handle);
+ if (FAILED(hr)) {
+ AtlTrace("%hs. Failed to launch command to finalize update; "
+ "hr=0x%X.\n", __FUNCTION__, hr);
+ } else {
+ process_handle.Set(reinterpret_cast<base::ProcessHandle>(handle));
+ }
+ }
+ }
+
+ // Wait for the update to complete and report the results.
+ if (process_handle.IsValid()) {
+ int exit_code = 0;
+ // WaitForExitCode will close the handle in all cases.
+ if (!base::WaitForExitCode(process_handle.Take(), &exit_code)) {
+ AtlTrace("%hs. Failed to get result when finalizing update.\n",
+ __FUNCTION__);
+ } else if (exit_code != installer::RENAME_SUCCESSFUL) {
+ AtlTrace("%hs. Failed to finalize update with exit code %d.\n",
+ __FUNCTION__, exit_code);
+ } else {
+ AtlTrace("%hs. Finalized pending update.\n", __FUNCTION__);
+ }
+ }
+#endif
+}
+
+} // delegate_execute
diff --git a/chromium/win8/delegate_execute/chrome_util.h b/chromium/win8/delegate_execute/chrome_util.h
new file mode 100644
index 00000000000..6310c25c7b1
--- /dev/null
+++ b/chromium/win8/delegate_execute/chrome_util.h
@@ -0,0 +1,21 @@
+// 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/strings/string16.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace delegate_execute {
+
+// Finalizes a previously updated installation.
+void UpdateChromeIfNeeded(const base::FilePath& chrome_exe);
+
+} // namespace delegate_execute
+
+#endif // WIN8_DELEGATE_EXECUTE_CHROME_UTIL_H_
diff --git a/chromium/win8/delegate_execute/command_execute_impl.cc b/chromium/win8/delegate_execute/command_execute_impl.cc
new file mode 100644
index 00000000000..cb68f54e573
--- /dev/null
+++ b/chromium/win8/delegate_execute/command_execute_impl.cc
@@ -0,0 +1,571 @@
+// 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 <shlguid.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process_handle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/message_window.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/win_util.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/util_constants.h"
+#include "ui/base/clipboard/clipboard_util_win.h"
+#include "win8/delegate_execute/chrome_util.h"
+#include "win8/delegate_execute/delegate_execute_util.h"
+#include "win8/viewer/metro_viewer_constants.h"
+
+namespace {
+
+// Helper function to retrieve the url from IShellItem interface passed in.
+// Returns S_OK on success.
+HRESULT GetUrlFromShellItem(IShellItem* shell_item, string16* url) {
+ DCHECK(shell_item);
+ DCHECK(url);
+ // First attempt to get the url from the underlying IDataObject if any. This
+ // ensures that we get the full url, i.e. including the anchor.
+ // If we fail to get the underlying IDataObject we retrieve the url via the
+ // IShellItem::GetDisplayName function.
+ CComPtr<IDataObject> object;
+ HRESULT hr = shell_item->BindToHandler(NULL,
+ BHID_DataObject,
+ IID_IDataObject,
+ reinterpret_cast<void**>(&object));
+ if (SUCCEEDED(hr)) {
+ DCHECK(object);
+ if (ui::ClipboardUtil::GetPlainText(object, url))
+ return S_OK;
+ }
+
+ 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 hr;
+ }
+
+ *url = static_cast<const wchar_t*>(name);
+ AtlTrace("Retrieved url from display name %ls\n", url->c_str());
+ return S_OK;
+}
+
+#if defined(USE_AURA)
+bool LaunchChromeBrowserProcess() {
+ base::FilePath delegate_exe_path;
+ if (!PathService::Get(base::FILE_EXE, &delegate_exe_path))
+ return false;
+
+ // First try and go up a level to find chrome.exe.
+ base::FilePath chrome_exe_path =
+ delegate_exe_path.DirName()
+ .DirName()
+ .Append(chrome::kBrowserProcessExecutableName);
+ if (!base::PathExists(chrome_exe_path)) {
+ // Try looking in the current directory if we couldn't find it one up in
+ // order to support developer installs.
+ chrome_exe_path =
+ delegate_exe_path.DirName()
+ .Append(chrome::kBrowserProcessExecutableName);
+ }
+
+ if (!base::PathExists(chrome_exe_path)) {
+ AtlTrace("Could not locate chrome.exe at: %ls\n",
+ chrome_exe_path.value().c_str());
+ return false;
+ }
+
+ CommandLine cl(chrome_exe_path);
+
+ // Prevent a Chrome window from showing up on the desktop.
+ cl.AppendSwitch(switches::kSilentLaunch);
+
+ // Tell Chrome to connect to the Metro viewer process.
+ cl.AppendSwitch(switches::kViewerConnect);
+
+ base::LaunchOptions launch_options;
+ launch_options.start_hidden = true;
+
+ return base::LaunchProcess(cl, launch_options, NULL);
+}
+#endif // defined(USE_AURA)
+
+} // namespace
+
+bool CommandExecuteImpl::path_provider_initialized_ = false;
+
+// 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) else we return what GetLaunchMode() tells us, which is:
+// i) if the command line --force-xxx is present return that
+// ii) if the registry 'launch_mode' exists return that
+// iii) if IsTouchEnabledDevice() is true return AHE_IMMERSIVE
+// iv) else return AHE_DESKTOP
+// 6- If we returned AHE_IMMERSIVE in step 5 windows might not call us back
+// and simply activate chrome in metro by itself, however in some cases
+// it might proceed at step 7.
+// As far as we know if we return AHE_DESKTOP then step 7 always happens.
+// 7- Windows calls CommandExecuteImpl::Execute()
+// Here we call GetLaunchMode() which returns the cached answer
+// computed at step 5c. which can be:
+// a) ECHUIM_DESKTOP then we call LaunchDesktopChrome() that calls
+// ::CreateProcess and we exit at this point even on failure.
+// b) else we call one of the IApplicationActivationManager activation
+// functions depending on the parameters passed in step 4.
+// c) If the activation returns E_APPLICATION_NOT_REGISTERED, then we fall
+// back to launching chrome on the desktop via LaunchDestopChrome().
+//
+// Note that if a command line --force-xxx is present we write that launch mode
+// in the registry so next time the logic reaches 5c-ii it will use the same
+// mode again.
+//
+// Also note that if we are not the default browser and IsTouchEnabledDevice()
+// returns true, launching chrome can go all the way to 7c, which might be
+// a slow way to start chrome.
+//
+CommandExecuteImpl::CommandExecuteImpl()
+ : parameters_(CommandLine::NO_PROGRAM),
+ launch_scheme_(INTERNET_SCHEME_DEFAULT),
+ integrity_level_(base::INTEGRITY_UNKNOWN),
+ 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. We can be created multiplie times in a single instance
+ // however so make sure we do this only once.
+ if (!path_provider_initialized_) {
+ chrome::RegisterPathProvider();
+ path_provider_initialized_ = true;
+ }
+}
+
+// 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);
+ parameters_ = delegate_execute::CommandLineFromParameters(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;
+ }
+
+ if (GetAsyncKeyState(VK_SHIFT) && GetAsyncKeyState(VK_F11)) {
+ AtlTrace("Using Shift-F11 debug hook, returning AHE_IMMERSIVE\n");
+ *pahe = AHE_IMMERSIVE;
+
+#if defined(USE_AURA)
+ // Launch the chrome browser process that metro chrome will connect to.
+ LaunchChromeBrowserProcess();
+#endif
+
+ return S_OK;
+ }
+
+ if (GetAsyncKeyState(VK_SHIFT) && GetAsyncKeyState(VK_F12)) {
+ AtlTrace("Using Shift-F12 debug hook, returning AHE_DESKTOP\n");
+ *pahe = AHE_DESKTOP;
+ return S_OK;
+ }
+
+ base::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;
+ }
+
+ bool decision_made = false;
+
+ // New Aura/Ash world we don't want to go throgh FindWindow path
+ // and instead take decision based on launch mode.
+#if !defined(USE_AURA)
+ HWND chrome_window = base::win::MessageWindow::FindWindow(
+ user_data_dir.value());
+ 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;
+ }
+
+ decision_made = true;
+ }
+#endif
+
+ if (!decision_made) {
+ EC_HOST_UI_MODE mode = GetLaunchMode();
+ *pahe = (mode == ECHUIM_DESKTOP) ? AHE_DESKTOP : AHE_IMMERSIVE;
+ }
+
+#if defined(USE_AURA)
+ if (*pahe == AHE_IMMERSIVE && verb_ != win8::kMetroViewerConnectVerb)
+ LaunchChromeBrowserProcess();
+#endif
+
+ 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;
+ }
+
+ BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
+ bool is_per_user_install = InstallUtil::IsPerUserInstall(
+ chrome_exe_.value().c_str());
+ string16 app_id = ShellUtil::GetBrowserModelId(
+ distribution, is_per_user_install);
+
+ DWORD pid = 0;
+ if (launch_scheme_ == INTERNET_SCHEME_FILE &&
+ display_name_.find(installer::kChromeExe) != string16::npos) {
+ AtlTrace("Activating for file\n");
+ hr = activation_manager->ActivateApplication(app_id.c_str(),
+ verb_.c_str(),
+ AO_NONE,
+ &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(base::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.
+ base::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 (!base::PathExists(*chrome_exe)) {
+ *chrome_exe = dir_exe.Append(chrome::kBrowserProcessExecutableName);
+ if (!base::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;
+ }
+
+ hr = GetUrlFromShellItem(shell_item, display_name);
+ if (FAILED(hr)) {
+ AtlTrace("Failed to get url. Error 0x%x\n", hr);
+ return false;
+ }
+
+ AtlTrace("url [%ls]\n", display_name->c_str());
+
+ 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(display_name->c_str(), 0, 0, &components)) {
+ AtlTrace("Failed to crack url %ls\n", display_name->c_str());
+ return false;
+ }
+
+ AtlTrace("Launch scheme is [%ls] (%d)\n", scheme_name, components.nScheme);
+ *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(installer::kChromeExe) != string16::npos)
+ display_name.clear();
+ break;
+
+ default:
+ break;
+ }
+
+ CommandLine chrome(
+ delegate_execute::MakeChromeCommandLine(chrome_exe_, parameters_,
+ display_name));
+ string16 command_line(chrome.GetCommandLineString());
+
+ AtlTrace("Formatted command line is %ls\n", command_line.c_str());
+
+ PROCESS_INFORMATION temp_process_info = {};
+ BOOL ret = CreateProcess(chrome_exe_.value().c_str(),
+ const_cast<LPWSTR>(command_line.c_str()),
+ NULL, NULL, FALSE, 0, NULL, NULL, &start_info_,
+ &temp_process_info);
+ if (ret) {
+ base::win::ScopedProcessInformation proc_info(temp_process_info);
+ AtlTrace("Process id is %d\n", proc_info.process_id());
+ AllowSetForegroundWindow(proc_info.process_id());
+ } else {
+ AtlTrace("Process launch failed, error %d\n", ::GetLastError());
+ }
+
+ return S_OK;
+}
+
+EC_HOST_UI_MODE CommandExecuteImpl::GetLaunchMode() {
+ // See the header file for an explanation of the mode selection logic.
+ static bool launch_mode_determined = false;
+ static EC_HOST_UI_MODE launch_mode = ECHUIM_DESKTOP;
+
+ const char* modes[] = { "Desktop", "Immersive", "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_.HasSwitch(switches::kForceImmersive)) {
+ launch_mode = ECHUIM_IMMERSIVE;
+ launch_mode_determined = true;
+ parameters_ = CommandLine(CommandLine::NO_PROGRAM);
+ } else if (parameters_.HasSwitch(switches::kForceDesktop)) {
+ launch_mode = ECHUIM_DESKTOP;
+ launch_mode_determined = true;
+ parameters_ = CommandLine(CommandLine::NO_PROGRAM);
+ }
+
+ base::win::RegKey reg_key;
+ LONG key_result = reg_key.Create(HKEY_CURRENT_USER,
+ chrome::kMetroRegistryPath,
+ KEY_ALL_ACCESS);
+ if (key_result != ERROR_SUCCESS) {
+ AtlTrace("Failed to open HKCU %ls key, error 0x%x\n",
+ chrome::kMetroRegistryPath,
+ key_result);
+ if (!launch_mode_determined) {
+ launch_mode = ECHUIM_DESKTOP;
+ launch_mode_determined = true;
+ }
+ return launch_mode;
+ }
+
+ if (launch_mode_determined) {
+ AtlTrace("Launch mode forced by cmdline to %s\n", modes[launch_mode]);
+ reg_key.WriteValue(chrome::kLaunchModeValue,
+ static_cast<DWORD>(launch_mode));
+ return launch_mode;
+ }
+
+ DWORD reg_value;
+ if (reg_key.ReadValueDW(chrome::kLaunchModeValue,
+ &reg_value) != ERROR_SUCCESS) {
+ launch_mode = ECHUIM_DESKTOP;
+ AtlTrace("Launch mode forced by heuristics to %s\n", modes[launch_mode]);
+ } else if (reg_value >= ECHUIM_SYSTEM_LAUNCHER) {
+ AtlTrace("Invalid registry launch mode value %u\n", reg_value);
+ launch_mode = ECHUIM_DESKTOP;
+ } else {
+ launch_mode = static_cast<EC_HOST_UI_MODE>(reg_value);
+ AtlTrace("Launch mode forced by registry to %s\n", modes[launch_mode]);
+ }
+
+ launch_mode_determined = true;
+ return launch_mode;
+}
diff --git a/chromium/win8/delegate_execute/command_execute_impl.h b/chromium/win8/delegate_execute/command_execute_impl.h
new file mode 100644
index 00000000000..7b1490b21d1
--- /dev/null
+++ b/chromium/win8/delegate_execute/command_execute_impl.h
@@ -0,0 +1,108 @@
+// 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/command_line.h"
+#include "base/files/file_path.h"
+#include "base/process/process_handle.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.
+// The CLSID here is a dummy CLSID not used for anything, since we register
+// the class with a dynamic CLSID. However, a static CLSID is necessary
+// so that we can force at least one entry into ATL's object map (it will
+// treat a 0-element object map as an initialization failure case).
+class ATL_NO_VTABLE DECLSPEC_UUID("071BB5F2-85A4-424F-BFE7-5F1609BE4C2C")
+ CommandExecuteImpl
+ : 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(base::FilePath* chrome_exe);
+
+ static bool path_provider_initialized_;
+
+ 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_;
+ CommandLine parameters_;
+ base::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/chromium/win8/delegate_execute/command_execute_impl.rgs b/chromium/win8/delegate_execute/command_execute_impl.rgs
new file mode 100644
index 00000000000..4f1aba3de75
--- /dev/null
+++ b/chromium/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/chromium/win8/delegate_execute/crash_server_init.cc b/chromium/win8/delegate_execute/crash_server_init.cc
new file mode 100644
index 00000000000..76893164294
--- /dev/null
+++ b/chromium/win8/delegate_execute/crash_server_init.cc
@@ -0,0 +1,96 @@
+// 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/crash_server_init.h"
+
+#include <shlobj.h>
+#include <windows.h>
+
+#include <cwchar>
+
+#include "base/file_version_info.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/win_util.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+
+const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
+const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
+
+const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithUnloadedModules | // Get unloaded modules when available.
+ MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
+
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+namespace {
+
+bool IsRunningSystemInstall() {
+ wchar_t exe_path[MAX_PATH * 2] = {0};
+ GetModuleFileName(reinterpret_cast<HMODULE>(&__ImageBase),
+ exe_path,
+ _countof(exe_path));
+
+ bool is_system = false;
+
+ wchar_t program_files_path[MAX_PATH] = {0};
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
+ SHGFP_TYPE_CURRENT, program_files_path))) {
+ if (wcsstr(exe_path, program_files_path) == exe_path) {
+ is_system = true;
+ }
+ }
+
+ return is_system;
+}
+
+google_breakpad::CustomClientInfo* GetCustomInfo() {
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule());
+
+ static google_breakpad::CustomInfoEntry ver_entry(
+ L"ver", version_info->file_version().c_str());
+ static google_breakpad::CustomInfoEntry prod_entry(L"prod", L"Chrome");
+ static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
+ static google_breakpad::CustomInfoEntry type_entry(L"ptype",
+ L"delegate_execute");
+ static google_breakpad::CustomInfoEntry entries[] = {
+ ver_entry, prod_entry, plat_entry, type_entry };
+ static google_breakpad::CustomClientInfo custom_info = {
+ entries, ARRAYSIZE(entries) };
+ return &custom_info;
+}
+
+} // namespace
+
+namespace delegate_execute {
+
+scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting() {
+ wchar_t temp_path[MAX_PATH + 1] = {0};
+ DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
+
+ string16 pipe_name;
+ pipe_name = kGoogleUpdatePipeName;
+ if (IsRunningSystemInstall()) {
+ pipe_name += kSystemPrincipalSid;
+ } else {
+ string16 user_sid;
+ if (base::win::GetUserSidString(&user_sid)) {
+ pipe_name += user_sid;
+ } else {
+ // We don't think we're a system install, but we couldn't get the
+ // user SID. Try connecting to the system-level crash service as a
+ // last ditch effort.
+ pipe_name += kSystemPrincipalSid;
+ }
+ }
+
+ return scoped_ptr<google_breakpad::ExceptionHandler>(
+ new google_breakpad::ExceptionHandler(
+ temp_path, NULL, NULL, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
+ pipe_name.c_str(), GetCustomInfo()));
+}
+
+} // namespace delegate_execute
diff --git a/chromium/win8/delegate_execute/crash_server_init.h b/chromium/win8/delegate_execute/crash_server_init.h
new file mode 100644
index 00000000000..4173ef5a81c
--- /dev/null
+++ b/chromium/win8/delegate_execute/crash_server_init.h
@@ -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.
+
+#ifndef WIN8_DELEGATE_EXECUTE_CRASH_SERVER_INIT_H_
+#define WIN8_DELEGATE_EXECUTE_CRASH_SERVER_INIT_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace google_breakpad {
+class ExceptionHandler;
+}
+
+namespace delegate_execute {
+
+// Initializes breakpad crash reporting and returns a pointer to a newly
+// constructed ExceptionHandler object. It is the responsibility of the caller
+// to delete this object which will shut down the crash reporting machinery.
+scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting();
+
+} // namespace delegate_execute
+
+#endif // WIN8_DELEGATE_EXECUTE_CRASH_SERVER_INIT_H_
diff --git a/chromium/win8/delegate_execute/delegate_execute.cc b/chromium/win8/delegate_execute/delegate_execute.cc
new file mode 100644
index 00000000000..ddb9883daa2
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute.cc
@@ -0,0 +1,165 @@
+// 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 "build/intsafe_workaround.h"
+
+#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/memory/scoped_ptr.h"
+#include "base/process/kill.h"
+#include "base/strings/string16.h"
+#include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_handle.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "win8/delegate_execute/command_execute_impl.h"
+#include "win8/delegate_execute/crash_server_init.h"
+#include "win8/delegate_execute/delegate_execute_operation.h"
+#include "win8/delegate_execute/resource.h"
+
+using namespace ATL;
+
+// Usually classes derived from CAtlExeModuleT, or other types of ATL
+// COM module classes statically define their CLSID at compile time through
+// the use of various macros, and ATL internals takes care of creating the
+// class objects and registering them. However, we need to register the same
+// object with different CLSIDs depending on a runtime setting, so we handle
+// that logic here, before the main ATL message loop runs.
+class DelegateExecuteModule
+ : public ATL::CAtlExeModuleT< DelegateExecuteModule > {
+ public :
+ typedef ATL::CAtlExeModuleT<DelegateExecuteModule> ParentClass;
+ typedef CComObject<CommandExecuteImpl> ImplType;
+
+ DelegateExecuteModule()
+ : registration_token_(0) {
+ }
+
+ HRESULT PreMessageLoop(int nShowCmd) {
+ HRESULT hr = S_OK;
+ string16 clsid_string;
+ GUID clsid;
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ if (!dist->GetCommandExecuteImplClsid(&clsid_string))
+ return E_FAIL;
+ hr = ::CLSIDFromString(clsid_string.c_str(), &clsid);
+ if (FAILED(hr))
+ return hr;
+
+ // We use the same class creation logic as ATL itself. See
+ // _ATL_OBJMAP_ENTRY::RegisterClassObject() in atlbase.h
+ hr = ImplType::_ClassFactoryCreatorClass::CreateInstance(
+ ImplType::_CreatorClass::CreateInstance, IID_IUnknown,
+ instance_.ReceiveVoid());
+ if (FAILED(hr))
+ return hr;
+ hr = ::CoRegisterClassObject(clsid, instance_, CLSCTX_LOCAL_SERVER,
+ REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED, &registration_token_);
+ if (FAILED(hr))
+ return hr;
+
+ return ParentClass::PreMessageLoop(nShowCmd);
+ }
+
+ HRESULT PostMessageLoop() {
+ if (registration_token_ != 0) {
+ ::CoRevokeClassObject(registration_token_);
+ registration_token_ = 0;
+ }
+
+ instance_.Release();
+
+ return ParentClass::PostMessageLoop();
+ }
+
+ private:
+ base::win::ScopedComPtr<IUnknown> instance_;
+ DWORD registration_token_;
+};
+
+DelegateExecuteModule _AtlModule;
+
+using delegate_execute::DelegateExecuteOperation;
+using base::win::ScopedHandle;
+
+int RelaunchChrome(const DelegateExecuteOperation& operation) {
+ AtlTrace("Relaunching [%ls] with flags [%s]\n",
+ operation.mutex().c_str(), operation.relaunch_flags());
+ ScopedHandle mutex(OpenMutexW(SYNCHRONIZE, FALSE, operation.mutex().c_str()));
+ if (mutex.IsValid()) {
+ const int kWaitSeconds = 5;
+ DWORD result = ::WaitForSingleObject(mutex, kWaitSeconds * 1000);
+ if (result == WAIT_ABANDONED) {
+ // This is the normal case. Chrome exits and windows marks the mutex as
+ // abandoned.
+ } else if (result == WAIT_OBJECT_0) {
+ // This is unexpected. Check if somebody is not closing the mutex on
+ // RelaunchChromehelper, the mutex should not be closed.
+ AtlTrace("Unexpected release of the relaunch mutex!!\n");
+ } else if (result == WAIT_TIMEOUT) {
+ // This could mean that Chrome is hung. Proceed to exterminate.
+ DWORD pid = operation.GetParentPid();
+ AtlTrace("%ds timeout. Killing Chrome %d\n", kWaitSeconds, pid);
+ base::KillProcessById(pid, 0, false);
+ } else {
+ AtlTrace("Failed to wait for relaunch mutex, result is 0x%x\n", result);
+ }
+ } else {
+ // It is possible that chrome exits so fast that the mutex is not there.
+ AtlTrace("No relaunch mutex found\n");
+ }
+
+ base::win::ScopedCOMInitializer com_initializer;
+
+ string16 relaunch_flags(operation.relaunch_flags());
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = operation.shortcut().value().c_str();
+ sei.lpParameters = relaunch_flags.c_str();
+
+ AtlTrace(L"Relaunching Chrome via shortcut [%ls]\n", sei.lpFile);
+
+ if (!::ShellExecuteExW(&sei)) {
+ int error = HRESULT_FROM_WIN32(::GetLastError());
+ AtlTrace("ShellExecute returned 0x%08X\n", error);
+ return error;
+ }
+ return S_OK;
+}
+
+extern "C" int WINAPI _tWinMain(HINSTANCE , HINSTANCE, LPTSTR, int nShowCmd) {
+ scoped_ptr<google_breakpad::ExceptionHandler> breakpad =
+ delegate_execute::InitializeCrashReporting();
+
+ base::AtExitManager exit_manager;
+ AtlTrace("delegate_execute enter\n");
+
+ CommandLine::Init(0, NULL);
+ HRESULT ret_code = E_UNEXPECTED;
+ DelegateExecuteOperation operation;
+ if (operation.Init(CommandLine::ForCurrentProcess())) {
+ switch (operation.operation_type()) {
+ case DelegateExecuteOperation::DELEGATE_EXECUTE:
+ ret_code = _AtlModule.WinMain(nShowCmd);
+ break;
+ case DelegateExecuteOperation::RELAUNCH_CHROME:
+ ret_code = RelaunchChrome(operation);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ AtlTrace("delegate_execute exit, code = %d\n", ret_code);
+ return ret_code;
+}
diff --git a/chromium/win8/delegate_execute/delegate_execute.gyp b/chromium/win8/delegate_execute/delegate_execute.gyp
index 7461484cf38..935534f0a84 100644
--- a/chromium/win8/delegate_execute/delegate_execute.gyp
+++ b/chromium/win8/delegate_execute/delegate_execute.gyp
@@ -42,7 +42,9 @@
'../../base/base.gyp:base',
'../../breakpad/breakpad.gyp:breakpad_handler',
'../../chrome/chrome.gyp:installer_util',
+ '../../content/content.gyp:content_common',
'../../google_update/google_update.gyp:google_update',
+ '../../ui/gfx/gfx.gyp:gfx',
'../../ui/ui.gyp:ui',
'../../win8/win8.gyp:check_sdk_patch',
'delegate_execute_version_resources',
@@ -69,6 +71,13 @@
'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
},
},
+ 'conditions': [
+ ['use_aura==1', {
+ 'dependencies': [
+ '../win8.gyp:metro_viewer_constants',
+ ],
+ }],
+ ],
},
{
'target_name': 'delegate_execute_unittests',
diff --git a/chromium/win8/delegate_execute/delegate_execute.rc b/chromium/win8/delegate_execute/delegate_execute.rc
new file mode 100644
index 00000000000..74a3cc47b62
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute.rc
@@ -0,0 +1,60 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+#include "verrsrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "#include ""verrsrc.h""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// REGISTRY
+//
+
+IDR_COMMANDEXECUTEIMPL REGISTRY "command_execute_impl.rgs"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_PROJNAME "DelegateExecute"
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
diff --git a/chromium/win8/delegate_execute/delegate_execute_exe.ver b/chromium/win8/delegate_execute/delegate_execute_exe.ver
new file mode 100644
index 00000000000..fb7fb127e6f
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute_exe.ver
@@ -0,0 +1,2 @@
+INTERNAL_NAME=delegate_execute_exe
+ORIGINAL_FILENAME=delegate_execute.exe
diff --git a/chromium/win8/delegate_execute/delegate_execute_operation.cc b/chromium/win8/delegate_execute/delegate_execute_operation.cc
new file mode 100644
index 00000000000..a7333125ba5
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute_operation.cc
@@ -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.
+
+#include "win8/delegate_execute/delegate_execute_operation.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "chrome/common/chrome_switches.h"
+#include "win8/delegate_execute/delegate_execute_util.h"
+
+namespace delegate_execute {
+
+DelegateExecuteOperation::DelegateExecuteOperation()
+ : operation_type_(DELEGATE_EXECUTE) {
+}
+
+DelegateExecuteOperation::~DelegateExecuteOperation() {
+}
+
+bool DelegateExecuteOperation::Init(const CommandLine* cmd_line) {
+ base::FilePath shortcut(
+ cmd_line->GetSwitchValuePath(switches::kRelaunchShortcut));
+ if (shortcut.empty()) {
+ operation_type_ = DELEGATE_EXECUTE;
+ return true;
+ }
+ relaunch_shortcut_ = shortcut;
+ mutex_ = cmd_line->GetSwitchValueNative(switches::kWaitForMutex);
+ if (mutex_.empty())
+ return false;
+ // Add the mode forcing flags, if any.
+ const char* the_switch = NULL;
+
+ if (cmd_line->HasSwitch(switches::kForceDesktop))
+ the_switch = switches::kForceDesktop;
+ else if (cmd_line->HasSwitch(switches::kForceImmersive))
+ the_switch = switches::kForceImmersive;
+
+ relaunch_flags_ = ParametersFromSwitch(the_switch);
+
+ operation_type_ = RELAUNCH_CHROME;
+ return true;
+}
+
+DWORD DelegateExecuteOperation::GetParentPid() const {
+ std::vector<string16> parts;
+ base::SplitString(mutex_, L'.', &parts);
+ if (parts.size() != 3)
+ return 0;
+ DWORD pid;
+ if (!base::StringToUint(parts[2], reinterpret_cast<uint32*>(&pid)))
+ return 0;
+ return pid;
+}
+
+} // namespace delegate_execute
diff --git a/chromium/win8/delegate_execute/delegate_execute_operation.h b/chromium/win8/delegate_execute/delegate_execute_operation.h
new file mode 100644
index 00000000000..7d1456cef52
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute_operation.h
@@ -0,0 +1,78 @@
+// 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 <windows.h>
+#include <atldef.h>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+
+class CommandLine;
+
+namespace delegate_execute {
+
+// Parses a portion of the DelegateExecute handler's command line to determine
+// the desired operation. The operation type is decided by looking at the
+// command line. The operations are:
+// DELEGATE_EXECUTE:
+// When the delegate_execute.exe is invoked by windows when a chrome
+// activation via the shell, possibly using ShellExecute. Control must
+// be given to ATLs WinMain.
+// RELAUNCH_CHROME:
+// When the delegate_execute.exe is launched by chrome, when chrome needs
+// to re-launch itself. The required command line parameters are:
+// --relaunch-shortcut=<PathToShortcut>
+// --wait-for-mutex=<MutexNamePid>
+// The PathToShortcut must be the fully qualified file name to the chrome
+// shortcut that has the appId and other 'metro ready' parameters.
+// The MutexNamePid is a mutex name that also encodes the process id and
+// must follow the format <A>.<B>.<pid> where A and B are arbitray strings
+// (usually chrome.relaunch) and pid is the process id of chrome.
+class DelegateExecuteOperation {
+ public:
+ enum OperationType {
+ DELEGATE_EXECUTE,
+ RELAUNCH_CHROME,
+ };
+
+ DelegateExecuteOperation();
+ ~DelegateExecuteOperation();
+
+ bool Init(const CommandLine* cmd_line);
+
+ OperationType operation_type() const {
+ return operation_type_;
+ }
+
+ const string16& relaunch_flags() const {
+ return relaunch_flags_;
+ }
+
+ const string16& mutex() const {
+ return mutex_;
+ }
+
+ // Returns the process id of the parent or 0 on failure.
+ DWORD GetParentPid() const;
+
+ const base::FilePath& shortcut() const {
+ return relaunch_shortcut_;
+ }
+
+ private:
+ OperationType operation_type_;
+ string16 relaunch_flags_;
+ base::FilePath relaunch_shortcut_;
+ string16 mutex_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelegateExecuteOperation);
+};
+
+} // namespace delegate_execute
+
+#endif // WIN8_DELEGATE_EXECUTE_DELEGATE_EXECUTE_OPERATION_H_
diff --git a/chromium/win8/delegate_execute/delegate_execute_util.cc b/chromium/win8/delegate_execute/delegate_execute_util.cc
new file mode 100644
index 00000000000..6a3dc048dbd
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute_util.cc
@@ -0,0 +1,50 @@
+// 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_util.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+
+namespace delegate_execute {
+
+CommandLine CommandLineFromParameters(const wchar_t* params) {
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+
+ if (params) {
+ string16 command_string(L"noprogram.exe ");
+ command_string.append(params);
+ command_line.ParseFromString(command_string);
+ command_line.SetProgram(base::FilePath());
+ }
+
+ return command_line;
+}
+
+CommandLine MakeChromeCommandLine(const base::FilePath& chrome_exe,
+ const CommandLine& params,
+ const string16& argument) {
+ CommandLine chrome_cmd(params);
+ chrome_cmd.SetProgram(chrome_exe);
+
+ if (!argument.empty())
+ chrome_cmd.AppendArgNative(argument);
+
+ return chrome_cmd;
+}
+
+string16 ParametersFromSwitch(const char* a_switch) {
+ if (!a_switch)
+ return string16();
+
+ CommandLine cmd_line(CommandLine::NO_PROGRAM);
+
+ cmd_line.AppendSwitch(a_switch);
+
+ string16 command_string(cmd_line.GetCommandLineString());
+ TrimWhitespace(command_string, TRIM_ALL, &command_string);
+ return command_string;
+}
+
+} // namespace delegate_execute
diff --git a/chromium/win8/delegate_execute/delegate_execute_util.h b/chromium/win8/delegate_execute/delegate_execute_util.h
new file mode 100644
index 00000000000..adb2e7acc9a
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute_util.h
@@ -0,0 +1,32 @@
+// 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_UTIL_H_
+#define WIN8_DELEGATE_EXECUTE_DELEGATE_EXECUTE_UTIL_H_
+
+#include "base/command_line.h"
+#include "base/strings/string16.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace delegate_execute {
+
+// Returns a CommandLine with an empty program parsed from |params|.
+CommandLine CommandLineFromParameters(const wchar_t* params);
+
+// Returns a CommandLine to launch |chrome_exe| with all switches and arguments
+// from |params| plus an optional |argument|.
+CommandLine MakeChromeCommandLine(const base::FilePath& chrome_exe,
+ const CommandLine& params,
+ const string16& argument);
+
+// Returns a properly quoted command-line string less the program (argv[0])
+// containing |switch|.
+string16 ParametersFromSwitch(const char* a_switch);
+
+} // namespace delegate_execute
+
+#endif // WIN8_DELEGATE_EXECUTE_DELEGATE_EXECUTE_UTIL_H_
diff --git a/chromium/win8/delegate_execute/delegate_execute_util_unittest.cc b/chromium/win8/delegate_execute/delegate_execute_util_unittest.cc
new file mode 100644
index 00000000000..f01b15757bf
--- /dev/null
+++ b/chromium/win8/delegate_execute/delegate_execute_util_unittest.cc
@@ -0,0 +1,79 @@
+// 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_util.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const char kSomeSwitch[] = "some-switch";
+
+} // namespace
+
+TEST(DelegateExecuteUtil, CommandLineFromParametersTest) {
+ CommandLine cl(CommandLine::NO_PROGRAM);
+
+ // Empty parameters means empty command-line string.
+ cl = delegate_execute::CommandLineFromParameters(NULL);
+ EXPECT_EQ(std::wstring(), cl.GetProgram().value());
+ EXPECT_EQ(CommandLine::StringType(), cl.GetCommandLineString());
+
+ // Parameters with a switch are parsed properly.
+ cl = delegate_execute::CommandLineFromParameters(
+ base::StringPrintf(L"--%ls", ASCIIToWide(kSomeSwitch).c_str()).c_str());
+ EXPECT_EQ(std::wstring(), cl.GetProgram().value());
+ EXPECT_TRUE(cl.HasSwitch(kSomeSwitch));
+}
+
+TEST(DelegateExecuteUtil, MakeChromeCommandLineTest) {
+ static const wchar_t kSomeArgument[] = L"http://some.url/";
+ static const wchar_t kOtherArgument[] = L"http://some.other.url/";
+ const base::FilePath this_exe(CommandLine::ForCurrentProcess()->GetProgram());
+
+ CommandLine cl(CommandLine::NO_PROGRAM);
+
+ // Empty params and argument contains only the exe.
+ cl = delegate_execute::MakeChromeCommandLine(
+ this_exe, delegate_execute::CommandLineFromParameters(NULL), string16());
+ EXPECT_EQ(1, cl.argv().size());
+ EXPECT_EQ(this_exe.value(), cl.GetProgram().value());
+
+ // Empty params with arg contains the arg.
+ cl = delegate_execute::MakeChromeCommandLine(
+ this_exe, delegate_execute::CommandLineFromParameters(NULL),
+ string16(kSomeArgument));
+ EXPECT_EQ(2, cl.argv().size());
+ EXPECT_EQ(this_exe.value(), cl.GetProgram().value());
+ EXPECT_EQ(1, cl.GetArgs().size());
+ EXPECT_EQ(string16(kSomeArgument), cl.GetArgs()[0]);
+
+ // Params with switchs and args plus arg contains the arg.
+ cl = delegate_execute::MakeChromeCommandLine(
+ this_exe, delegate_execute::CommandLineFromParameters(
+ base::StringPrintf(L"--%ls -- %ls", ASCIIToWide(kSomeSwitch).c_str(),
+ kOtherArgument).c_str()),
+ string16(kSomeArgument));
+ EXPECT_EQ(5, cl.argv().size());
+ EXPECT_EQ(this_exe.value(), cl.GetProgram().value());
+ EXPECT_TRUE(cl.HasSwitch(kSomeSwitch));
+ CommandLine::StringVector args(cl.GetArgs());
+ EXPECT_EQ(2, args.size());
+ EXPECT_NE(args.end(),
+ std::find(args.begin(), args.end(), string16(kOtherArgument)));
+ EXPECT_NE(args.end(),
+ std::find(args.begin(), args.end(), string16(kSomeArgument)));
+}
+
+TEST(DelegateExecuteUtil, ParametersFromSwitchTest) {
+ EXPECT_EQ(string16(), delegate_execute::ParametersFromSwitch(NULL));
+ EXPECT_EQ(string16(L"--some-switch"),
+ delegate_execute::ParametersFromSwitch(kSomeSwitch));
+}
diff --git a/chromium/win8/delegate_execute/post_build.bat b/chromium/win8/delegate_execute/post_build.bat
new file mode 100755
index 00000000000..7bb9f9e3f2c
--- /dev/null
+++ b/chromium/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/chromium/win8/delegate_execute/resource.h b/chromium/win8/delegate_execute/resource.h
new file mode 100644
index 00000000000..971d6ae5b9a
--- /dev/null
+++ b/chromium/win8/delegate_execute/resource.h
@@ -0,0 +1,21 @@
+// 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_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/chromium/win8/metro_driver/DEPS b/chromium/win8/metro_driver/DEPS
new file mode 100644
index 00000000000..c767809f8dd
--- /dev/null
+++ b/chromium/win8/metro_driver/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ # Allow metro_driver to include from delegate_execute *temporarily*.
+ # TODO(ananta): Remove this by fixing toast_notification_handler.cc.
+ "+win8/delegate_execute",
+] \ No newline at end of file
diff --git a/chromium/win8/metro_driver/OWNERS b/chromium/win8/metro_driver/OWNERS
new file mode 100644
index 00000000000..38029375810
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/chrome_app_view.cc b/chromium/win8/metro_driver/chrome_app_view.cc
new file mode 100644
index 00000000000..79bd58474bc
--- /dev/null
+++ b/chromium/win8/metro_driver/chrome_app_view.cc
@@ -0,0 +1,1216 @@
+// 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 <corewindow.h>
+#include <windows.applicationModel.datatransfer.h>
+#include <windows.foundation.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/message_loop/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 "ui/base/ui_base_switches.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/metro_driver/metro_driver.h"
+#include "win8/metro_driver/winrt_utils.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;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::PointerEventArgs*> PointerEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::KeyEventArgs*> KeyEventHandler;
+
+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;
+
+const wchar_t kChromeSubclassWindowProp[] = L"MetroChromeWindowProc";
+
+namespace {
+
+enum Modifier {
+ NONE,
+ SHIFT = 1,
+ CONTROL = 2,
+ ALT = 4
+};
+
+// Helper function to send keystrokes via the SendInput function.
+// Params:
+// mnemonic_char: The keystroke to be sent.
+// modifiers: Combination with Alt, Ctrl, Shift, etc.
+// extended: whether this is an extended key.
+// unicode: whether this is a unicode key.
+void SendMnemonic(WORD mnemonic_char, Modifier modifiers, bool extended,
+ bool unicode) {
+ INPUT keys[4] = {0}; // Keyboard events
+ int key_count = 0; // Number of generated events
+
+ if (modifiers & SHIFT) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_SHIFT;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_SHIFT, 0);
+ key_count++;
+ }
+
+ if (modifiers & CONTROL) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_CONTROL;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
+ key_count++;
+ }
+
+ if (modifiers & ALT) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_MENU;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_MENU, 0);
+ key_count++;
+ }
+
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = mnemonic_char;
+ keys[key_count].ki.wScan = MapVirtualKey(mnemonic_char, 0);
+
+ if (extended)
+ keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
+ if (unicode)
+ keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE;
+ key_count++;
+
+ bool should_sleep = key_count > 1;
+
+ // Send key downs
+ for (int i = 0; i < key_count; i++) {
+ SendInput(1, &keys[ i ], sizeof(keys[0]));
+ keys[i].ki.dwFlags |= KEYEVENTF_KEYUP;
+ if (should_sleep) {
+ Sleep(10);
+ }
+ }
+
+ // Now send key ups in reverse order
+ for (int i = key_count; i; i--) {
+ SendInput(1, &keys[ i - 1 ], sizeof(keys[0]));
+ if (should_sleep) {
+ Sleep(10);
+ }
+ }
+}
+
+// Helper function to Exit metro chrome cleanly. If we are in the foreground
+// then we try and exit by sending an Alt+F4 key combination to the core
+// window which ensures that the chrome application tile does not show up in
+// the running metro apps list on the top left corner. We have seen cases
+// where this does work. To workaround that we invoke the
+// ICoreApplicationExit::Exit function in a background delayed task which
+// ensures that chrome exits.
+void MetroExit(bool send_alt_f4_mnemonic) {
+ if (send_alt_f4_mnemonic && globals.view &&
+ globals.view->core_window_hwnd() == ::GetForegroundWindow()) {
+ DVLOG(1) << "We are in the foreground. Exiting via Alt F4";
+ SendMnemonic(VK_F4, ALT, false, false);
+ DWORD core_window_process_id = 0;
+ DWORD core_window_thread_id = GetWindowThreadProcessId(
+ globals.view->core_window_hwnd(), &core_window_process_id);
+ if (core_window_thread_id != ::GetCurrentThreadId()) {
+ globals.appview_msg_loop->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MetroExit, false),
+ base::TimeDelta::FromMilliseconds(100));
+ }
+ } else {
+ globals.app_exit->Exit();
+ }
+}
+
+void AdjustToFitWindow(HWND hwnd, int flags) {
+ RECT rect = {0};
+ ::GetWindowRect(globals.view->core_window_hwnd() , &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);
+}
+
+LRESULT CALLBACK ChromeWindowProc(HWND window,
+ UINT message,
+ WPARAM wp,
+ LPARAM lp) {
+ if (message == WM_SETCURSOR) {
+ // Override the WM_SETCURSOR message to avoid showing the resize cursor
+ // as the mouse moves to the edge of the screen.
+ switch (LOWORD(lp)) {
+ case HTBOTTOM:
+ case HTBOTTOMLEFT:
+ case HTBOTTOMRIGHT:
+ case HTLEFT:
+ case HTRIGHT:
+ case HTTOP:
+ case HTTOPLEFT:
+ case HTTOPRIGHT:
+ lp = MAKELPARAM(HTCLIENT, HIWORD(lp));
+ break;
+ default:
+ break;
+ }
+ }
+
+ WNDPROC old_proc = reinterpret_cast<WNDPROC>(
+ ::GetProp(window, kChromeSubclassWindowProp));
+ DCHECK(old_proc);
+ return CallWindowProc(old_proc, window, message, wp, lp);
+}
+
+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));
+
+ // Subclass the wndproc of the frame window, if it's not already there.
+ if (::GetProp(hwnd, kChromeSubclassWindowProp) == NULL) {
+ WNDPROC old_chrome_proc =
+ reinterpret_cast<WNDPROC>(::SetWindowLongPtr(
+ hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(ChromeWindowProc)));
+ ::SetProp(hwnd, kChromeSubclassWindowProp, old_chrome_proc);
+ }
+ 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);
+ }
+
+ // Visible frame windows always need to be at the head of the list.
+ // Check if the window being shown already exists in our global list.
+ // If no then add it at the head of the list.
+ // If yes, retrieve the osk window scrolled state, remove the window from the
+ // list and readd it at the head.
+ std::list<std::pair<HWND, bool> >::iterator index =
+ std::find_if(globals.host_windows.begin(), globals.host_windows.end(),
+ [hwnd](std::pair<HWND, bool>& item) {
+ return (item.first == hwnd);
+ });
+
+ bool window_scrolled_state = false;
+ bool new_window = (index == globals.host_windows.end());
+ if (!new_window) {
+ window_scrolled_state = index->second;
+ globals.host_windows.erase(index);
+ }
+
+ globals.host_windows.push_front(std::make_pair(hwnd, window_scrolled_state));
+
+ if (new_window) {
+ AdjustFrameWindowStyleForMetro(hwnd);
+ } else {
+ DVLOG(1) << "Adjusting new top window to core window size";
+ AdjustToFitWindow(hwnd, 0);
+ }
+ if (globals.view->GetViewState() ==
+ winui::ViewManagement::ApplicationViewState_Snapped) {
+ DVLOG(1) << "Enabling Metro snap state on new window: " << hwnd;
+ ::PostMessageW(hwnd, WM_SYSCOMMAND, IDC_METRO_SNAP_ENABLE, 0);
+ }
+}
+
+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().";
+ MetroExit(true);
+ }
+}
+
+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
+
+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;
+ }
+}
+
+winui::ViewManagement::ApplicationViewState ChromeAppView::GetViewState() {
+ winui::ViewManagement::ApplicationViewState view_state =
+ winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
+ app_view_->get_Value(&view_state);
+ return view_state;
+}
+
+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.view->core_window_hwnd();
+}
+
+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,
+ base::win::MetroNotificationClickedHandler handler,
+ const wchar_t* handler_context) {
+ // TODO(ananta)
+ // Needs implementation.
+ DVLOG(1) << __FUNCTION__;
+
+ ToastNotificationHandler::DesktopNotification notification(origin_url,
+ icon_url,
+ title,
+ body,
+ display_source,
+ notification_id,
+ handler,
+ handler_context);
+ 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));
+}
+
+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>(::SetWindowLongPtr(
+ globals.view->core_window_hwnd(), GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(ChromeAppView::CoreWindowProc)));
+ DWORD exit_code = globals.host_main(globals.host_context);
+
+ DVLOG(1) << "host thread done, exit_code=" << exit_code;
+ MetroExit(true);
+ return exit_code;
+}
+
+ChromeAppView::ChromeAppView()
+ : osk_visible_notification_received_(false),
+ osk_offset_adjustment_(0),
+ core_window_hwnd_(NULL) {
+ globals.previous_state =
+ winapp::Activation::ApplicationExecutionState_NotRunning;
+}
+
+ChromeAppView::~ChromeAppView() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+IFACEMETHODIMP
+ChromeAppView::Initialize(winapp::Core::ICoreApplicationView* view) {
+ view_ = view;
+ DVLOG(1) << __FUNCTION__;
+
+ 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__;
+
+ // Retrieve the native window handle via the interop layer.
+ mswr::ComPtr<ICoreWindowInterop> interop;
+ HRESULT hr = window->QueryInterface(interop.GetAddressOf());
+ CheckHR(hr);
+ hr = interop->get_WindowHandle(&core_window_hwnd_);
+ CheckHR(hr);
+
+ 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.
+ base::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.
+ base::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);
+ }
+ }
+ }
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppView::CheckForOSKActivation, base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
+}
+
+IFACEMETHODIMP
+ChromeAppView::Run() {
+ DVLOG(1) << __FUNCTION__;
+ 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.
+ base::MessageLoop msg_loop(base::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;
+ }
+
+ DVLOG(1) << "Host thread exited";
+
+ ::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;
+ }
+
+ if (!globals.host_thread) {
+ DWORD chrome_ui_thread_id = 0;
+ globals.host_thread =
+ ::CreateThread(NULL, 0, HostMainThreadProc, NULL, 0,
+ &chrome_ui_thread_id);
+
+ if (!globals.host_thread) {
+ NOTREACHED() << "thread creation failed.";
+ return E_UNEXPECTED;
+ }
+ }
+
+ if (RegisterHotKey(core_window_hwnd_, 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/chromium/win8/metro_driver/chrome_app_view.h b/chromium/win8/metro_driver/chrome_app_view.h
new file mode 100644
index 00000000000..0554ab31366
--- /dev/null
+++ b/chromium/win8/metro_driver/chrome_app_view.h
@@ -0,0 +1,172 @@
+// 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_METRO_DRIVER_CHROME_APP_VIEW_H_
+#define WIN8_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/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"
+
+namespace IPC {
+ class Listener;
+ class ChannelProxy;
+}
+
+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);
+
+ 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);
+
+ // Returns the current view state of the chrome window.
+ winui::ViewManagement::ApplicationViewState GetViewState();
+
+ HWND core_window_hwnd() { return core_window_hwnd_; }
+
+ 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_;
+
+ // The actual window behind the view surface.
+ HWND core_window_hwnd_;
+
+ 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_;
+};
+
+// Global information used across the metro driver.
+struct Globals {
+ LPTHREAD_START_ROUTINE host_main;
+ void* host_context;
+ // 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;
+ 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;
+ string16 metro_command_line_switches;
+};
+
+extern Globals globals;
+
+#endif // WIN8_METRO_DRIVER_CHROME_APP_VIEW_H_
diff --git a/chromium/win8/metro_driver/chrome_app_view_ash.cc b/chromium/win8/metro_driver/chrome_app_view_ash.cc
new file mode 100644
index 00000000000..c1a8f4cd573
--- /dev/null
+++ b/chromium/win8/metro_driver/chrome_app_view_ash.cc
@@ -0,0 +1,1259 @@
+// 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_ash.h"
+
+#include <corewindow.h>
+#include <shellapi.h>
+#include <windows.foundation.h>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/threading/thread.h"
+#include "base/win/metro.h"
+#include "base/win/win_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_sender.h"
+#include "ui/events/gestures/gesture_sequence.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/metro_driver/file_picker_ash.h"
+#include "win8/metro_driver/ime/ime_popup_monitor.h"
+#include "win8/metro_driver/ime/input_source.h"
+#include "win8/metro_driver/ime/text_service.h"
+#include "win8/metro_driver/metro_driver.h"
+#include "win8/metro_driver/winrt_utils.h"
+#include "win8/viewer/metro_viewer_constants.h"
+
+typedef winfoundtn::ITypedEventHandler<
+ winapp::Core::CoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::PointerEventArgs*> PointerEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::KeyEventArgs*> KeyEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreDispatcher*,
+ winui::Core::AcceleratorKeyEventArgs*> AcceleratorKeyEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::CharacterReceivedEventArgs*> CharEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowActivatedEventArgs*> WindowActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Input::EdgeGesture*,
+ winui::Input::EdgeGestureEventArgs*> EdgeEventHandler;
+
+// This function is exported by chrome.exe.
+typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);
+
+// Global information used across the metro driver.
+struct Globals {
+ winapp::Activation::ApplicationExecutionState previous_state;
+ winapp::Core::ICoreApplicationExit* app_exit;
+ BreakpadExceptionHandler breakpad_exception_handler;
+} globals;
+
+namespace {
+
+enum KeyModifier {
+ NONE,
+ SHIFT = 1,
+ CONTROL = 2,
+ ALT = 4
+};
+
+// Helper function to send keystrokes via the SendInput function.
+// mnemonic_char: The keystroke to be sent.
+// modifiers: Combination with Alt, Ctrl, Shift, etc.
+void SendMnemonic(
+ WORD mnemonic_char, KeyModifier modifiers) {
+ INPUT keys[4] = {0}; // Keyboard events
+ int key_count = 0; // Number of generated events
+
+ if (modifiers & SHIFT) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_SHIFT;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_SHIFT, 0);
+ key_count++;
+ }
+
+ if (modifiers & CONTROL) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_CONTROL;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
+ key_count++;
+ }
+
+ if (modifiers & ALT) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_MENU;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_MENU, 0);
+ key_count++;
+ }
+
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = mnemonic_char;
+ keys[key_count].ki.wScan = MapVirtualKey(mnemonic_char, 0);
+ key_count++;
+
+ bool should_sleep = key_count > 1;
+
+ // Send key downs.
+ for (int i = 0; i < key_count; i++) {
+ SendInput(1, &keys[ i ], sizeof(keys[0]));
+ keys[i].ki.dwFlags |= KEYEVENTF_KEYUP;
+ if (should_sleep)
+ Sleep(10);
+ }
+
+ // Now send key ups in reverse order.
+ for (int i = key_count; i; i--) {
+ SendInput(1, &keys[ i - 1 ], sizeof(keys[0]));
+ if (should_sleep)
+ Sleep(10);
+ }
+}
+
+// Helper function to Exit metro chrome cleanly. If we are in the foreground
+// then we try and exit by sending an Alt+F4 key combination to the core
+// window which ensures that the chrome application tile does not show up in
+// the running metro apps list on the top left corner.
+void MetroExit(HWND core_window) {
+ if ((core_window != NULL) && (core_window == ::GetForegroundWindow())) {
+ DVLOG(1) << "We are in the foreground. Exiting via Alt F4";
+ SendMnemonic(VK_F4, ALT);
+ } else {
+ globals.app_exit->Exit();
+ }
+}
+
+class ChromeChannelListener : public IPC::Listener {
+ public:
+ ChromeChannelListener(base::MessageLoop* ui_loop, ChromeAppViewAsh* app_view)
+ : ui_proxy_(ui_loop->message_loop_proxy()),
+ app_view_(app_view) {}
+
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ IPC_BEGIN_MESSAGE_MAP(ChromeChannelListener, message)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ActivateDesktop,
+ OnActivateDesktop)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MetroExit, OnMetroExit)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_OpenURLOnDesktop,
+ OnOpenURLOnDesktop)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursor, OnSetCursor)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileOpen,
+ OnDisplayFileOpenDialog)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileSaveAs,
+ OnDisplayFileSaveAsDialog)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplaySelectFolder,
+ OnDisplayFolderPicker)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPos, OnSetCursorPos)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCancelComposition,
+ OnImeCancelComposition)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeTextInputClientUpdated,
+ OnImeTextInputClientChanged)
+ IPC_MESSAGE_UNHANDLED(__debugbreak())
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ virtual void OnChannelError() OVERRIDE {
+ DVLOG(1) << "Channel error. Exiting.";
+ MetroExit(app_view_->core_window_hwnd());
+ // In early Windows 8 versions the code above sometimes fails so we call
+ // it a second time with a NULL window which just calls Exit().
+ ui_proxy_->PostDelayedTask(FROM_HERE,
+ base::Bind(&MetroExit, HWND(NULL)),
+ base::TimeDelta::FromMilliseconds(100));
+ }
+
+ private:
+ void OnActivateDesktop(const base::FilePath& shortcut, bool ash_exit) {
+ ui_proxy_->PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnActivateDesktop,
+ base::Unretained(app_view_),
+ shortcut, ash_exit));
+ }
+
+ void OnMetroExit() {
+ MetroExit(app_view_->core_window_hwnd());
+ }
+
+ void OnOpenURLOnDesktop(const base::FilePath& shortcut,
+ const string16& url) {
+ ui_proxy_->PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnOpenURLOnDesktop,
+ base::Unretained(app_view_),
+ shortcut, url));
+ }
+
+ void OnSetCursor(int64 cursor) {
+ ui_proxy_->PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnSetCursor,
+ base::Unretained(app_view_),
+ reinterpret_cast<HCURSOR>(cursor)));
+ }
+
+ void OnDisplayFileOpenDialog(const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multiple_files) {
+ ui_proxy_->PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnDisplayFileOpenDialog,
+ base::Unretained(app_view_),
+ title,
+ filter,
+ default_path,
+ allow_multiple_files));
+ }
+
+ void OnDisplayFileSaveAsDialog(
+ const MetroViewerHostMsg_SaveAsDialogParams& params) {
+ ui_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnDisplayFileSaveAsDialog,
+ base::Unretained(app_view_),
+ params));
+ }
+
+ void OnDisplayFolderPicker(const string16& title) {
+ ui_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnDisplayFolderPicker,
+ base::Unretained(app_view_),
+ title));
+ }
+
+ void OnSetCursorPos(int x, int y) {
+ VLOG(1) << "In IPC OnSetCursorPos: " << x << ", " << y;
+ ui_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnSetCursorPos,
+ base::Unretained(app_view_),
+ x, y));
+ }
+
+ void OnImeCancelComposition() {
+ ui_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnImeCancelComposition,
+ base::Unretained(app_view_)));
+ }
+
+ void OnImeTextInputClientChanged(
+ const std::vector<int32>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds) {
+ ui_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnImeUpdateTextInputClient,
+ base::Unretained(app_view_),
+ input_scopes,
+ character_bounds));
+ }
+
+ scoped_refptr<base::MessageLoopProxy> ui_proxy_;
+ ChromeAppViewAsh* app_view_;
+};
+
+bool WaitForChromeIPCConnection(const std::string& channel_name) {
+ int ms_elapsed = 0;
+ while (!IPC::Channel::IsNamedServerInitialized(channel_name) &&
+ ms_elapsed < 10000) {
+ ms_elapsed += 500;
+ Sleep(500);
+ }
+ return IPC::Channel::IsNamedServerInitialized(channel_name);
+}
+
+// This class helps decoding the pointer properties of an event.
+class PointerInfoHandler {
+ public:
+ PointerInfoHandler()
+ : x_(0),
+ y_(0),
+ wheel_delta_(0),
+ update_kind_(winui::Input::PointerUpdateKind_Other),
+ timestamp_(0),
+ pointer_id_(0) {}
+
+ HRESULT Init(winui::Core::IPointerEventArgs* args) {
+ HRESULT hr = args->get_CurrentPoint(&pointer_point_);
+ if (FAILED(hr))
+ return hr;
+
+ winfoundtn::Point point;
+ hr = pointer_point_->get_Position(&point);
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<winui::Input::IPointerPointProperties> properties;
+ hr = pointer_point_->get_Properties(&properties);
+ if (FAILED(hr))
+ return hr;
+
+ hr = properties->get_PointerUpdateKind(&update_kind_);
+ if (FAILED(hr))
+ return hr;
+
+ hr = properties->get_MouseWheelDelta(&wheel_delta_);
+ if (FAILED(hr))
+ return hr;
+ x_ = point.X;
+ y_ = point.Y;
+ pointer_point_->get_Timestamp(&timestamp_);
+ pointer_point_->get_PointerId(&pointer_id_);
+ // Map the OS touch event id to a range allowed by the gesture recognizer.
+ if (IsTouch())
+ pointer_id_ %= ui::GestureSequence::kMaxGesturePoints;
+ return S_OK;
+ }
+
+ bool IsType(windevs::Input::PointerDeviceType type) const {
+ mswr::ComPtr<windevs::Input::IPointerDevice> pointer_device;
+ CheckHR(pointer_point_->get_PointerDevice(&pointer_device));
+ windevs::Input::PointerDeviceType device_type;
+ CheckHR(pointer_device->get_PointerDeviceType(&device_type));
+ return (device_type == type);
+ }
+
+ bool IsMouse() const {
+ return IsType(windevs::Input::PointerDeviceType_Mouse);
+ }
+
+ bool IsTouch() const {
+ return IsType(windevs::Input::PointerDeviceType_Touch);
+ }
+
+ int32 wheel_delta() const {
+ return wheel_delta_;
+ }
+
+ ui::EventFlags flags() {
+ switch (update_kind_) {
+ case winui::Input::PointerUpdateKind_LeftButtonPressed:
+ return ui::EF_LEFT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_LeftButtonReleased:
+ return ui::EF_LEFT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_RightButtonPressed:
+ return ui::EF_RIGHT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_RightButtonReleased:
+ return ui::EF_RIGHT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_MiddleButtonPressed:
+ return ui::EF_MIDDLE_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_MiddleButtonReleased:
+ return ui::EF_MIDDLE_MOUSE_BUTTON;
+ default:
+ return ui::EF_NONE;
+ };
+ }
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+
+ uint32 pointer_id() const {
+ return pointer_id_;
+ }
+
+ uint64 timestamp() const { return timestamp_; }
+
+ private:
+ int x_;
+ int y_;
+ int wheel_delta_;
+ uint32 pointer_id_;
+ winui::Input::PointerUpdateKind update_kind_;
+ mswr::ComPtr<winui::Input::IPointerPoint> pointer_point_;
+ uint64 timestamp_;
+};
+
+void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) {
+ // We're entering a nested message loop, let's allow dispatching
+ // tasks while we're in there.
+ base::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.
+ base::MessageLoop::current()->Quit();
+}
+
+// Helper to return the state of the shift/control/alt keys.
+uint32 GetKeyboardEventFlags() {
+ uint32 flags = 0;
+ if (base::win::IsShiftPressed())
+ flags |= ui::EF_SHIFT_DOWN;
+ if (base::win::IsCtrlPressed())
+ flags |= ui::EF_CONTROL_DOWN;
+ if (base::win::IsAltPressed())
+ flags |= ui::EF_ALT_DOWN;
+ return flags;
+}
+
+bool LaunchChromeBrowserProcess(const wchar_t* additional_parameters,
+ winapp::Activation::IActivatedEventArgs* args) {
+ if (args) {
+ DVLOG(1) << __FUNCTION__ << ":" << ::GetCommandLineW();
+ winapp::Activation::ActivationKind activation_kind;
+ CheckHR(args->get_Kind(&activation_kind));
+
+ DVLOG(1) << __FUNCTION__ << ", 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";
+ mswrw::HString launch_args_str;
+ launch_args->get_Arguments(launch_args_str.GetAddressOf());
+ string16 actual_launch_args(MakeStdWString(launch_args_str.Get()));
+ if (actual_launch_args == win8::kMetroViewerConnectVerb) {
+ DVLOG(1) << __FUNCTION__ << "Not launching chrome server";
+ return true;
+ }
+ }
+ }
+ }
+
+ DVLOG(1) << "Launching chrome server";
+ base::FilePath chrome_exe_path;
+
+ if (!PathService::Get(base::FILE_EXE, &chrome_exe_path))
+ return false;
+
+ string16 parameters = L"--silent-launch --viewer-connect ";
+ if (additional_parameters)
+ parameters += additional_parameters;
+
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = chrome_exe_path.value().c_str();
+ sei.lpDirectory = L"";
+ sei.lpParameters = parameters.c_str();
+ ::ShellExecuteEx(&sei);
+ return true;
+}
+
+} // namespace
+
+ChromeAppViewAsh::ChromeAppViewAsh()
+ : mouse_down_flags_(ui::EF_NONE),
+ ui_channel_(nullptr),
+ core_window_hwnd_(NULL),
+ ui_loop_(base::MessageLoop::TYPE_UI) {
+ DVLOG(1) << __FUNCTION__;
+ globals.previous_state =
+ winapp::Activation::ApplicationExecutionState_NotRunning;
+}
+
+ChromeAppViewAsh::~ChromeAppViewAsh() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Initialize(winapp::Core::ICoreApplicationView* view) {
+ view_ = view;
+ DVLOG(1) << __FUNCTION__;
+ HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
+ this, &ChromeAppViewAsh::OnActivate).Get(),
+ &activated_token_);
+ CheckHR(hr);
+ return hr;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::SetWindow(winui::Core::ICoreWindow* window) {
+ window_ = window;
+ DVLOG(1) << __FUNCTION__;
+
+ // Retrieve the native window handle via the interop layer.
+ mswr::ComPtr<ICoreWindowInterop> interop;
+ HRESULT hr = window->QueryInterface(interop.GetAddressOf());
+ CheckHR(hr);
+ hr = interop->get_WindowHandle(&core_window_hwnd_);
+ CheckHR(hr);
+
+ text_service_ = metro_driver::CreateTextService(this, core_window_hwnd_);
+
+ hr = window_->add_SizeChanged(mswr::Callback<SizeChangedHandler>(
+ this, &ChromeAppViewAsh::OnSizeChanged).Get(),
+ &sizechange_token_);
+ CheckHR(hr);
+
+ // Register for pointer and keyboard notifications. We forward
+ // them to the browser process via IPC.
+ hr = window_->add_PointerMoved(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnPointerMoved).Get(),
+ &pointermoved_token_);
+ CheckHR(hr);
+
+ hr = window_->add_PointerPressed(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnPointerPressed).Get(),
+ &pointerpressed_token_);
+ CheckHR(hr);
+
+ hr = window_->add_PointerReleased(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnPointerReleased).Get(),
+ &pointerreleased_token_);
+ CheckHR(hr);
+
+ hr = window_->add_KeyDown(mswr::Callback<KeyEventHandler>(
+ this, &ChromeAppViewAsh::OnKeyDown).Get(),
+ &keydown_token_);
+ CheckHR(hr);
+
+ hr = window_->add_KeyUp(mswr::Callback<KeyEventHandler>(
+ this, &ChromeAppViewAsh::OnKeyUp).Get(),
+ &keyup_token_);
+ CheckHR(hr);
+
+ mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
+ hr = window_->get_Dispatcher(&dispatcher);
+ CheckHR(hr, "Get Dispatcher failed.");
+
+ mswr::ComPtr<winui::Core::ICoreAcceleratorKeys> accelerator_keys;
+ hr = dispatcher.CopyTo(__uuidof(winui::Core::ICoreAcceleratorKeys),
+ reinterpret_cast<void**>(
+ accelerator_keys.GetAddressOf()));
+ CheckHR(hr, "QI for ICoreAcceleratorKeys failed.");
+ hr = accelerator_keys->add_AcceleratorKeyActivated(
+ mswr::Callback<AcceleratorKeyEventHandler>(
+ this, &ChromeAppViewAsh::OnAcceleratorKeyDown).Get(),
+ &accel_keydown_token_);
+ CheckHR(hr);
+
+ hr = window_->add_PointerWheelChanged(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnWheel).Get(),
+ &wheel_token_);
+ CheckHR(hr);
+
+ hr = window_->add_CharacterReceived(mswr::Callback<CharEventHandler>(
+ this, &ChromeAppViewAsh::OnCharacterReceived).Get(),
+ &character_received_token_);
+ CheckHR(hr);
+
+ hr = window_->add_Activated(mswr::Callback<WindowActivatedHandler>(
+ this, &ChromeAppViewAsh::OnWindowActivated).Get(),
+ &window_activated_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);
+
+ 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, &ChromeAppViewAsh::OnEdgeGestureCompleted).Get(),
+ &edgeevent_token_);
+ CheckHR(hr);
+
+ // By initializing the direct 3D swap chain with the corewindow
+ // we can now directly blit to it from the browser process.
+ direct3d_helper_.Initialize(window);
+ DVLOG(1) << "Initialized Direct3D.";
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Load(HSTRING entryPoint) {
+ DVLOG(1) << __FUNCTION__;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Run() {
+ DVLOG(1) << __FUNCTION__;
+ mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
+ HRESULT hr = window_->get_Dispatcher(&dispatcher);
+ CheckHR(hr, "Dispatcher failed.");
+
+ hr = window_->Activate();
+ if (FAILED(hr)) {
+ DLOG(WARNING) << "activation failed hr=" << hr;
+ return hr;
+ }
+
+ // Create the IPC channel IO thread. It needs to out-live the ChannelProxy.
+ base::Thread io_thread("metro_IO_thread");
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ io_thread.StartWithOptions(options);
+
+ // Start up Chrome and wait for the desired IPC server connection to exist.
+ WaitForChromeIPCConnection(win8::kMetroViewerIPCChannelName);
+
+ // In Aura mode we create an IPC channel to the browser, then ask it to
+ // connect to us.
+ ChromeChannelListener ui_channel_listener(&ui_loop_, this);
+ IPC::ChannelProxy ui_channel(win8::kMetroViewerIPCChannelName,
+ IPC::Channel::MODE_NAMED_CLIENT,
+ &ui_channel_listener,
+ io_thread.message_loop_proxy());
+ ui_channel_ = &ui_channel;
+
+ // Upon receipt of the MetroViewerHostMsg_SetTargetSurface message the
+ // browser will use D3D from the browser process to present to our Window.
+ ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface(
+ gfx::NativeViewId(core_window_hwnd_)));
+ DVLOG(1) << "ICoreWindow sent " << core_window_hwnd_;
+
+ // Send an initial size message so that the Ash root window host gets sized
+ // correctly.
+ RECT rect = {0};
+ ::GetWindowRect(core_window_hwnd_, &rect);
+ ui_channel_->Send(
+ new MetroViewerHostMsg_WindowSizeChanged(rect.right - rect.left,
+ rect.bottom - rect.top));
+
+ input_source_ = metro_driver::InputSource::Create();
+ if (input_source_) {
+ input_source_->AddObserver(this);
+ // Send an initial input source.
+ OnInputSourceChanged();
+ }
+
+ // Start receiving IME popup window notifications.
+ metro_driver::AddImePopupObserver(this);
+
+ // And post the task that'll do the inner Metro message pumping to it.
+ ui_loop_.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get()));
+ ui_loop_.Run();
+
+ DVLOG(0) << "ProcessEvents done, hr=" << hr;
+ return hr;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Uninitialize() {
+ DVLOG(1) << __FUNCTION__;
+ metro_driver::RemoveImePopupObserver(this);
+ input_source_.reset();
+ text_service_.reset();
+ window_ = nullptr;
+ view_ = nullptr;
+ core_window_hwnd_ = NULL;
+ return S_OK;
+}
+
+// static
+HRESULT ChromeAppViewAsh::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 ChromeAppViewAsh::OnActivateDesktop(const base::FilePath& file_path,
+ bool ash_exit) {
+ DVLOG(1) << "ChannelAppViewAsh::OnActivateDesktop\n";
+
+ if (ash_exit) {
+ // As we are the top level window, the exiting is done async so we manage
+ // to execute the entire function including the final Send().
+ MetroExit(core_window_hwnd());
+ }
+
+ // We are just executing delegate_execute here without parameters. Assumption
+ // here is that this process will be reused by shell when asking for
+ // IExecuteCommand interface.
+
+ // TODO(shrikant): Consolidate ShellExecuteEx with SEE_MASK_FLAG_LOG_USAGE
+ // and place it metro.h or similar accessible file from all code code paths
+ // using this function.
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = file_path.value().c_str();
+ sei.lpParameters = NULL;
+ if (!ash_exit)
+ sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
+ ::ShellExecuteExW(&sei);
+ if (!ash_exit) {
+ ::TerminateProcess(sei.hProcess, 0);
+ ::CloseHandle(sei.hProcess);
+ }
+}
+
+void ChromeAppViewAsh::OnOpenURLOnDesktop(const base::FilePath& shortcut,
+ const string16& url) {
+ base::FilePath::StringType file = shortcut.value();
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = file.c_str();
+ sei.lpDirectory = L"";
+ sei.lpParameters = url.c_str();
+ BOOL result = ShellExecuteEx(&sei);
+}
+
+void ChromeAppViewAsh::OnSetCursor(HCURSOR cursor) {
+ ::SetCursor(HCURSOR(cursor));
+}
+
+void ChromeAppViewAsh::OnDisplayFileOpenDialog(
+ const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multiple_files) {
+ DVLOG(1) << __FUNCTION__;
+
+ // The OpenFilePickerSession instance is deleted when we receive a
+ // callback from the OpenFilePickerSession class about the completion of the
+ // operation.
+ FilePickerSessionBase* file_picker_ =
+ new OpenFilePickerSession(this,
+ title,
+ filter,
+ default_path,
+ allow_multiple_files);
+ file_picker_->Run();
+}
+
+void ChromeAppViewAsh::OnDisplayFileSaveAsDialog(
+ const MetroViewerHostMsg_SaveAsDialogParams& params) {
+ DVLOG(1) << __FUNCTION__;
+
+ // The SaveFilePickerSession instance is deleted when we receive a
+ // callback from the SaveFilePickerSession class about the completion of the
+ // operation.
+ FilePickerSessionBase* file_picker_ =
+ new SaveFilePickerSession(this, params);
+ file_picker_->Run();
+}
+
+void ChromeAppViewAsh::OnDisplayFolderPicker(const string16& title) {
+ DVLOG(1) << __FUNCTION__;
+ // The FolderPickerSession instance is deleted when we receive a
+ // callback from the FolderPickerSession class about the completion of the
+ // operation.
+ FilePickerSessionBase* file_picker_ = new FolderPickerSession(this, title);
+ file_picker_->Run();
+}
+
+void ChromeAppViewAsh::OnSetCursorPos(int x, int y) {
+ if (ui_channel_) {
+ ::SetCursorPos(x, y);
+ DVLOG(1) << "In UI OnSetCursorPos: " << x << ", " << y;
+ ui_channel_->Send(new MetroViewerHostMsg_SetCursorPosAck());
+ // Generate a fake mouse move which matches the SetCursor coordinates as
+ // the browser expects to receive a mouse move for these coordinates.
+ // It is not clear why we don't receive a real mouse move in response to
+ // the SetCursorPos calll above.
+ ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(x, y, 0));
+ }
+}
+
+void ChromeAppViewAsh::OnOpenFileCompleted(
+ OpenFilePickerSession* open_file_picker,
+ bool success) {
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "Success: " << success;
+ if (ui_channel_) {
+ if (open_file_picker->allow_multi_select()) {
+ ui_channel_->Send(new MetroViewerHostMsg_MultiFileOpenDone(
+ success, open_file_picker->filenames()));
+ } else {
+ ui_channel_->Send(new MetroViewerHostMsg_FileOpenDone(
+ success, base::FilePath(open_file_picker->result())));
+ }
+ }
+ delete open_file_picker;
+}
+
+void ChromeAppViewAsh::OnSaveFileCompleted(
+ SaveFilePickerSession* save_file_picker,
+ bool success) {
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "Success: " << success;
+ if (ui_channel_) {
+ ui_channel_->Send(new MetroViewerHostMsg_FileSaveAsDone(
+ success,
+ base::FilePath(save_file_picker->result()),
+ save_file_picker->filter_index()));
+ }
+ delete save_file_picker;
+}
+
+void ChromeAppViewAsh::OnFolderPickerCompleted(
+ FolderPickerSession* folder_picker,
+ bool success) {
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "Success: " << success;
+ if (ui_channel_) {
+ ui_channel_->Send(new MetroViewerHostMsg_SelectFolderDone(
+ success,
+ base::FilePath(folder_picker->result())));
+ }
+ delete folder_picker;
+}
+
+void ChromeAppViewAsh::OnImeCancelComposition() {
+ if (!text_service_)
+ return;
+ text_service_->CancelComposition();
+}
+
+void ChromeAppViewAsh::OnImeUpdateTextInputClient(
+ const std::vector<int32>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds) {
+ if (!text_service_)
+ return;
+ text_service_->OnDocumentChanged(input_scopes, character_bounds);
+}
+
+void ChromeAppViewAsh::OnImePopupChanged(ImePopupObserver::EventType event) {
+ if (!ui_channel_)
+ return;
+ switch (event) {
+ case ImePopupObserver::kPopupShown:
+ ui_channel_->Send(new MetroViewerHostMsg_ImeCandidatePopupChanged(true));
+ return;
+ case ImePopupObserver::kPopupHidden:
+ ui_channel_->Send(new MetroViewerHostMsg_ImeCandidatePopupChanged(false));
+ return;
+ case ImePopupObserver::kPopupUpdated:
+ // TODO(kochi): Support this event for W3C IME API proposal.
+ // See crbug.com/238585.
+ return;
+ default:
+ NOTREACHED() << "unknown event type: " << event;
+ return;
+ }
+}
+
+void ChromeAppViewAsh::OnInputSourceChanged() {
+ if (!input_source_)
+ return;
+
+ LANGID langid = 0;
+ bool is_ime = false;
+ if (!input_source_->GetActiveSource(&langid, &is_ime)) {
+ LOG(ERROR) << "GetActiveSource failed";
+ return;
+ }
+ ui_channel_->Send(new MetroViewerHostMsg_ImeInputSourceChanged(langid,
+ is_ime));
+}
+
+void ChromeAppViewAsh::OnCompositionChanged(
+ const string16& text,
+ int32 selection_start,
+ int32 selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) {
+ ui_channel_->Send(new MetroViewerHostMsg_ImeCompositionChanged(
+ text, selection_start, selection_end, underlines));
+}
+
+void ChromeAppViewAsh::OnTextCommitted(const string16& text) {
+ ui_channel_->Send(new MetroViewerHostMsg_ImeTextCommitted(text));
+}
+
+HRESULT ChromeAppViewAsh::OnActivate(
+ winapp::Core::ICoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ // Note: If doing more work in this function, you migth need to call
+ // get_PreviousExecutionState() and skip the work if the result is
+ // ApplicationExecutionState_Running and globals.previous_state is too.
+ args->get_PreviousExecutionState(&globals.previous_state);
+ DVLOG(1) << "Previous Execution State: " << globals.previous_state;
+
+ winapp::Activation::ActivationKind activation_kind;
+ CheckHR(args->get_Kind(&activation_kind));
+ DVLOG(1) << "Activation kind: " << activation_kind;
+
+ if (activation_kind == winapp::Activation::ActivationKind_Search)
+ HandleSearchRequest(args);
+ else if (activation_kind == winapp::Activation::ActivationKind_Protocol)
+ HandleProtocolRequest(args);
+ else
+ LaunchChromeBrowserProcess(NULL, args);
+ // We call ICoreWindow::Activate after the handling for the search/protocol
+ // requests because Chrome can be launched to handle a search request which
+ // in turn launches the chrome browser process in desktop mode via
+ // ShellExecute. If we call ICoreWindow::Activate before this, then
+ // Windows kills the metro chrome process when it calls ShellExecute. Seems
+ // to be a bug.
+ window_->Activate();
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ PointerInfoHandler pointer;
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+
+ if (pointer.IsMouse()) {
+ ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(
+ pointer.x(),
+ pointer.y(),
+ mouse_down_flags_ | GetKeyboardEventFlags()));
+ } else {
+ DCHECK(pointer.IsTouch());
+ ui_channel_->Send(new MetroViewerHostMsg_TouchMoved(pointer.x(),
+ pointer.y(),
+ pointer.timestamp(),
+ pointer.pointer_id()));
+ }
+ return S_OK;
+}
+
+// NOTE: From experimentation, it seems like Metro only sends a PointerPressed
+// event for the first button pressed and the last button released in a sequence
+// of mouse events.
+// For example, a sequence of LEFT_DOWN, RIGHT_DOWN, LEFT_UP, RIGHT_UP results
+// only in PointerPressed(LEFT)/PointerReleased(RIGHT) events.
+HRESULT ChromeAppViewAsh::OnPointerPressed(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ PointerInfoHandler pointer;
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+
+ if (pointer.IsMouse()) {
+ // TODO: this is wrong, more than one pointer may be down at a time.
+ mouse_down_flags_ = pointer.flags();
+ ui_channel_->Send(new MetroViewerHostMsg_MouseButton(
+ pointer.x(),
+ pointer.y(),
+ 0,
+ ui::ET_MOUSE_PRESSED,
+ static_cast<ui::EventFlags>(
+ mouse_down_flags_ | GetKeyboardEventFlags())));
+ } else {
+ DCHECK(pointer.IsTouch());
+ ui_channel_->Send(new MetroViewerHostMsg_TouchDown(pointer.x(),
+ pointer.y(),
+ pointer.timestamp(),
+ pointer.pointer_id()));
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnPointerReleased(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ PointerInfoHandler pointer;
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+
+ if (pointer.IsMouse()) {
+ // TODO: this is wrong, more than one pointer may be down at a time.
+ mouse_down_flags_ = ui::EF_NONE;
+ ui_channel_->Send(new MetroViewerHostMsg_MouseButton(
+ pointer.x(),
+ pointer.y(),
+ 0,
+ ui::ET_MOUSE_RELEASED,
+ static_cast<ui::EventFlags>(
+ pointer.flags() | GetKeyboardEventFlags())));
+ } else {
+ DCHECK(pointer.IsTouch());
+ ui_channel_->Send(new MetroViewerHostMsg_TouchUp(pointer.x(),
+ pointer.y(),
+ pointer.timestamp(),
+ pointer.pointer_id()));
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnWheel(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ PointerInfoHandler pointer;
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+ DCHECK(pointer.IsMouse());
+ ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(), pointer.y(),
+ pointer.wheel_delta(),
+ ui::ET_MOUSEWHEEL,
+ ui::EF_NONE));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnKeyDown(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args) {
+ winsys::VirtualKey virtual_key;
+ HRESULT hr = args->get_VirtualKey(&virtual_key);
+ if (FAILED(hr))
+ return hr;
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ GetKeyboardEventFlags()));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnKeyUp(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args) {
+ winsys::VirtualKey virtual_key;
+ HRESULT hr = args->get_VirtualKey(&virtual_key);
+ if (FAILED(hr))
+ return hr;
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ GetKeyboardEventFlags()));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnAcceleratorKeyDown(
+ winui::Core::ICoreDispatcher* sender,
+ winui::Core::IAcceleratorKeyEventArgs* args) {
+ winsys::VirtualKey virtual_key;
+ HRESULT hr = args->get_VirtualKey(&virtual_key);
+ if (FAILED(hr))
+ return hr;
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ winui::Core::CoreAcceleratorKeyEventType event_type;
+ hr = args->get_EventType(&event_type);
+ if (FAILED(hr))
+ return hr;
+
+ uint32 keyboard_flags = GetKeyboardEventFlags();
+
+ switch (event_type) {
+ case winui::Core::CoreAcceleratorKeyEventType_SystemCharacter:
+ ui_channel_->Send(new MetroViewerHostMsg_Character(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ keyboard_flags));
+ break;
+
+ case winui::Core::CoreAcceleratorKeyEventType_SystemKeyDown:
+ ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ keyboard_flags));
+ break;
+
+ case winui::Core::CoreAcceleratorKeyEventType_SystemKeyUp:
+ ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ keyboard_flags));
+ break;
+
+ default:
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnCharacterReceived(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::ICharacterReceivedEventArgs* args) {
+ unsigned int char_code = 0;
+ HRESULT hr = args->get_KeyCode(&char_code);
+ if (FAILED(hr))
+ return hr;
+
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ ui_channel_->Send(new MetroViewerHostMsg_Character(char_code,
+ status.RepeatCount,
+ status.ScanCode,
+ GetKeyboardEventFlags()));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnWindowActivated(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowActivatedEventArgs* args) {
+ winui::Core::CoreWindowActivationState state;
+ HRESULT hr = args->get_WindowActivationState(&state);
+ if (FAILED(hr))
+ return hr;
+
+ // Treat both full activation (Ash was reopened from the Start Screen or from
+ // any other Metro entry point in Windows) and pointer activation (user
+ // clicked back in Ash after using another app on another monitor) the same.
+ if (state == winui::Core::CoreWindowActivationState_CodeActivated ||
+ state == winui::Core::CoreWindowActivationState_PointerActivated) {
+ if (text_service_)
+ text_service_->OnWindowActivated();
+ ui_channel_->Send(new MetroViewerHostMsg_WindowActivated());
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::HandleSearchRequest(
+ winapp::Activation::IActivatedEventArgs* args) {
+ mswr::ComPtr<winapp::Activation::ISearchActivatedEventArgs> search_args;
+ CheckHR(args->QueryInterface(
+ winapp::Activation::IID_ISearchActivatedEventArgs, &search_args));
+
+ if (!ui_channel_) {
+ DVLOG(1) << "Launched to handle search request";
+ LaunchChromeBrowserProcess(L"--windows8-search", args);
+ }
+
+ mswrw::HString search_string;
+ CheckHR(search_args->get_QueryText(search_string.GetAddressOf()));
+ string16 search_text(MakeStdWString(search_string.Get()));
+
+ ui_loop_.PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnSearchRequest,
+ base::Unretained(this),
+ search_text));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::HandleProtocolRequest(
+ winapp::Activation::IActivatedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ if (!ui_channel_)
+ DVLOG(1) << "Launched to handle url request";
+
+ mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs>
+ protocol_args;
+ CheckHR(args->QueryInterface(
+ winapp::Activation::IID_IProtocolActivatedEventArgs,
+ &protocol_args));
+
+ mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
+ protocol_args->get_Uri(&uri);
+ mswrw::HString url;
+ uri->get_AbsoluteUri(url.GetAddressOf());
+ string16 actual_url(MakeStdWString(url.Get()));
+ DVLOG(1) << "Received url request: " << actual_url;
+
+ ui_loop_.PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnNavigateToUrl,
+ base::Unretained(this),
+ actual_url));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnEdgeGestureCompleted(
+ winui::Input::IEdgeGesture* gesture,
+ winui::Input::IEdgeGestureEventArgs* args) {
+ // Swipe from edge gesture (and win+z) is equivalent to pressing F11.
+ // TODO(cpu): Make this cleaner for m33.
+ ui_channel_->Send(new MetroViewerHostMsg_KeyDown(VK_F11, 1, 0, 0));
+ ::Sleep(15);
+ ui_channel_->Send(new MetroViewerHostMsg_KeyUp(VK_F11, 1, 0, 0));
+ return S_OK;
+}
+
+void ChromeAppViewAsh::OnSearchRequest(const string16& search_string) {
+ DCHECK(ui_channel_);
+ ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string));
+}
+
+void ChromeAppViewAsh::OnNavigateToUrl(const string16& url) {
+ DCHECK(ui_channel_);
+ ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url));
+}
+
+HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowSizeChangedEventArgs* args) {
+ if (!window_) {
+ return S_OK;
+ }
+
+ winfoundtn::Size size;
+ HRESULT hr = args->get_Size(&size);
+ if (FAILED(hr))
+ return hr;
+
+ uint32 cx = static_cast<uint32>(size.Width);
+ uint32 cy = static_cast<uint32>(size.Height);
+
+ DVLOG(1) << "Window size changed: width=" << cx << ", height=" << cy;
+ ui_channel_->Send(new MetroViewerHostMsg_WindowSizeChanged(cx, cy));
+ return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ChromeAppViewFactory::ChromeAppViewFactory(
+ winapp::Core::ICoreApplication* icore_app,
+ LPTHREAD_START_ROUTINE host_main,
+ void* 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) {
+ *view = mswr::Make<ChromeAppViewAsh>().Detach();
+ return (*view) ? S_OK : E_OUTOFMEMORY;
+}
diff --git a/chromium/win8/metro_driver/chrome_app_view_ash.h b/chromium/win8/metro_driver/chrome_app_view_ash.h
new file mode 100644
index 00000000000..f6577925119
--- /dev/null
+++ b/chromium/win8/metro_driver/chrome_app_view_ash.h
@@ -0,0 +1,208 @@
+// 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_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_
+#define WIN8_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_
+
+#include <windows.applicationmodel.core.h>
+#include <windows.ui.core.h>
+#include <windows.ui.input.h>
+#include <windows.ui.viewmanagement.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string16.h"
+#include "ui/events/event_constants.h"
+#include "win8/metro_driver/direct3d_helper.h"
+#include "win8/metro_driver/ime/ime_popup_observer.h"
+#include "win8/metro_driver/ime/input_source_observer.h"
+#include "win8/metro_driver/ime/text_service_delegate.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace IPC {
+class Listener;
+class ChannelProxy;
+}
+
+namespace metro_driver {
+class InputSource;
+class TextService;
+}
+
+namespace metro_viewer {
+struct CharacterBounds;
+struct UnderlineInfo;
+}
+
+class OpenFilePickerSession;
+class SaveFilePickerSession;
+class FolderPickerSession;
+class FilePickerSessionBase;
+
+struct MetroViewerHostMsg_SaveAsDialogParams;
+
+class ChromeAppViewAsh
+ : public mswr::RuntimeClass<winapp::Core::IFrameworkView>,
+ public metro_driver::ImePopupObserver,
+ public metro_driver::InputSourceObserver,
+ public metro_driver::TextServiceDelegate {
+ public:
+ ChromeAppViewAsh();
+ ~ChromeAppViewAsh();
+
+ // IViewProvider overrides.
+ IFACEMETHOD(Initialize)(winapp::Core::ICoreApplicationView* view);
+ IFACEMETHOD(SetWindow)(winui::Core::ICoreWindow* window);
+ IFACEMETHOD(Load)(HSTRING entryPoint);
+ IFACEMETHOD(Run)();
+ IFACEMETHOD(Uninitialize)();
+
+ // Helper function to unsnap the chrome metro app if it is snapped.
+ // Returns S_OK on success.
+ static HRESULT Unsnap();
+
+ void OnActivateDesktop(const base::FilePath& file_path, bool ash_exit);
+ void OnOpenURLOnDesktop(const base::FilePath& shortcut, const string16& url);
+ void OnSetCursor(HCURSOR cursor);
+ void OnDisplayFileOpenDialog(const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multiple_files);
+ void OnDisplayFileSaveAsDialog(
+ const MetroViewerHostMsg_SaveAsDialogParams& params);
+ void OnDisplayFolderPicker(const string16& title);
+ void OnSetCursorPos(int x, int y);
+
+ // This function is invoked when the open file operation completes. The
+ // result of the operation is passed in along with the OpenFilePickerSession
+ // instance which is deleted after we read the required information from
+ // the OpenFilePickerSession class.
+ void OnOpenFileCompleted(OpenFilePickerSession* open_file_picker,
+ bool success);
+
+ // This function is invoked when the save file operation completes. The
+ // result of the operation is passed in along with the SaveFilePickerSession
+ // instance which is deleted after we read the required information from
+ // the SaveFilePickerSession class.
+ void OnSaveFileCompleted(SaveFilePickerSession* save_file_picker,
+ bool success);
+
+ // This function is invoked when the folder picker operation completes. The
+ // result of the operation is passed in along with the FolderPickerSession
+ // instance which is deleted after we read the required information from
+ // the FolderPickerSession class.
+ void OnFolderPickerCompleted(FolderPickerSession* folder_picker,
+ bool success);
+
+ void OnImeCancelComposition();
+ void OnImeUpdateTextInputClient(
+ const std::vector<int32>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds);
+
+ HWND core_window_hwnd() const { return core_window_hwnd_; }
+
+
+ private:
+ // ImePopupObserver overrides.
+ virtual void OnImePopupChanged(ImePopupObserver::EventType event) OVERRIDE;
+
+ // InputSourceObserver overrides.
+ virtual void OnInputSourceChanged() OVERRIDE;
+
+ // TextServiceDelegate overrides.
+ virtual void OnCompositionChanged(
+ const string16& text,
+ int32 selection_start,
+ int32 selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) OVERRIDE;
+ virtual void OnTextCommitted(const string16& text) OVERRIDE;
+
+ HRESULT OnActivate(winapp::Core::ICoreApplicationView* view,
+ winapp::Activation::IActivatedEventArgs* args);
+
+ HRESULT OnPointerMoved(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnPointerPressed(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnPointerReleased(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnWheel(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnKeyDown(winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args);
+
+ HRESULT OnKeyUp(winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args);
+
+ // Invoked for system keys like Alt, etc.
+ HRESULT OnAcceleratorKeyDown(winui::Core::ICoreDispatcher* sender,
+ winui::Core::IAcceleratorKeyEventArgs* args);
+
+ HRESULT OnCharacterReceived(winui::Core::ICoreWindow* sender,
+ winui::Core::ICharacterReceivedEventArgs* args);
+
+ HRESULT OnWindowActivated(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowActivatedEventArgs* args);
+
+ // Helper to handle search requests received via the search charm in ASH.
+ HRESULT HandleSearchRequest(winapp::Activation::IActivatedEventArgs* args);
+ // Helper to handle http/https url requests in ASH.
+ HRESULT HandleProtocolRequest(winapp::Activation::IActivatedEventArgs* args);
+
+ HRESULT OnEdgeGestureCompleted(winui::Input::IEdgeGesture* gesture,
+ winui::Input::IEdgeGestureEventArgs* args);
+
+ // Tasks posted to the UI thread to initiate the search/url navigation
+ // requests.
+ void OnSearchRequest(const string16& search_string);
+ void OnNavigateToUrl(const string16& url);
+
+ HRESULT OnSizeChanged(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowSizeChangedEventArgs* args);
+
+ mswr::ComPtr<winui::Core::ICoreWindow> window_;
+ mswr::ComPtr<winapp::Core::ICoreApplicationView> view_;
+ EventRegistrationToken activated_token_;
+ EventRegistrationToken pointermoved_token_;
+ EventRegistrationToken pointerpressed_token_;
+ EventRegistrationToken pointerreleased_token_;
+ EventRegistrationToken wheel_token_;
+ EventRegistrationToken keydown_token_;
+ EventRegistrationToken keyup_token_;
+ EventRegistrationToken character_received_token_;
+ EventRegistrationToken accel_keydown_token_;
+ EventRegistrationToken accel_keyup_token_;
+ EventRegistrationToken window_activated_token_;
+ EventRegistrationToken sizechange_token_;
+ EventRegistrationToken edgeevent_token_;
+
+ // Keep state about which button is currently down, if any, as PointerMoved
+ // events do not contain that state, but Ash's MouseEvents need it.
+ ui::EventFlags mouse_down_flags_;
+
+ // Set the D3D swap chain and nothing else.
+ metro_driver::Direct3DHelper direct3d_helper_;
+
+ // The channel to Chrome, in particular to the MetroViewerProcessHost.
+ IPC::ChannelProxy* ui_channel_;
+
+ // The actual window behind the view surface.
+ HWND core_window_hwnd_;
+
+ // UI message loop to allow message passing into this thread.
+ base::MessageLoop ui_loop_;
+
+ // For IME support.
+ scoped_ptr<metro_driver::InputSource> input_source_;
+ scoped_ptr<metro_driver::TextService> text_service_;
+};
+
+#endif // WIN8_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_
diff --git a/chromium/win8/metro_driver/chrome_url_launch_handler.cc b/chromium/win8/metro_driver/chrome_url_launch_handler.cc
new file mode 100644
index 00000000000..897276a9b12
--- /dev/null
+++ b/chromium/win8/metro_driver/chrome_url_launch_handler.cc
@@ -0,0 +1,206 @@
+// 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/command_line.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);
+}
+
+// |launch_args| is an encoded command line, minus the executable name. To
+// find the URL to launch the first argument is used. If any other parameters
+// are encoded in |launch_args| they are ignored.
+string16 ChromeUrlLaunchHandler::GetUrlFromLaunchArgs(
+ const string16& launch_args) {
+ if (launch_args == L"opennewwindow") {
+ VLOG(1) << "Returning new tab url";
+ return L"chrome://newtab";
+ }
+ string16 dummy_command_line(L"dummy.exe ");
+ dummy_command_line.append(launch_args);
+ CommandLine command_line = CommandLine::FromString(dummy_command_line);
+ CommandLine::StringVector args = command_line.GetArgs();
+ if (args.size() > 0)
+ return args[0];
+
+ 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");
+
+ if (globals.host_windows.empty()) {
+ DVLOG(1) << "No chrome windows registered. Ignoring nav request";
+ return;
+ }
+
+ // 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/chromium/win8/metro_driver/chrome_url_launch_handler.h b/chromium/win8/metro_driver/chrome_url_launch_handler.h
new file mode 100644
index 00000000000..d8c7ed01b7b
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/devices_handler.cc b/chromium/win8/metro_driver/devices_handler.cc
new file mode 100644
index 00000000000..20fd413bf35
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/devices_handler.h b/chromium/win8/metro_driver/devices_handler.h
new file mode 100644
index 00000000000..fdb2226c9fc
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/direct3d_helper.cc b/chromium/win8/metro_driver/direct3d_helper.cc
new file mode 100644
index 00000000000..056e84b4cb9
--- /dev/null
+++ b/chromium/win8/metro_driver/direct3d_helper.cc
@@ -0,0 +1,128 @@
+// Copyright 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/direct3d_helper.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+#include "base/logging.h"
+
+#include <windows.graphics.display.h>
+
+namespace {
+
+void CheckIfFailed(HRESULT hr) {
+ DCHECK(!FAILED(hr));
+ if (FAILED(hr))
+ DVLOG(0) << "Direct3D call failed, hr = " << hr;
+}
+
+float GetLogicalDpi() {
+ mswr::ComPtr<wingfx::Display::IDisplayPropertiesStatics> display_properties;
+ CheckIfFailed(winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Graphics_Display_DisplayProperties,
+ display_properties.GetAddressOf()));
+ float dpi = 0.0;
+ CheckIfFailed(display_properties->get_LogicalDpi(&dpi));
+ return dpi;
+}
+
+float ConvertDipsToPixels(float dips) {
+ static const float dips_per_inch = 96.f;
+ float logical_dpi = GetLogicalDpi();
+ return floor(dips * logical_dpi / dips_per_inch + 0.5f);
+}
+
+}
+
+namespace metro_driver {
+
+Direct3DHelper::Direct3DHelper() {
+}
+
+Direct3DHelper::~Direct3DHelper() {
+}
+
+void Direct3DHelper::Initialize(winui::Core::ICoreWindow* window) {
+ window_ = window;
+ CreateDeviceResources();
+ CreateWindowSizeDependentResources();
+}
+
+// TODO(scottmg): Need to handle resize messages and recreation.
+
+void Direct3DHelper::CreateDeviceResources() {
+ unsigned int creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+ 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;
+ CheckIfFailed(
+ D3D11CreateDevice(
+ nullptr,
+ D3D_DRIVER_TYPE_HARDWARE,
+ nullptr,
+ creation_flags,
+ feature_levels,
+ ARRAYSIZE(feature_levels),
+ D3D11_SDK_VERSION,
+ &device,
+ &feature_level_,
+ &context));
+ CheckIfFailed(device.As(&d3d_device_));
+ CheckIfFailed(context.As(&d3d_context_));
+}
+
+void Direct3DHelper::CreateWindowSizeDependentResources() {
+ CheckIfFailed(window_->get_Bounds(&window_bounds_));
+ float window_width = ConvertDipsToPixels(window_bounds_.Width);
+ float window_height = ConvertDipsToPixels(window_bounds_.Height);
+
+ // TODO(scottmg): Orientation.
+
+ if (swap_chain_ != nullptr) {
+ // TODO(scottmg): Resize if it already exists.
+ NOTIMPLEMENTED();
+ } else {
+ DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { 0 };
+ swap_chain_desc.Width = window_width;
+ swap_chain_desc.Height = window_height;
+ swap_chain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swap_chain_desc.Stereo = false;
+ swap_chain_desc.SampleDesc.Count = 1;
+ swap_chain_desc.SampleDesc.Quality = 0;
+ swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swap_chain_desc.BufferCount = 2; // TODO(scottmg): Probably 1 is fine.
+ swap_chain_desc.Scaling = DXGI_SCALING_NONE;
+ swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ swap_chain_desc.Flags = 0;
+
+ mswr::ComPtr<IDXGIDevice1> dxgi_device;
+ CheckIfFailed(d3d_device_.As(&dxgi_device));
+
+ mswr::ComPtr<IDXGIAdapter> dxgi_adapter;
+ CheckIfFailed(dxgi_device->GetAdapter(&dxgi_adapter));
+
+ mswr::ComPtr<IDXGIFactory2> dxgi_factory;
+ CheckIfFailed(dxgi_adapter->GetParent(
+ __uuidof(IDXGIFactory2), &dxgi_factory));
+
+ CheckIfFailed(dxgi_factory->CreateSwapChainForCoreWindow(
+ d3d_device_.Get(),
+ reinterpret_cast<IUnknown*>(window_),
+ &swap_chain_desc,
+ nullptr,
+ &swap_chain_));
+ }
+}
+
+} // namespace metro_driver
diff --git a/chromium/win8/metro_driver/direct3d_helper.h b/chromium/win8/metro_driver/direct3d_helper.h
new file mode 100644
index 00000000000..a5547ba08fe
--- /dev/null
+++ b/chromium/win8/metro_driver/direct3d_helper.h
@@ -0,0 +1,44 @@
+// Copyright 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_METRO_DRIVER_DIRECT3D_HELPER_
+#define WIN8_METRO_DRIVER_DIRECT3D_HELPER_
+
+#include "base/basictypes.h"
+
+#include <windows.ui.core.h>
+#include <windows.foundation.h>
+#include <d3d11_1.h>
+
+namespace metro_driver {
+
+// We need to initalize a Direct3D device and swapchain so that the browser
+// can Present to our HWND. This class takes care of creating and keeping the
+// swapchain up to date.
+class Direct3DHelper {
+ public:
+ Direct3DHelper();
+ ~Direct3DHelper();
+
+ void Initialize(winui::Core::ICoreWindow* window);
+
+ private:
+ void CreateDeviceResources();
+ void CreateWindowSizeDependentResources();
+
+ winui::Core::ICoreWindow* window_;
+
+ mswr::ComPtr<ID3D11Device1> d3d_device_;
+ mswr::ComPtr<ID3D11DeviceContext1> d3d_context_;
+ mswr::ComPtr<IDXGISwapChain1> swap_chain_;
+ D3D_FEATURE_LEVEL feature_level_;
+
+ ABI::Windows::Foundation::Rect window_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(Direct3DHelper);
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_DIRECT3D_HELPER_
diff --git a/chromium/win8/metro_driver/display_properties.cc b/chromium/win8/metro_driver/display_properties.cc
new file mode 100644
index 00000000000..a7cf42794be
--- /dev/null
+++ b/chromium/win8/metro_driver/display_properties.cc
@@ -0,0 +1,34 @@
+// Copyright 2013 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 <windows.h>
+#include <wrl\implements.h>
+#include <wrl\wrappers\corewrappers.h>
+#include <windows.foundation.h>
+#include <windows.graphics.display.h>
+#include "base/win/scoped_com_initializer.h"
+#include "winrt_utils.h"
+
+#pragma comment(lib, "runtimeobject.lib")
+
+extern "C" {
+
+ __declspec(dllexport) float GetModernUIScale() {
+ base::win::ScopedCOMInitializer com_init;
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Display::IDisplayPropertiesStatics>
+ display_properties;
+ if (SUCCEEDED(winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_Graphics_Display_DisplayProperties,
+ display_properties.GetAddressOf()))) {
+ ABI::Windows::Graphics::Display::ResolutionScale resolution_scale;
+ if (SUCCEEDED(display_properties->get_ResolutionScale(&resolution_scale)))
+ return static_cast<float>(resolution_scale) / 100.0f;
+ }
+ return 1.0f;
+}
+
+}
diff --git a/chromium/win8/metro_driver/file_picker.cc b/chromium/win8/metro_driver/file_picker.cc
new file mode 100644
index 00000000000..cf1c878b1fd
--- /dev/null
+++ b/chromium/win8/metro_driver/file_picker.cc
@@ -0,0 +1,619 @@
+// 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/files/file_path.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/metro.h"
+#include "base/win/scoped_comptr.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) {
+ if (strings_.size() > UINT_MAX)
+ return E_UNEXPECTED;
+ *size = static_cast<unsigned>(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)) {
+ UINT32 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 " << static_cast<int>(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 " << static_cast<int>(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;
+ 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 = base::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();
+
+ unsigned int 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.
+ base::FilePath base_path;
+
+ // Iterate through the collection and append the file paths to the result.
+ for (unsigned int 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;
+
+ base::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.
+ base::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 = base::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 " << static_cast<int>(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/chromium/win8/metro_driver/file_picker.h b/chromium/win8/metro_driver/file_picker.h
new file mode 100644
index 00000000000..ef56cb39082
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/file_picker_ash.cc b/chromium/win8/metro_driver/file_picker_ash.cc
new file mode 100644
index 00000000000..343d7b4f99a
--- /dev/null
+++ b/chromium/win8/metro_driver/file_picker_ash.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2013 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_ash.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/metro.h"
+#include "base/win/scoped_comptr.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/metro_driver/chrome_app_view_ash.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) {
+ if (strings_.size() > UINT_MAX)
+ return E_UNEXPECTED;
+ *size = static_cast<unsigned>(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_;
+};
+
+} // namespace
+
+FilePickerSessionBase::FilePickerSessionBase(ChromeAppViewAsh* app_view,
+ const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path)
+ : app_view_(app_view),
+ title_(title),
+ filter_(filter),
+ default_path_(default_path),
+ success_(false) {
+}
+
+bool FilePickerSessionBase::Run() {
+ if (!DoFilePicker())
+ return false;
+ return success_;
+}
+
+bool 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 = ChromeAppViewAsh::Unsnap();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to unsnap for file picker, error 0x" << hr;
+ return false;
+ }
+ hr = StartFilePicker();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to start file picker, error 0x"
+ << std::hex << hr;
+ return false;
+ }
+ return true;
+}
+
+OpenFilePickerSession::OpenFilePickerSession(
+ ChromeAppViewAsh* app_view,
+ const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multi_select)
+ : FilePickerSessionBase(app_view, title, filter, default_path),
+ allow_multi_select_(allow_multi_select) {
+}
+
+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)) {
+ UINT32 path_len = 0;
+ const wchar_t* path_str =
+ ::WindowsGetStringRawBuffer(file_path.Get(), &path_len);
+
+ result_ = path_str;
+ success_ = true;
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnOpenFileCompleted(this, success_);
+ 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)) {
+ success_ = true;
+ // The code below has been copied from the
+ // SelectFileDialogImpl::RunOpenMultiFileDialog function in
+ // select_file_dialog_win.cc.
+ // TODO(ananta)
+ // Consolidate this into a common place.
+ const wchar_t* selection = result.c_str();
+ std::vector<base::FilePath> files;
+
+ while (*selection) { // Empty string indicates end of list.
+ files.push_back(base::FilePath(selection));
+ // Skip over filename and null-terminator.
+ selection += files.back().value().length() + 1;
+ }
+ if (files.empty()) {
+ success_ = false;
+ } else if (files.size() == 1) {
+ // When there is one file, it contains the path and filename.
+ filenames_ = files;
+ } else if (files.size() > 1) {
+ // Otherwise, the first string is the path, and the remainder are
+ // filenames.
+ std::vector<base::FilePath>::iterator path = files.begin();
+ for (std::vector<base::FilePath>::iterator file = path + 1;
+ file != files.end(); ++file) {
+ filenames_.push_back(path->Append(*file));
+ }
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL StorageFileVectorCollection";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnOpenFileCompleted(this, success_);
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::StartFilePicker() {
+ 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 (filter_.empty()) {
+ 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 = filter_.c_str();
+ 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;
+ 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 = base::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 (allow_multi_select_) {
+ 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();
+
+ unsigned int 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.
+ base::FilePath base_path;
+
+ // Iterate through the collection and append the file paths to the result.
+ for (unsigned int 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;
+
+ base::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.
+ base::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(
+ ChromeAppViewAsh* app_view,
+ const MetroViewerHostMsg_SaveAsDialogParams& params)
+ : FilePickerSessionBase(app_view,
+ params.title,
+ params.filter,
+ params.suggested_name),
+ filter_index_(params.filter_index) {
+}
+
+int SaveFilePickerSession::filter_index() const {
+ // TODO(ananta)
+ // Add support for returning the correct filter index. This does not work in
+ // regular Chrome metro on trunk as well.
+ // BUG: https://code.google.com/p/chromium/issues/detail?id=172704
+ return filter_index_;
+}
+
+HRESULT SaveFilePickerSession::StartFilePicker() {
+ 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 (!filter_.empty()) {
+ // 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 = filter_.c_str();
+ 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 = base::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 (!default_path_.empty()) {
+ string16 file_part = default_path_.BaseName().value();
+ // If the suggested_name is a root directory, then don't set it as the
+ // suggested name.
+ if (file_part.size() == 1 && file_part[0] == L'\\')
+ file_part.clear();
+ hr = picker->put_SuggestedFileName(
+ mswrw::HStringReference(file_part.c_str()).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());
+ result_ = path_str;
+ success_ = true;
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnSaveFileCompleted(this, success_);
+ return S_OK;
+}
+
+FolderPickerSession::FolderPickerSession(ChromeAppViewAsh* app_view,
+ const string16& title)
+ : FilePickerSessionBase(app_view, title, L"", base::FilePath()) {}
+
+HRESULT FolderPickerSession::StartFilePicker() {
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FolderPicker);
+
+ // Create the folder picker.
+ mswr::ComPtr<winstorage::Pickers::IFolderPicker> 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;
+
+ hr = filter->Append(mswrw::HStringReference(L"*").Get());
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<FolderPickerAsyncOp> completion;
+ hr = picker->PickSingleFolderAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFolder*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &FolderPickerSession::FolderPickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+ return hr;
+}
+
+HRESULT FolderPickerSession::FolderPickerDone(FolderPickerAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFolder> folder;
+ HRESULT hr = async->GetResults(folder.GetAddressOf());
+
+ if (folder) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = folder.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());
+ result_ = path_str;
+ success_ = true;
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnFolderPickerCompleted(this, success_);
+ return S_OK;
+}
+
diff --git a/chromium/win8/metro_driver/file_picker_ash.h b/chromium/win8/metro_driver/file_picker_ash.h
new file mode 100644
index 00000000000..b08115698d5
--- /dev/null
+++ b/chromium/win8/metro_driver/file_picker_ash.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2013 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_ASH_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_ASH_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+
+class ChromeAppViewAsh;
+struct MetroViewerHostMsg_SaveAsDialogParams;
+
+namespace base {
+class FilePath;
+}
+
+// Base class for the file pickers.
+class FilePickerSessionBase {
+ public:
+ // Creates a file picker for open_file_name.
+ explicit FilePickerSessionBase(ChromeAppViewAsh* app_view,
+ const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path);
+
+ virtual ~FilePickerSessionBase() {
+ }
+
+ // Runs the picker, returns true on success.
+ bool Run();
+
+ const string16& result() const {
+ return result_;
+ }
+
+ bool success() const {
+ return success_;
+ }
+
+ 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;
+
+ // True iff a file picker has successfully finished.
+ bool success_;
+
+ // The title of the file picker.
+ string16 title_;
+
+ // The file type filter.
+ string16 filter_;
+
+ // The starting directory/file name.
+ base::FilePath default_path_;
+
+ // Pointer to the ChromeAppViewAsh instance. We notify the ChromeAppViewAsh
+ // instance when the file open/save operations complete.
+ ChromeAppViewAsh* app_view_;
+
+ string16 result_;
+
+ private:
+ // Initiate a file picker, must be called on the main metro thread.
+ // Returns true on success.
+ bool DoFilePicker();
+
+ DISALLOW_COPY_AND_ASSIGN(FilePickerSessionBase);
+};
+
+// Provides functionality to display the open file/multiple open file pickers
+// metro dialog.
+class OpenFilePickerSession : public FilePickerSessionBase {
+ public:
+ explicit OpenFilePickerSession(ChromeAppViewAsh* app_view,
+ const string16& title,
+ const string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multi_select);
+
+ const std::vector<base::FilePath>& filenames() const {
+ return filenames_;
+ }
+
+ const bool allow_multi_select() const {
+ return allow_multi_select_;
+ }
+
+ 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:
+ // True if the multi file picker is to be displayed.
+ bool allow_multi_select_;
+ // If multi select is true then this member contains the list of filenames
+ // to be returned back.
+ std::vector<base::FilePath> filenames_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenFilePickerSession);
+};
+
+// Provides functionality to display the save file picker.
+class SaveFilePickerSession : public FilePickerSessionBase {
+ public:
+ explicit SaveFilePickerSession(
+ ChromeAppViewAsh* app_view,
+ const MetroViewerHostMsg_SaveAsDialogParams& params);
+
+ int SaveFilePickerSession::filter_index() const;
+
+ 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);
+
+ int filter_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(SaveFilePickerSession);
+};
+
+// Provides functionality to display the folder picker.
+class FolderPickerSession : public FilePickerSessionBase {
+ public:
+ explicit FolderPickerSession(ChromeAppViewAsh* app_view,
+ const string16& title);
+
+ private:
+ virtual HRESULT StartFilePicker() OVERRIDE;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFolder*>
+ FolderPickerAsyncOp;
+
+ // Called asynchronously when the folder picker is done.
+ HRESULT FolderPickerDone(FolderPickerAsyncOp* async, AsyncStatus status);
+
+ DISALLOW_COPY_AND_ASSIGN(FolderPickerSession);
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_ASH_H_
+
diff --git a/chromium/win8/metro_driver/ime/ime.gypi b/chromium/win8/metro_driver/ime/ime.gypi
new file mode 100644
index 00000000000..ba9db464b74
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/ime.gypi
@@ -0,0 +1,22 @@
+# Copyright (c) 2013 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.
+
+{
+ 'sources': [
+ 'ime_popup_monitor.cc',
+ 'ime_popup_monitor.h',
+ 'ime_popup_observer.h',
+ 'input_scope.cc',
+ 'input_scope.h',
+ 'input_source.cc',
+ 'input_source.h',
+ 'input_source_observer.h',
+ 'text_service.cc',
+ 'text_service.h',
+ 'text_service_delegate.h',
+ 'text_store.cc',
+ 'text_store.h',
+ 'text_store_delegate.h',
+ ],
+}
diff --git a/chromium/win8/metro_driver/ime/ime_popup_monitor.cc b/chromium/win8/metro_driver/ime/ime_popup_monitor.cc
new file mode 100644
index 00000000000..a6368ae9e26
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/ime_popup_monitor.cc
@@ -0,0 +1,82 @@
+// Copyright 2013 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/ime/ime_popup_monitor.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "win8/metro_driver/ime/ime_popup_observer.h"
+
+namespace metro_driver {
+namespace {
+
+ImePopupObserver* g_observer_ = NULL;
+HWINEVENTHOOK g_hook_handle_ = NULL;
+
+void CALLBACK ImeEventCallback(HWINEVENTHOOK win_event_hook_handle,
+ DWORD event,
+ HWND window_handle,
+ LONG object_id,
+ LONG child_id,
+ DWORD event_thread,
+ DWORD event_time) {
+ // This function is registered to SetWinEventHook to be called back on the UI
+ // thread.
+ DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI));
+
+ if (!g_observer_)
+ return;
+ switch (event) {
+ case EVENT_OBJECT_IME_SHOW:
+ g_observer_->OnImePopupChanged(ImePopupObserver::kPopupShown);
+ return;
+ case EVENT_OBJECT_IME_HIDE:
+ g_observer_->OnImePopupChanged(ImePopupObserver::kPopupHidden);
+ return;
+ case EVENT_OBJECT_IME_CHANGE:
+ g_observer_->OnImePopupChanged(ImePopupObserver::kPopupUpdated);
+ return;
+ }
+}
+
+} // namespace
+
+void AddImePopupObserver(ImePopupObserver* observer) {
+ CHECK(g_observer_ == NULL)
+ << "Currently only one observer is supported at the same time.";
+ g_observer_ = observer;
+
+ // IMEs running under immersive mode are supposed to generate WinEvent
+ // whenever their popup UI such as candidate window is shown, updated, and
+ // hidden to support accessibility applications.
+ // http://msdn.microsoft.com/en-us/library/windows/apps/hh967425.aspx#accessibility
+ // Note that there is another mechanism in TSF, called ITfUIElementSink, to
+ // subscribe when the visibility of an IME's UI element is changed. However,
+ // MS-IME running under immersive mode does not fully support this API.
+ // Thus, WinEvent is more reliable for this purpose.
+ g_hook_handle_ = SetWinEventHook(
+ EVENT_OBJECT_IME_SHOW,
+ EVENT_OBJECT_IME_CHANGE,
+ NULL,
+ ImeEventCallback,
+ GetCurrentProcessId(), // monitor the metro_driver process only
+ 0, // hook all threads because MS-IME emits WinEvent in a worker thread
+ WINEVENT_OUTOFCONTEXT); // allows us to receive message in the UI thread
+ LOG_IF(ERROR, !g_hook_handle_) << "SetWinEventHook failed.";
+}
+
+void RemoveImePopupObserver(ImePopupObserver* observer) {
+ if (g_observer_ != observer)
+ return;
+ g_observer_ = NULL;
+ if (!g_hook_handle_)
+ return;
+ const bool unhook_succeeded = !!UnhookWinEvent(g_hook_handle_);
+ LOG_IF(ERROR, !unhook_succeeded) << "UnhookWinEvent failed.";
+ g_hook_handle_ = NULL;
+}
+
+} // namespace metro_driver
diff --git a/chromium/win8/metro_driver/ime/ime_popup_monitor.h b/chromium/win8/metro_driver/ime/ime_popup_monitor.h
new file mode 100644
index 00000000000..3bf65580e98
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/ime_popup_monitor.h
@@ -0,0 +1,19 @@
+// Copyright 2013 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_METRO_DRIVER_IME_IME_POPUP_MONITOR_H_
+#define WIN8_METRO_DRIVER_IME_IME_POPUP_MONITOR_H_
+
+namespace metro_driver {
+
+class ImePopupObserver;
+
+// Adds/Removes ImePopupObserver. Currently only one observer is supported at
+// the same time.
+void AddImePopupObserver(ImePopupObserver* observer);
+void RemoveImePopupObserver(ImePopupObserver* observer);
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_IME_POPUP_MONITOR_H_
diff --git a/chromium/win8/metro_driver/ime/ime_popup_observer.h b/chromium/win8/metro_driver/ime/ime_popup_observer.h
new file mode 100644
index 00000000000..b871084a8cc
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/ime_popup_observer.h
@@ -0,0 +1,27 @@
+// Copyright 2013 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_METRO_DRIVER_IME_IME_POPUP_OBSERVER_H_
+#define WIN8_METRO_DRIVER_IME_IME_POPUP_OBSERVER_H_
+
+namespace metro_driver {
+
+// An observer interface implemented by objects that want to be informed when
+// an IME shows or hides its popup window.
+class ImePopupObserver {
+ public:
+ enum EventType {
+ kPopupShown,
+ kPopupHidden,
+ kPopupUpdated,
+ };
+ virtual ~ImePopupObserver() {}
+
+ // Called whenever an IME's popup window is changed.
+ virtual void OnImePopupChanged(EventType type) = 0;
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_IME_POPUP_OBSERVER_H_
diff --git a/chromium/win8/metro_driver/ime/input_scope.cc b/chromium/win8/metro_driver/ime/input_scope.cc
new file mode 100644
index 00000000000..82679c3d4c9
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/input_scope.cc
@@ -0,0 +1,87 @@
+// Copyright 2013 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/ime/input_scope.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "base/logging.h"
+#include "ui/base/win/atl_module.h"
+
+namespace metro_driver {
+namespace {
+
+// An implementation of ITfInputScope interface.
+// This implementation only covers ITfInputScope::GetInputScopes since built-in
+// on-screen keyboard on Windows 8+ changes its layout depending on the returned
+// value of this method.
+// Although other advanced features of ITfInputScope such as phase list or
+// regex support might be useful for IMEs or on-screen keyboards in future,
+// no IME seems to be utilizing such features as of Windows 8.1.
+class ATL_NO_VTABLE InputScopeImpl
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public ITfInputScope {
+ public:
+ InputScopeImpl() {}
+
+ BEGIN_COM_MAP(InputScopeImpl)
+ COM_INTERFACE_ENTRY(ITfInputScope)
+ END_COM_MAP()
+
+ void Initialize(const std::vector<InputScope>& input_scopes) {
+ input_scopes_ = input_scopes;
+ }
+
+ private:
+ // ITfInputScope overrides:
+ STDMETHOD(GetInputScopes)(InputScope** input_scopes, UINT* count) OVERRIDE {
+ if (!count || !input_scopes)
+ return E_INVALIDARG;
+ *input_scopes = static_cast<InputScope*>(
+ CoTaskMemAlloc(sizeof(InputScope) * input_scopes_.size()));
+ if (!input_scopes) {
+ *count = 0;
+ return E_OUTOFMEMORY;
+ }
+ std::copy(input_scopes_.begin(), input_scopes_.end(), *input_scopes);
+ *count = static_cast<UINT>(input_scopes_.size());
+ return S_OK;
+ }
+ STDMETHOD(GetPhrase)(BSTR** phrases, UINT* count) OVERRIDE {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(GetRegularExpression)(BSTR* regexp) OVERRIDE {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(GetSRGS)(BSTR* srgs) OVERRIDE {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(GetXML)(BSTR* xml) OVERRIDE {
+ return E_NOTIMPL;
+ }
+
+ // Data which ITfInputScope::GetInputScopes should return.
+ std::vector<InputScope> input_scopes_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputScopeImpl);
+};
+
+} // namespace
+
+base::win::ScopedComPtr<ITfInputScope>
+CreteInputScope(const std::vector<InputScope>& input_scopes) {
+ ui::win::CreateATLModuleIfNeeded();
+ CComObject<InputScopeImpl>* object = NULL;
+ HRESULT hr = CComObject<InputScopeImpl>::CreateInstance(&object);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "CComObject<InputScopeImpl>::CreateInstance failed. hr = "
+ << hr;
+ return base::win::ScopedComPtr<ITfInputScope>();
+ }
+ object->Initialize(input_scopes);
+ return base::win::ScopedComPtr<ITfInputScope>(object);
+}
+
+} // namespace metro_driver
diff --git a/chromium/win8/metro_driver/ime/input_scope.h b/chromium/win8/metro_driver/ime/input_scope.h
new file mode 100644
index 00000000000..99ed283e7c7
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/input_scope.h
@@ -0,0 +1,22 @@
+// Copyright 2013 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_METRO_DRIVER_IME_INPUT_SCOPE_H_
+#define WIN8_METRO_DRIVER_IME_INPUT_SCOPE_H_
+
+#include <InputScope.h>
+#include <vector>
+
+#include "base/win/scoped_comptr.h"
+
+namespace metro_driver {
+
+// Returns an instance of ITfInputScope object, which represents an array of
+// InputScope enumeration passed as |input_scopes|.
+base::win::ScopedComPtr<ITfInputScope>
+CreteInputScope(const std::vector<InputScope>& input_scopes);
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_TEXT_STORE_H_
diff --git a/chromium/win8/metro_driver/ime/input_source.cc b/chromium/win8/metro_driver/ime/input_source.cc
new file mode 100644
index 00000000000..dd27380108d
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/input_source.cc
@@ -0,0 +1,170 @@
+// Copyright 2013 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/ime/input_source.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <msctf.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/win/scoped_comptr.h"
+#include "ui/base/win/atl_module.h"
+#include "win8/metro_driver/ime/input_source_observer.h"
+
+namespace metro_driver {
+namespace {
+
+// An implementation of ITfLanguageProfileNotifySink interface, which will be
+// used to receive notifications when the text input source is changed.
+class ATL_NO_VTABLE InputSourceMonitor
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public ITfLanguageProfileNotifySink {
+ public:
+ InputSourceMonitor()
+ : cookie_(TF_INVALID_COOKIE) {
+ }
+
+ BEGIN_COM_MAP(InputSourceMonitor)
+ COM_INTERFACE_ENTRY(ITfLanguageProfileNotifySink)
+ END_COM_MAP()
+
+ bool Initialize(ITfSource* source) {
+ DWORD cookie = TF_INVALID_COOKIE;
+ HRESULT hr = source->AdviseSink(IID_ITfLanguageProfileNotifySink,
+ this,
+ &cookie);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfSource::AdviseSink failed. hr = " << hr;
+ return false;
+ }
+ cookie_ = cookie;
+ source_ = source;
+ return true;
+ }
+
+ void SetCallback(base::Closure on_language_chanaged) {
+ on_language_chanaged_ = on_language_chanaged;
+ }
+
+ void Unadvise() {
+ if (cookie_ == TF_INVALID_COOKIE || !source_)
+ return;
+ if (FAILED(source_->UnadviseSink(cookie_)))
+ return;
+ cookie_ = TF_INVALID_COOKIE;
+ source_.Release();
+ }
+
+ private:
+ // ITfLanguageProfileNotifySink overrides:
+ STDMETHOD(OnLanguageChange)(LANGID langid, BOOL *accept) OVERRIDE {
+ if (!accept)
+ return E_INVALIDARG;
+ *accept = TRUE;
+ return S_OK;
+ }
+
+ STDMETHOD(OnLanguageChanged)() OVERRIDE {
+ if (!on_language_chanaged_.is_null())
+ on_language_chanaged_.Run();
+ return S_OK;
+ }
+
+ base::Closure on_language_chanaged_;
+ base::win::ScopedComPtr<ITfSource> source_;
+ DWORD cookie_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputSourceMonitor);
+};
+
+class InputSourceImpl : public InputSource {
+ public:
+ InputSourceImpl(ITfInputProcessorProfileMgr* profile_manager,
+ InputSourceMonitor* monitor)
+ : profile_manager_(profile_manager),
+ monitor_(monitor) {
+ monitor_->SetCallback(base::Bind(&InputSourceImpl::OnLanguageChanged,
+ base::Unretained(this)));
+ }
+ virtual ~InputSourceImpl() {
+ monitor_->SetCallback(base::Closure());
+ monitor_->Unadvise();
+ }
+
+ private:
+ // InputSource overrides.
+ virtual bool GetActiveSource(LANGID* langid, bool* is_ime) OVERRIDE {
+ TF_INPUTPROCESSORPROFILE profile = {};
+ HRESULT hr = profile_manager_->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD,
+ &profile);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfInputProcessorProfileMgr::GetActiveProfile failed."
+ << " hr = " << hr;
+ return false;
+ }
+ *langid = profile.langid;
+ *is_ime = profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR;
+ return true;
+ }
+ virtual void AddObserver(InputSourceObserver* observer) OVERRIDE {
+ observer_list_.AddObserver(observer);
+ }
+ virtual void RemoveObserver(InputSourceObserver* observer) OVERRIDE {
+ observer_list_.RemoveObserver(observer);
+ }
+ void OnLanguageChanged() {
+ FOR_EACH_OBSERVER(InputSourceObserver,
+ observer_list_,
+ OnInputSourceChanged());
+ }
+
+ base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager_;
+ scoped_refptr<InputSourceMonitor> monitor_;
+ ObserverList<InputSourceObserver> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputSourceImpl);
+};
+
+} // namespace
+
+// static
+scoped_ptr<InputSource> InputSource::Create() {
+ ui::win::CreateATLModuleIfNeeded();
+
+ base::win::ScopedComPtr<ITfInputProcessorProfileMgr> profile_manager;
+ HRESULT hr = profile_manager.CreateInstance(CLSID_TF_InputProcessorProfiles);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to instantiate CLSID_TF_InputProcessorProfiles."
+ << " hr = " << hr;
+ return scoped_ptr<InputSource>();
+ }
+ base::win::ScopedComPtr<ITfSource> profiles_source;
+ hr = profiles_source.QueryFrom(profile_manager);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "QueryFrom to ITfSource failed. hr = " << hr;
+ return scoped_ptr<InputSource>();
+ }
+
+ CComObject<InputSourceMonitor>* monitor = NULL;
+ hr = CComObject<InputSourceMonitor>::CreateInstance(&monitor);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "CComObject<InputSourceMonitor>::CreateInstance failed."
+ << " hr = " << hr;
+ return scoped_ptr<InputSource>();
+ }
+ if (!monitor->Initialize(profiles_source)) {
+ LOG(ERROR) << "Failed to initialize the monitor.";
+ return scoped_ptr<InputSource>();
+ }
+
+ // Transfer the ownership.
+ return scoped_ptr<InputSource>(new InputSourceImpl(profile_manager, monitor));
+}
+
+} // namespace metro_driver
diff --git a/chromium/win8/metro_driver/ime/input_source.h b/chromium/win8/metro_driver/ime/input_source.h
new file mode 100644
index 00000000000..8b057b2e083
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/input_source.h
@@ -0,0 +1,36 @@
+// Copyright 2013 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_METRO_DRIVER_IME_INPUT_SOURCE_H_
+#define WIN8_METRO_DRIVER_IME_INPUT_SOURCE_H_
+
+#include <Windows.h>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace metro_driver {
+
+class InputSourceObserver;
+
+// An interface through which information about the input source can be
+// retrieved, where an input source represents an IME or a keyboard layout.
+class InputSource {
+ public:
+ virtual ~InputSource() {}
+ // Create an instance. Returns NULL if fails.
+ static scoped_ptr<InputSource> Create();
+
+ // Returns true if |langid| and |is_ime| are filled based on the current
+ // active input source.
+ virtual bool GetActiveSource(LANGID* langid, bool* is_ime) = 0;
+
+ // Adds/Removes an observer to receive notifications when the active input
+ // source is changed.
+ virtual void AddObserver(InputSourceObserver* observer) = 0;
+ virtual void RemoveObserver(InputSourceObserver* observer) = 0;
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_INPUT_SOURCE_H_
diff --git a/chromium/win8/metro_driver/ime/input_source_observer.h b/chromium/win8/metro_driver/ime/input_source_observer.h
new file mode 100644
index 00000000000..4be77a558b7
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/input_source_observer.h
@@ -0,0 +1,26 @@
+// Copyright 2013 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_METRO_DRIVER_IME_INPUT_SOURCE_OBSERVER_H_
+#define WIN8_METRO_DRIVER_IME_INPUT_SOURCE_OBSERVER_H_
+
+#include <Windows.h>
+
+#include "base/basictypes.h"
+
+namespace metro_driver {
+
+// An observer interface implemented by objects that want to be informed
+// when the active language profile is changed.
+class InputSourceObserver {
+ public:
+ virtual ~InputSourceObserver() {}
+
+ // Called when the active language profile is changed.
+ virtual void OnInputSourceChanged() = 0;
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_INPUT_SOURCE_OBSERVER_H_
diff --git a/chromium/win8/metro_driver/ime/text_service.cc b/chromium/win8/metro_driver/ime/text_service.cc
new file mode 100644
index 00000000000..7147d201f79
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/text_service.cc
@@ -0,0 +1,493 @@
+// Copyright 2013 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/ime/text_service.h"
+
+#include <msctf.h>
+
+#include "base/logging.h"
+#include "base/win/scoped_variant.h"
+#include "ui/metro_viewer/ime_types.h"
+#include "win8/metro_driver/ime/text_service_delegate.h"
+#include "win8/metro_driver/ime/text_store.h"
+#include "win8/metro_driver/ime/text_store_delegate.h"
+
+// Architecture overview of input method support on Ash mode:
+//
+// Overview:
+// On Ash mode, the system keyboard focus is owned by the metro_driver process
+// while most of event handling are still implemented in the browser process.
+// Thus the metro_driver basically works as a proxy that simply forwards
+// keyevents to the metro_driver process. IME support must be involved somewhere
+// in this flow.
+//
+// In short, we need to interact with an IME in the metro_driver process since
+// TSF (Text Services Framework) runtime wants to processes keyevents while
+// (and only while) the attached UI thread owns keyboard focus.
+//
+// Due to this limitation, we need to split IME handling into two parts, one
+// is in the metro_driver process and the other is in the browser process.
+// The metro_driver process is responsible for implementing the primary data
+// store for the composition text and wiring it up with an IME via TSF APIs.
+// On the other hand, the browser process is responsible for calculating
+// character position in the composition text whenever the composition text
+// is updated.
+//
+// IPC overview:
+// Fortunately, we don't need so many IPC messages to support IMEs. In fact,
+// only 4 messages are required to enable basic IME functionality.
+//
+// metro_driver process -> browser process
+// Message Type:
+// - MetroViewerHostMsg_ImeCompositionChanged
+// - MetroViewerHostMsg_ImeTextCommitted
+// Message Routing:
+// TextServiceImpl
+// -> ChromeAppViewAsh
+// -- (process boundary) --
+// -> RemoteRootWindowHostWin
+// -> RemoteInputMethodWin
+//
+// browser process -> metro_driver process
+// Message Type:
+// - MetroViewerHostMsg_ImeCancelComposition
+// - MetroViewerHostMsg_ImeTextInputClientUpdated
+// Message Routing:
+// RemoteInputMethodWin
+// -> RemoteRootWindowHostWin
+// -- (process boundary) --
+// -> ChromeAppViewAsh
+// -> TextServiceImpl
+//
+// Note that a keyevent may be forwarded through a different path. When a
+// keyevent is not handled by an IME, such keyevent and subsequent character
+// events will be sent from the metro_driver process to the browser process as
+// following IPC messages.
+// - MetroViewerHostMsg_KeyDown
+// - MetroViewerHostMsg_KeyUp
+// - MetroViewerHostMsg_Character
+//
+// How TextServiceImpl works:
+// Here is the list of the major tasks that are handled in TextServiceImpl.
+// - Manages a session object obtained from TSF runtime. We need them to call
+// most of TSF APIs.
+// - Handles OnDocumentChanged event. Whenever the document type is changed,
+// TextServiceImpl destroyes the current document and initializes new one
+// according to the given |input_scopes|.
+// - Stores the |composition_character_bounds_| passed from OnDocumentChanged
+// event so that an IME or on-screen keyboard can query the character
+// position synchronously.
+// The most complicated part is the OnDocumentChanged handler. Since some IMEs
+// such as Japanese IMEs drastically change their behavior depending on
+// properties exposed from the virtual document, we need to set up a lot
+// properties carefully and correctly. See DocumentBinding class in this file
+// about what will be involved in this multi-phase construction. See also
+// text_store.cc and input_scope.cc for more underlying details.
+
+namespace metro_driver {
+namespace {
+
+// Japanese IME expects the default value of this compartment is
+// TF_SENTENCEMODE_PHRASEPREDICT to emulate IMM32 behavior. This value is
+// managed per thread, thus setting this value at once is sufficient. This
+// value never affects non-Japanese IMEs.
+bool InitializeSentenceMode(ITfThreadMgr2* thread_manager,
+ TfClientId client_id) {
+ base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager;
+ HRESULT hr = thread_compartment_manager.QueryFrom(thread_manager);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "QueryFrom failed. hr = " << hr;
+ return false;
+ }
+ base::win::ScopedComPtr<ITfCompartment> sentence_compartment;
+ hr = thread_compartment_manager->GetCompartment(
+ GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE,
+ sentence_compartment.Receive());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
+ return false;
+ }
+
+ base::win::ScopedVariant sentence_variant;
+ sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
+ hr = sentence_compartment->SetValue(client_id, &sentence_variant);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
+ return false;
+ }
+ return true;
+}
+
+// Initializes |context| as disabled context where IMEs will be disabled.
+bool InitializeDisabledContext(ITfContext* context, TfClientId client_id) {
+ base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr;
+ HRESULT hr = compartment_mgr.QueryFrom(context);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "QueryFrom failed. hr = " << hr;
+ return false;
+ }
+
+ base::win::ScopedComPtr<ITfCompartment> disabled_compartment;
+ hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED,
+ disabled_compartment.Receive());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
+ return false;
+ }
+
+ base::win::ScopedVariant variant;
+ variant.Set(1);
+ hr = disabled_compartment->SetValue(client_id, &variant);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
+ return false;
+ }
+
+ base::win::ScopedComPtr<ITfCompartment> empty_context;
+ hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
+ empty_context.Receive());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
+ return false;
+ }
+
+ base::win::ScopedVariant empty_context_variant;
+ empty_context_variant.Set(static_cast<int32>(1));
+ hr = empty_context->SetValue(client_id, &empty_context_variant);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
+ return false;
+ }
+
+ return true;
+}
+
+bool IsPasswordField(const std::vector<InputScope>& input_scopes) {
+ return std::find(input_scopes.begin(), input_scopes.end(), IS_PASSWORD) !=
+ input_scopes.end();
+}
+
+// A class that manages the lifetime of the event callback registration. When
+// this object is destroyed, corresponding event callback will be unregistered.
+class EventSink {
+ public:
+ EventSink(DWORD cookie, base::win::ScopedComPtr<ITfSource> source)
+ : cookie_(cookie),
+ source_(source) {}
+ ~EventSink() {
+ if (!source_ || cookie_ != TF_INVALID_COOKIE)
+ return;
+ source_->UnadviseSink(cookie_);
+ cookie_ = TF_INVALID_COOKIE;
+ source_.Release();
+ }
+
+ private:
+ DWORD cookie_;
+ base::win::ScopedComPtr<ITfSource> source_;
+ DISALLOW_COPY_AND_ASSIGN(EventSink);
+};
+
+scoped_ptr<EventSink> CreateTextEditSink(ITfContext* context,
+ ITfTextEditSink* text_store) {
+ DCHECK(text_store);
+ base::win::ScopedComPtr<ITfSource> source;
+ DWORD cookie = TF_INVALID_EDIT_COOKIE;
+ HRESULT hr = source.QueryFrom(context);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "QueryFrom failed, hr = " << hr;
+ return scoped_ptr<EventSink>();
+ }
+ hr = source->AdviseSink(IID_ITfTextEditSink, text_store, &cookie);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "AdviseSink failed, hr = " << hr;
+ return scoped_ptr<EventSink>();
+ }
+ return scoped_ptr<EventSink>(new EventSink(cookie, source));
+}
+
+// A set of objects that should have the same lifetime. Following things
+// are maintained.
+// - TextStore: a COM object that abstracts text buffer. This object is
+// actually implemented by us in text_store.cc
+// - ITfDocumentMgr: a focusable unit in TSF. This object is implemented by
+// TSF runtime and works as a container of TextStore.
+// - EventSink: an object that ensures that the event callback between
+// TSF runtime and TextStore is unregistered when this object is destroyed.
+class DocumentBinding {
+ public:
+ ~DocumentBinding() {
+ if (!document_manager_)
+ return;
+ document_manager_->Pop(TF_POPF_ALL);
+ }
+
+ static scoped_ptr<DocumentBinding> Create(
+ ITfThreadMgr2* thread_manager,
+ TfClientId client_id,
+ const std::vector<InputScope>& input_scopes,
+ HWND window_handle,
+ TextStoreDelegate* delegate) {
+ base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
+ HRESULT hr = thread_manager->CreateDocumentMgr(document_manager.Receive());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfThreadMgr2::CreateDocumentMgr failed. hr = " << hr;
+ return scoped_ptr<DocumentBinding>();
+ }
+
+ // Note: In our IPC protocol, an empty |input_scopes| is used to indicate
+ // that an IME must be disabled in this context. In such case, we need not
+ // instantiate TextStore.
+ const bool use_null_text_store = input_scopes.empty();
+
+ scoped_refptr<TextStore> text_store;
+ if (!use_null_text_store) {
+ text_store = TextStore::Create(window_handle, input_scopes, delegate);
+ if (!text_store) {
+ LOG(ERROR) << "Failed to create TextStore.";
+ return scoped_ptr<DocumentBinding>();
+ }
+ }
+
+ base::win::ScopedComPtr<ITfContext> context;
+ DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
+ hr = document_manager->CreateContext(
+ client_id,
+ 0,
+ static_cast<ITextStoreACP*>(text_store.get()),
+ context.Receive(),
+ &edit_cookie);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfDocumentMgr::CreateContext failed. hr = " << hr;
+ return scoped_ptr<DocumentBinding>();
+ }
+
+ // If null-TextStore is used or |input_scopes| looks like a password field,
+ // set special properties to tell IMEs to be disabled.
+ if ((use_null_text_store || IsPasswordField(input_scopes)) &&
+ !InitializeDisabledContext(context, client_id)) {
+ LOG(ERROR) << "InitializeDisabledContext failed.";
+ return scoped_ptr<DocumentBinding>();
+ }
+
+ scoped_ptr<EventSink> text_edit_sink;
+ if (!use_null_text_store) {
+ text_edit_sink = CreateTextEditSink(context, text_store);
+ if (!text_edit_sink) {
+ LOG(ERROR) << "CreateTextEditSink failed.";
+ return scoped_ptr<DocumentBinding>();
+ }
+ }
+ hr = document_manager->Push(context);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfDocumentMgr::Push failed. hr = " << hr;
+ return scoped_ptr<DocumentBinding>();
+ }
+ return scoped_ptr<DocumentBinding>(
+ new DocumentBinding(text_store,
+ document_manager,
+ text_edit_sink.Pass()));
+ }
+
+ ITfDocumentMgr* document_manager() const {
+ return document_manager_;
+ }
+
+ scoped_refptr<TextStore> text_store() const {
+ return text_store_;
+ }
+
+ private:
+ DocumentBinding(scoped_refptr<TextStore> text_store,
+ base::win::ScopedComPtr<ITfDocumentMgr> document_manager,
+ scoped_ptr<EventSink> text_edit_sink)
+ : text_store_(text_store),
+ document_manager_(document_manager),
+ text_edit_sink_(text_edit_sink.Pass()) {}
+
+ scoped_refptr<TextStore> text_store_;
+ base::win::ScopedComPtr<ITfDocumentMgr> document_manager_;
+ scoped_ptr<EventSink> text_edit_sink_;
+
+ DISALLOW_COPY_AND_ASSIGN(DocumentBinding);
+};
+
+class TextServiceImpl : public TextService,
+ public TextStoreDelegate {
+ public:
+ TextServiceImpl(ITfThreadMgr2* thread_manager,
+ TfClientId client_id,
+ HWND window_handle,
+ TextServiceDelegate* delegate)
+ : client_id_(client_id),
+ window_handle_(window_handle),
+ delegate_(delegate),
+ thread_manager_(thread_manager) {
+ DCHECK_NE(TF_CLIENTID_NULL, client_id);
+ DCHECK(window_handle != NULL);
+ DCHECK(thread_manager_);
+ }
+ virtual ~TextServiceImpl() {
+ thread_manager_->Deactivate();
+ }
+
+ private:
+ // TextService overrides:
+ virtual void TextService::CancelComposition() OVERRIDE {
+ if (!current_document_) {
+ VLOG(0) << "|current_document_| is NULL due to the previous error.";
+ return;
+ }
+ TextStore* text_store = current_document_->text_store();
+ if (!text_store)
+ return;
+ text_store->CancelComposition();
+ }
+
+ virtual void OnDocumentChanged(
+ const std::vector<int32>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds)
+ OVERRIDE {
+ bool document_type_changed = input_scopes_ != input_scopes;
+ input_scopes_ = input_scopes;
+ composition_character_bounds_ = character_bounds;
+ if (document_type_changed)
+ OnDocumentTypeChanged(input_scopes);
+ }
+
+ virtual void OnWindowActivated() OVERRIDE {
+ if (!current_document_) {
+ VLOG(0) << "|current_document_| is NULL due to the previous error.";
+ return;
+ }
+ ITfDocumentMgr* document_manager = current_document_->document_manager();
+ if (!document_manager) {
+ VLOG(0) << "|document_manager| is NULL due to the previous error.";
+ return;
+ }
+ HRESULT hr = thread_manager_->SetFocus(document_manager);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfThreadMgr2::SetFocus failed. hr = " << hr;
+ return;
+ }
+ }
+
+ virtual void OnCompositionChanged(
+ const string16& text,
+ int32 selection_start,
+ int32 selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) OVERRIDE {
+ if (!delegate_)
+ return;
+ delegate_->OnCompositionChanged(text,
+ selection_start,
+ selection_end,
+ underlines);
+ }
+
+ virtual void OnTextCommitted(const string16& text) OVERRIDE {
+ if (!delegate_)
+ return;
+ delegate_->OnTextCommitted(text);
+ }
+
+ virtual RECT GetCaretBounds() {
+ if (composition_character_bounds_.empty()) {
+ const RECT rect = {};
+ return rect;
+ }
+ const metro_viewer::CharacterBounds& bounds =
+ composition_character_bounds_[0];
+ POINT left_top = { bounds.left, bounds.top };
+ POINT right_bottom = { bounds.right, bounds.bottom };
+ ClientToScreen(window_handle_, &left_top);
+ ClientToScreen(window_handle_, &right_bottom);
+ const RECT rect = {
+ left_top.x,
+ left_top.y,
+ right_bottom.x,
+ right_bottom.y,
+ };
+ return rect;
+ }
+
+ virtual bool GetCompositionCharacterBounds(uint32 index,
+ RECT* rect) OVERRIDE {
+ if (index >= composition_character_bounds_.size()) {
+ return false;
+ }
+ const metro_viewer::CharacterBounds& bounds =
+ composition_character_bounds_[index];
+ POINT left_top = { bounds.left, bounds.top };
+ POINT right_bottom = { bounds.right, bounds.bottom };
+ ClientToScreen(window_handle_, &left_top);
+ ClientToScreen(window_handle_, &right_bottom);
+ SetRect(rect, left_top.x, left_top.y, right_bottom.x, right_bottom.y);
+ return true;
+ }
+
+ void OnDocumentTypeChanged(const std::vector<int32>& input_scopes) {
+ std::vector<InputScope> native_input_scopes(input_scopes.size());
+ for (size_t i = 0; i < input_scopes.size(); ++i)
+ native_input_scopes[i] = static_cast<InputScope>(input_scopes[i]);
+ scoped_ptr<DocumentBinding> new_document =
+ DocumentBinding::Create(thread_manager_.get(),
+ client_id_,
+ native_input_scopes,
+ window_handle_,
+ this);
+ LOG_IF(ERROR, !new_document) << "Failed to create a new document.";
+ current_document_.swap(new_document);
+ OnWindowActivated();
+ }
+
+ TfClientId client_id_;
+ HWND window_handle_;
+ TextServiceDelegate* delegate_;
+ scoped_ptr<DocumentBinding> current_document_;
+ base::win::ScopedComPtr<ITfThreadMgr2> thread_manager_;
+
+ // A vector of InputScope enumeration, which represents the document type of
+ // the focused text field. Note that in our IPC message protocol, an empty
+ // |input_scopes_| has special meaning that IMEs must be disabled on this
+ // document.
+ std::vector<int32> input_scopes_;
+ // Character bounds of the composition. When there is no composition but this
+ // vector is not empty, the first element contains the caret bounds.
+ std::vector<metro_viewer::CharacterBounds> composition_character_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextServiceImpl);
+};
+
+} // namespace
+
+scoped_ptr<TextService>
+CreateTextService(TextServiceDelegate* delegate, HWND window_handle) {
+ if (!delegate)
+ return scoped_ptr<TextService>();
+ base::win::ScopedComPtr<ITfThreadMgr2> thread_manager;
+ HRESULT hr = thread_manager.CreateInstance(CLSID_TF_ThreadMgr);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to create instance of CLSID_TF_ThreadMgr. hr = "
+ << hr;
+ return scoped_ptr<TextService>();
+ }
+ TfClientId client_id = TF_CLIENTID_NULL;
+ hr = thread_manager->ActivateEx(&client_id, 0);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "ITfThreadMgr2::ActivateEx failed. hr = " << hr;
+ return scoped_ptr<TextService>();
+ }
+ if (!InitializeSentenceMode(thread_manager, client_id)) {
+ LOG(ERROR) << "InitializeSentenceMode failed.";
+ thread_manager->Deactivate();
+ return scoped_ptr<TextService>();
+ }
+ return scoped_ptr<TextService>(new TextServiceImpl(thread_manager,
+ client_id,
+ window_handle,
+ delegate));
+}
+
+} // namespace metro_driver
diff --git a/chromium/win8/metro_driver/ime/text_service.h b/chromium/win8/metro_driver/ime/text_service.h
new file mode 100644
index 00000000000..6ddd21f3ccb
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/text_service.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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_METRO_DRIVER_IME_TEXT_SERVICE_H_
+#define WIN8_METRO_DRIVER_IME_TEXT_SERVICE_H_
+
+#include <Windows.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace metro_viewer {
+struct CharacterBounds;
+}
+
+namespace metro_driver {
+
+class TextServiceDelegate;
+
+// An interface to manage a virtual text store with which an IME communicates.
+class TextService {
+ public:
+ virtual ~TextService() {}
+
+ // Cancels on-going composition. Does nothing if there is no composition.
+ virtual void CancelComposition() = 0;
+
+ // Updates document type with |input_scopes| and caret/composition position
+ // with |character_bounds|. An empty |input_scopes| indicates that IMEs
+ // should be disabled until non-empty |input_scopes| is specified.
+ // Note: |input_scopes| is defined as std::vector<int32> here rather than
+ // std::vector<InputScope> because the wire format of IPC message
+ // MetroViewerHostMsg_ImeTextInputClientUpdated uses std::vector<int32> to
+ // avoid dependency on <InputScope.h> header.
+ virtual void OnDocumentChanged(
+ const std::vector<int32>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds) = 0;
+
+ // Must be called whenever the attached window gains keyboard focus.
+ virtual void OnWindowActivated() = 0;
+};
+
+// Returns an instance of TextService that works together with
+// |text_store_delegate| as if it was an text area owned by |window_handle|.
+scoped_ptr<TextService>
+CreateTextService(TextServiceDelegate* delegate, HWND window_handle);
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_TEXT_SERVICE_H_
diff --git a/chromium/win8/metro_driver/ime/text_service_delegate.h b/chromium/win8/metro_driver/ime/text_service_delegate.h
new file mode 100644
index 00000000000..ea9cde72ec5
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/text_service_delegate.h
@@ -0,0 +1,40 @@
+// Copyright 2013 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_METRO_DRIVER_IME_TEXT_SERVICE_DELEGATE_H_
+#define WIN8_METRO_DRIVER_IME_TEXT_SERVICE_DELEGATE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+
+namespace metro_viewer {
+struct UnderlineInfo;
+}
+
+namespace metro_driver {
+
+// A delegate which works together with virtual text service.
+// Objects that implement this delegate will receive notifications from a
+// virtual text service whenever an IME updates the composition or commits text.
+class TextServiceDelegate {
+ public:
+ virtual ~TextServiceDelegate() {}
+
+ // Called when on-going composition is updated. An empty |text| represents
+ // that the composition is canceled.
+ virtual void OnCompositionChanged(
+ const string16& text,
+ int32 selection_start,
+ int32 selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) = 0;
+
+ // Called when |text| is committed.
+ virtual void OnTextCommitted(const string16& text) = 0;
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_TEXT_SERVICE_DELEGATE_H_
diff --git a/chromium/win8/metro_driver/ime/text_store.cc b/chromium/win8/metro_driver/ime/text_store.cc
new file mode 100644
index 00000000000..a8f0d95e6c1
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/text_store.cc
@@ -0,0 +1,897 @@
+// Copyright 2013 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/ime/text_store.h"
+
+#include <algorithm>
+
+#include "base/win/scoped_variant.h"
+#include "ui/base/win/atl_module.h"
+#include "win8/metro_driver/ime/input_scope.h"
+#include "win8/metro_driver/ime/text_store_delegate.h"
+
+namespace metro_driver {
+namespace {
+
+// We support only one view.
+const TsViewCookie kViewCookie = 1;
+
+} // namespace
+
+TextStore::TextStore()
+ : text_store_acp_sink_mask_(0),
+ window_handle_(NULL),
+ delegate_(NULL),
+ committed_size_(0),
+ selection_start_(0),
+ selection_end_(0),
+ edit_flag_(false),
+ current_lock_type_(0),
+ category_manager_(NULL),
+ display_attribute_manager_(NULL),
+ input_scope_(NULL) {
+}
+
+TextStore::~TextStore() {
+}
+
+// static
+scoped_refptr<TextStore> TextStore::Create(
+ HWND window_handle,
+ const std::vector<InputScope>& input_scopes,
+ TextStoreDelegate* delegate) {
+ if (!delegate) {
+ LOG(ERROR) << "|delegate| must be non-NULL.";
+ return scoped_refptr<TextStore>();
+ }
+ base::win::ScopedComPtr<ITfCategoryMgr> category_manager;
+ HRESULT hr = category_manager.CreateInstance(CLSID_TF_CategoryMgr);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to initialize CategoryMgr. hr = " << hr;
+ return scoped_refptr<TextStore>();
+ }
+ base::win::ScopedComPtr<ITfDisplayAttributeMgr> display_attribute_manager;
+ hr = display_attribute_manager.CreateInstance(CLSID_TF_DisplayAttributeMgr);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to initialize DisplayAttributeMgr. hr = " << hr;
+ return scoped_refptr<TextStore>();
+ }
+ base::win::ScopedComPtr<ITfInputScope> input_scope =
+ CreteInputScope(input_scopes);
+ if (!input_scope) {
+ LOG(ERROR) << "Failed to initialize InputScope.";
+ return scoped_refptr<TextStore>();
+ }
+
+ ui::win::CreateATLModuleIfNeeded();
+ CComObject<TextStore>* object = NULL;
+ hr = CComObject<TextStore>::CreateInstance(&object);
+ if (FAILED(hr)) {
+ LOG(ERROR) << "CComObject<TextStore>::CreateInstance failed. hr = "
+ << hr;
+ return scoped_refptr<TextStore>();
+ }
+ object->Initialize(window_handle,
+ category_manager,
+ display_attribute_manager,
+ input_scope,
+ delegate);
+ return scoped_refptr<TextStore>(object);
+}
+
+void TextStore::Initialize(HWND window_handle,
+ ITfCategoryMgr* category_manager,
+ ITfDisplayAttributeMgr* display_attribute_manager,
+ ITfInputScope* input_scope,
+ TextStoreDelegate* delegate) {
+ window_handle_ = window_handle;
+ category_manager_ = category_manager;
+ display_attribute_manager_ = display_attribute_manager;
+ input_scope_ = input_scope;
+ delegate_ = delegate;
+}
+
+
+STDMETHODIMP TextStore::AdviseSink(REFIID iid,
+ IUnknown* unknown,
+ DWORD mask) {
+ if (!IsEqualGUID(iid, IID_ITextStoreACPSink))
+ return E_INVALIDARG;
+ if (text_store_acp_sink_) {
+ if (text_store_acp_sink_.IsSameObject(unknown)) {
+ text_store_acp_sink_mask_ = mask;
+ return S_OK;
+ } else {
+ return CONNECT_E_ADVISELIMIT;
+ }
+ }
+ if (FAILED(text_store_acp_sink_.QueryFrom(unknown)))
+ return E_UNEXPECTED;
+ text_store_acp_sink_mask_ = mask;
+
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::FindNextAttrTransition(
+ LONG acp_start,
+ LONG acp_halt,
+ ULONG num_filter_attributes,
+ const TS_ATTRID* filter_attributes,
+ DWORD flags,
+ LONG* acp_next,
+ BOOL* found,
+ LONG* found_offset) {
+ if (!acp_next || !found || !found_offset)
+ return E_INVALIDARG;
+ // We don't support any attributes.
+ // So we always return "not found".
+ *acp_next = 0;
+ *found = FALSE;
+ *found_offset = 0;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetACPFromPoint(TsViewCookie view_cookie,
+ const POINT* point,
+ DWORD flags,
+ LONG* acp) {
+ NOTIMPLEMENTED();
+ if (view_cookie != kViewCookie)
+ return E_INVALIDARG;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TextStore::GetActiveView(TsViewCookie* view_cookie) {
+ if (!view_cookie)
+ return E_INVALIDARG;
+ // We support only one view.
+ *view_cookie = kViewCookie;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetEmbedded(LONG acp_pos,
+ REFGUID service,
+ REFIID iid,
+ IUnknown** unknown) {
+ // We don't support any embedded objects.
+ NOTIMPLEMENTED();
+ if (!unknown)
+ return E_INVALIDARG;
+ *unknown = NULL;
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TextStore::GetEndACP(LONG* acp) {
+ if (!acp)
+ return E_INVALIDARG;
+ if (!HasReadLock())
+ return TS_E_NOLOCK;
+ *acp = static_cast<LONG>(string_buffer_.size());
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetFormattedText(LONG acp_start,
+ LONG acp_end,
+ IDataObject** data_object) {
+ NOTIMPLEMENTED();
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) {
+ if (view_cookie != kViewCookie)
+ return E_INVALIDARG;
+ if (!rect)
+ return E_INVALIDARG;
+
+ // {0, 0, 0, 0} means that the document rect is not currently displayed.
+ SetRect(rect, 0, 0, 0, 0);
+
+ RECT client_rect = {};
+ if (!GetClientRect(window_handle_, &client_rect))
+ return E_FAIL;
+ POINT left_top = {client_rect.left, client_rect.top};
+ POINT right_bottom = {client_rect.right, client_rect.bottom};
+ if (!ClientToScreen(window_handle_, &left_top))
+ return E_FAIL;
+ if (!ClientToScreen(window_handle_, &right_bottom))
+ return E_FAIL;
+
+ rect->left = left_top.x;
+ rect->top = left_top.y;
+ rect->right = right_bottom.x;
+ rect->bottom = right_bottom.y;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetSelection(ULONG selection_index,
+ ULONG selection_buffer_size,
+ TS_SELECTION_ACP* selection_buffer,
+ ULONG* fetched_count) {
+ if (!selection_buffer)
+ return E_INVALIDARG;
+ if (!fetched_count)
+ return E_INVALIDARG;
+ if (!HasReadLock())
+ return TS_E_NOLOCK;
+ *fetched_count = 0;
+ if ((selection_buffer_size > 0) &&
+ ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) {
+ selection_buffer[0].acpStart = selection_start_;
+ selection_buffer[0].acpEnd = selection_end_;
+ selection_buffer[0].style.ase = TS_AE_END;
+ selection_buffer[0].style.fInterimChar = FALSE;
+ *fetched_count = 1;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetStatus(TS_STATUS* status) {
+ if (!status)
+ return E_INVALIDARG;
+
+ status->dwDynamicFlags = 0;
+ // We use transitory contexts and we don't support hidden text.
+ status->dwStaticFlags = TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT;
+
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetText(LONG acp_start,
+ LONG acp_end,
+ wchar_t* text_buffer,
+ ULONG text_buffer_size,
+ ULONG* text_buffer_copied,
+ TS_RUNINFO* run_info_buffer,
+ ULONG run_info_buffer_size,
+ ULONG* run_info_buffer_copied,
+ LONG* next_acp) {
+ if (!text_buffer_copied || !run_info_buffer_copied)
+ return E_INVALIDARG;
+ if (!text_buffer && text_buffer_size != 0)
+ return E_INVALIDARG;
+ if (!run_info_buffer && run_info_buffer_size != 0)
+ return E_INVALIDARG;
+ if (!next_acp)
+ return E_INVALIDARG;
+ if (!HasReadLock())
+ return TF_E_NOLOCK;
+ const LONG string_buffer_size = static_cast<LONG>(string_buffer_.size());
+ if (acp_end == -1)
+ acp_end = string_buffer_size;
+ if (!((0 <= acp_start) &&
+ (acp_start <= acp_end) &&
+ (acp_end <= string_buffer_size))) {
+ return TF_E_INVALIDPOS;
+ }
+ acp_end = std::min(acp_end, acp_start + static_cast<LONG>(text_buffer_size));
+ *text_buffer_copied = acp_end - acp_start;
+
+ const string16& result =
+ string_buffer_.substr(acp_start, *text_buffer_copied);
+ for (size_t i = 0; i < result.size(); ++i)
+ text_buffer[i] = result[i];
+
+ if (run_info_buffer_size) {
+ run_info_buffer[0].uCount = *text_buffer_copied;
+ run_info_buffer[0].type = TS_RT_PLAIN;
+ *run_info_buffer_copied = 1;
+ }
+
+ *next_acp = acp_end;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetTextExt(TsViewCookie view_cookie,
+ LONG acp_start,
+ LONG acp_end,
+ RECT* rect,
+ BOOL* clipped) {
+ if (!rect || !clipped)
+ return E_INVALIDARG;
+ if (view_cookie != kViewCookie)
+ return E_INVALIDARG;
+ if (!HasReadLock())
+ return TS_E_NOLOCK;
+ if (!((static_cast<LONG>(committed_size_) <= acp_start) &&
+ (acp_start <= acp_end) &&
+ (acp_end <= static_cast<LONG>(string_buffer_.size())))) {
+ return TS_E_INVALIDPOS;
+ }
+
+ // According to a behavior of notepad.exe and wordpad.exe, top left corner of
+ // rect indicates a first character's one, and bottom right corner of rect
+ // indicates a last character's one.
+ // We use RECT instead of gfx::Rect since left position may be bigger than
+ // right position when composition has multiple lines.
+ RECT result;
+ RECT tmp_rect;
+ const uint32 start_pos = acp_start - committed_size_;
+ const uint32 end_pos = acp_end - committed_size_;
+
+ if (start_pos == end_pos) {
+ // According to MSDN document, if |acp_start| and |acp_end| are equal it is
+ // OK to just return E_INVALIDARG.
+ // http://msdn.microsoft.com/en-us/library/ms538435
+ // But when using Pinin IME of Windows 8, this method is called with the
+ // equal values of |acp_start| and |acp_end|. So we handle this condition.
+ if (start_pos == 0) {
+ if (delegate_->GetCompositionCharacterBounds(0, &tmp_rect)) {
+ tmp_rect.right = tmp_rect.right;
+ result = tmp_rect;
+ } else if (string_buffer_.size() == committed_size_) {
+ result = delegate_->GetCaretBounds();
+ } else {
+ return TS_E_NOLAYOUT;
+ }
+ } else if (delegate_->GetCompositionCharacterBounds(start_pos - 1,
+ &tmp_rect)) {
+ tmp_rect.left = tmp_rect.right;
+ result = tmp_rect;
+ } else {
+ return TS_E_NOLAYOUT;
+ }
+ } else {
+ if (delegate_->GetCompositionCharacterBounds(start_pos, &tmp_rect)) {
+ result = tmp_rect;
+ if (delegate_->GetCompositionCharacterBounds(end_pos - 1, &tmp_rect)) {
+ result.right = tmp_rect.right;
+ result.bottom = tmp_rect.bottom;
+ } else {
+ // We may not be able to get the last character bounds, so we use the
+ // first character bounds instead of returning TS_E_NOLAYOUT.
+ }
+ } else {
+ // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so
+ // it's better to return previous caret rectangle instead.
+ // TODO(nona, kinaba): Remove this hack.
+ if (start_pos == 0)
+ result = delegate_->GetCaretBounds();
+ else
+ return TS_E_NOLAYOUT;
+ }
+ }
+
+ *rect = result;
+ *clipped = FALSE;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::GetWnd(TsViewCookie view_cookie,
+ HWND* window_handle) {
+ if (!window_handle)
+ return E_INVALIDARG;
+ if (view_cookie != kViewCookie)
+ return E_INVALIDARG;
+ *window_handle = window_handle_;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::InsertEmbedded(DWORD flags,
+ LONG acp_start,
+ LONG acp_end,
+ IDataObject* data_object,
+ TS_TEXTCHANGE* change) {
+ // We don't support any embedded objects.
+ NOTIMPLEMENTED();
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TextStore::InsertEmbeddedAtSelection(DWORD flags,
+ IDataObject* data_object,
+ LONG* acp_start,
+ LONG* acp_end,
+ TS_TEXTCHANGE* change) {
+ // We don't support any embedded objects.
+ NOTIMPLEMENTED();
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP TextStore::InsertTextAtSelection(DWORD flags,
+ const wchar_t* text_buffer,
+ ULONG text_buffer_size,
+ LONG* acp_start,
+ LONG* acp_end,
+ TS_TEXTCHANGE* text_change) {
+ const LONG start_pos = selection_start_;
+ const LONG end_pos = selection_end_;
+ const LONG new_end_pos = start_pos + text_buffer_size;
+
+ if (flags & TS_IAS_QUERYONLY) {
+ if (!HasReadLock())
+ return TS_E_NOLOCK;
+ if (acp_start)
+ *acp_start = start_pos;
+ if (acp_end)
+ *acp_end = end_pos;
+ return S_OK;
+ }
+
+ if (!HasReadWriteLock())
+ return TS_E_NOLOCK;
+ if (!text_buffer)
+ return E_INVALIDARG;
+
+ DCHECK_LE(start_pos, end_pos);
+ string_buffer_ = string_buffer_.substr(0, start_pos) +
+ string16(text_buffer, text_buffer + text_buffer_size) +
+ string_buffer_.substr(end_pos);
+ if (acp_start)
+ *acp_start = start_pos;
+ if (acp_end)
+ *acp_end = new_end_pos;
+ if (text_change) {
+ text_change->acpStart = start_pos;
+ text_change->acpOldEnd = end_pos;
+ text_change->acpNewEnd = new_end_pos;
+ }
+ selection_start_ = start_pos;
+ selection_end_ = new_end_pos;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::QueryInsert(
+ LONG acp_test_start,
+ LONG acp_test_end,
+ ULONG text_size,
+ LONG* acp_result_start,
+ LONG* acp_result_end) {
+ if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end)
+ return E_INVALIDARG;
+ const LONG committed_size = static_cast<LONG>(committed_size_);
+ const LONG buffer_size = static_cast<LONG>(string_buffer_.size());
+ *acp_result_start = std::min(std::max(committed_size, acp_test_start),
+ buffer_size);
+ *acp_result_end = std::min(std::max(committed_size, acp_test_end),
+ buffer_size);
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::QueryInsertEmbedded(const GUID* service,
+ const FORMATETC* format,
+ BOOL* insertable) {
+ if (!format)
+ return E_INVALIDARG;
+ // We don't support any embedded objects.
+ if (insertable)
+ *insertable = FALSE;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::RequestAttrsAtPosition(
+ LONG acp_pos,
+ ULONG attribute_buffer_size,
+ const TS_ATTRID* attribute_buffer,
+ DWORD flags) {
+ // We don't support any document attributes.
+ // This method just returns S_OK, and the subsequently called
+ // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::RequestAttrsTransitioningAtPosition(
+ LONG acp_pos,
+ ULONG attribute_buffer_size,
+ const TS_ATTRID* attribute_buffer,
+ DWORD flags) {
+ // We don't support any document attributes.
+ // This method just returns S_OK, and the subsequently called
+ // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
+ if (!text_store_acp_sink_.get())
+ return E_FAIL;
+ if (!result)
+ return E_INVALIDARG;
+
+ if (current_lock_type_ != 0) {
+ if (lock_flags & TS_LF_SYNC) {
+ // Can't lock synchronously.
+ *result = TS_E_SYNCHRONOUS;
+ return S_OK;
+ }
+ // Queue the lock request.
+ lock_queue_.push_back(lock_flags & TS_LF_READWRITE);
+ *result = TS_S_ASYNC;
+ return S_OK;
+ }
+
+ // Lock
+ current_lock_type_ = (lock_flags & TS_LF_READWRITE);
+
+ edit_flag_ = false;
+ const uint32 last_committed_size = committed_size_;
+
+ // Grant the lock.
+ *result = text_store_acp_sink_->OnLockGranted(current_lock_type_);
+
+ // Unlock
+ current_lock_type_ = 0;
+
+ // Handles the pending lock requests.
+ while (!lock_queue_.empty()) {
+ current_lock_type_ = lock_queue_.front();
+ lock_queue_.pop_front();
+ text_store_acp_sink_->OnLockGranted(current_lock_type_);
+ current_lock_type_ = 0;
+ }
+
+ if (!edit_flag_)
+ return S_OK;
+
+ // If the text store is edited in OnLockGranted(), we may need to call
+ // TextStoreDelegate::ConfirmComposition() or
+ // TextStoreDelegate::SetComposition().
+ const uint32 new_committed_size = committed_size_;
+ const string16& new_committed_string =
+ string_buffer_.substr(last_committed_size,
+ new_committed_size - last_committed_size);
+ const string16& composition_string =
+ string_buffer_.substr(new_committed_size);
+
+ // If there is new committed string, calls
+ // TextStoreDelegate::ConfirmComposition().
+ if ((!new_committed_string.empty()))
+ delegate_->OnTextCommitted(new_committed_string);
+
+ // Calls TextInputClient::SetCompositionText().
+ std::vector<metro_viewer::UnderlineInfo> underlines = underlines_;
+ // Adjusts the offset.
+ for (size_t i = 0; i < underlines_.size(); ++i) {
+ underlines[i].start_offset -= new_committed_size;
+ underlines[i].end_offset -= new_committed_size;
+ }
+ int32 selection_start = 0;
+ int32 selection_end = 0;
+ if (selection_start_ >= new_committed_size)
+ selection_start = selection_start_ - new_committed_size;
+ if (selection_end_ >= new_committed_size)
+ selection_end = selection_end_ - new_committed_size;
+ delegate_->OnCompositionChanged(
+ composition_string, selection_start, selection_end, underlines);
+
+ // If there is no composition string, clear the text store status.
+ // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange().
+ if ((composition_string.empty()) && (new_committed_size != 0)) {
+ string_buffer_.clear();
+ committed_size_ = 0;
+ selection_start_ = 0;
+ selection_end_ = 0;
+ if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
+ text_store_acp_sink_->OnSelectionChange();
+ if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
+ text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
+ if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
+ TS_TEXTCHANGE textChange;
+ textChange.acpStart = 0;
+ textChange.acpOldEnd = new_committed_size;
+ textChange.acpNewEnd = 0;
+ text_store_acp_sink_->OnTextChange(0, &textChange);
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::RequestSupportedAttrs(
+ DWORD /* flags */, // Seems that we should ignore this.
+ ULONG attribute_buffer_size,
+ const TS_ATTRID* attribute_buffer) {
+ if (!attribute_buffer)
+ return E_INVALIDARG;
+ if (!input_scope_)
+ return E_FAIL;
+ // We support only input scope attribute.
+ for (size_t i = 0; i < attribute_buffer_size; ++i) {
+ if (IsEqualGUID(GUID_PROP_INPUTSCOPE, attribute_buffer[i]))
+ return S_OK;
+ }
+ return E_FAIL;
+}
+
+STDMETHODIMP TextStore::RetrieveRequestedAttrs(
+ ULONG attribute_buffer_size,
+ TS_ATTRVAL* attribute_buffer,
+ ULONG* attribute_buffer_copied) {
+ if (!attribute_buffer_copied)
+ return E_INVALIDARG;
+ *attribute_buffer_copied = 0;
+ if (!attribute_buffer)
+ return E_INVALIDARG;
+ if (!input_scope_)
+ return E_UNEXPECTED;
+ // We support only input scope attribute.
+ *attribute_buffer_copied = 0;
+ if (attribute_buffer_size == 0)
+ return S_OK;
+
+ attribute_buffer[0].dwOverlapId = 0;
+ attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE;
+ attribute_buffer[0].varValue.vt = VT_UNKNOWN;
+ attribute_buffer[0].varValue.punkVal = input_scope_.get();
+ attribute_buffer[0].varValue.punkVal->AddRef();
+ *attribute_buffer_copied = 1;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::SetSelection(
+ ULONG selection_buffer_size,
+ const TS_SELECTION_ACP* selection_buffer) {
+ if (!HasReadWriteLock())
+ return TF_E_NOLOCK;
+ if (selection_buffer_size > 0) {
+ const LONG start_pos = selection_buffer[0].acpStart;
+ const LONG end_pos = selection_buffer[0].acpEnd;
+ if (!((static_cast<LONG>(committed_size_) <= start_pos) &&
+ (start_pos <= end_pos) &&
+ (end_pos <= static_cast<LONG>(string_buffer_.size())))) {
+ return TF_E_INVALIDPOS;
+ }
+ selection_start_ = start_pos;
+ selection_end_ = end_pos;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::SetText(DWORD flags,
+ LONG acp_start,
+ LONG acp_end,
+ const wchar_t* text_buffer,
+ ULONG text_buffer_size,
+ TS_TEXTCHANGE* text_change) {
+ if (!HasReadWriteLock())
+ return TS_E_NOLOCK;
+ if (!((static_cast<LONG>(committed_size_) <= acp_start) &&
+ (acp_start <= acp_end) &&
+ (acp_end <= static_cast<LONG>(string_buffer_.size())))) {
+ return TS_E_INVALIDPOS;
+ }
+
+ TS_SELECTION_ACP selection;
+ selection.acpStart = acp_start;
+ selection.acpEnd = acp_end;
+ selection.style.ase = TS_AE_NONE;
+ selection.style.fInterimChar = 0;
+
+ HRESULT ret;
+ ret = SetSelection(1, &selection);
+ if (ret != S_OK)
+ return ret;
+
+ TS_TEXTCHANGE change;
+ ret = InsertTextAtSelection(0, text_buffer, text_buffer_size,
+ &acp_start, &acp_end, &change);
+ if (ret != S_OK)
+ return ret;
+
+ if (text_change)
+ *text_change = change;
+
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::UnadviseSink(IUnknown* unknown) {
+ if (!text_store_acp_sink_.IsSameObject(unknown))
+ return CONNECT_E_NOCONNECTION;
+ text_store_acp_sink_.Release();
+ text_store_acp_sink_mask_ = 0;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::OnStartComposition(
+ ITfCompositionView* composition_view,
+ BOOL* ok) {
+ if (ok)
+ *ok = TRUE;
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::OnUpdateComposition(
+ ITfCompositionView* composition_view,
+ ITfRange* range) {
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::OnEndComposition(
+ ITfCompositionView* composition_view) {
+ return S_OK;
+}
+
+STDMETHODIMP TextStore::OnEndEdit(ITfContext* context,
+ TfEditCookie read_only_edit_cookie,
+ ITfEditRecord* edit_record) {
+ if (!context || !edit_record)
+ return E_INVALIDARG;
+ if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size_,
+ &underlines_)) {
+ return S_OK;
+ }
+ edit_flag_ = true;
+ return S_OK;
+}
+
+bool TextStore::GetDisplayAttribute(TfGuidAtom guid_atom,
+ TF_DISPLAYATTRIBUTE* attribute) {
+ GUID guid;
+ if (FAILED(category_manager_->GetGUID(guid_atom, &guid)))
+ return false;
+
+ base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info;
+ if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo(
+ guid, display_attribute_info.Receive(), NULL))) {
+ return false;
+ }
+ return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute));
+}
+
+bool TextStore::GetCompositionStatus(
+ ITfContext* context,
+ const TfEditCookie read_only_edit_cookie,
+ uint32* committed_size,
+ std::vector<metro_viewer::UnderlineInfo>* undelines) {
+ DCHECK(context);
+ DCHECK(committed_size);
+ DCHECK(undelines);
+ const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE};
+ base::win::ScopedComPtr<ITfReadOnlyProperty> track_property;
+ if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0,
+ track_property.Receive()))) {
+ return false;
+ }
+
+ *committed_size = 0;
+ undelines->clear();
+ base::win::ScopedComPtr<ITfRange> start_to_end_range;
+ base::win::ScopedComPtr<ITfRange> end_range;
+ if (FAILED(context->GetStart(read_only_edit_cookie,
+ start_to_end_range.Receive()))) {
+ return false;
+ }
+ if (FAILED(context->GetEnd(read_only_edit_cookie, end_range.Receive())))
+ return false;
+ if (FAILED(start_to_end_range->ShiftEndToRange(read_only_edit_cookie,
+ end_range, TF_ANCHOR_END))) {
+ return false;
+ }
+
+ base::win::ScopedComPtr<IEnumTfRanges> ranges;
+ if (FAILED(track_property->EnumRanges(read_only_edit_cookie, ranges.Receive(),
+ start_to_end_range))) {
+ return false;
+ }
+
+ while (true) {
+ base::win::ScopedComPtr<ITfRange> range;
+ if (ranges->Next(1, range.Receive(), NULL) != S_OK)
+ break;
+ base::win::ScopedVariant value;
+ base::win::ScopedComPtr<IEnumTfPropertyValue> enum_prop_value;
+ if (FAILED(track_property->GetValue(read_only_edit_cookie, range,
+ value.Receive()))) {
+ return false;
+ }
+ if (FAILED(enum_prop_value.QueryFrom(value.AsInput()->punkVal)))
+ return false;
+
+ TF_PROPERTYVAL property_value;
+ bool is_composition = false;
+ bool has_display_attribute = false;
+ TF_DISPLAYATTRIBUTE display_attribute;
+ while (enum_prop_value->Next(1, &property_value, NULL) == S_OK) {
+ if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) {
+ is_composition = (property_value.varValue.lVal == TRUE);
+ } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) {
+ TfGuidAtom guid_atom =
+ static_cast<TfGuidAtom>(property_value.varValue.lVal);
+ if (GetDisplayAttribute(guid_atom, &display_attribute))
+ has_display_attribute = true;
+ }
+ VariantClear(&property_value.varValue);
+ }
+
+ base::win::ScopedComPtr<ITfRangeACP> range_acp;
+ range_acp.QueryFrom(range);
+ LONG start_pos, length;
+ range_acp->GetExtent(&start_pos, &length);
+ if (!is_composition) {
+ if (*committed_size < static_cast<size_t>(start_pos + length))
+ *committed_size = start_pos + length;
+ } else {
+ metro_viewer::UnderlineInfo underline;
+ underline.start_offset = start_pos;
+ underline.end_offset = start_pos + length;
+ underline.thick = !!display_attribute.fBoldLine;
+ undelines->push_back(underline);
+ }
+ }
+ return true;
+}
+
+bool TextStore::CancelComposition() {
+ // If there is an on-going document lock, we must not edit the text.
+ if (edit_flag_)
+ return false;
+
+ if (string_buffer_.empty())
+ return true;
+
+ // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does
+ // not have a dedicated method to cancel composition. However, CUAS actually
+ // has a protocol conversion from CPS_CANCEL into TSF operations. According
+ // to the observations on Windows 7, TIPs are expected to cancel composition
+ // when an on-going composition text is replaced with an empty string. So
+ // we use the same operation to cancel composition here to minimize the risk
+ // of potential compatibility issues.
+
+ const uint32 previous_buffer_size =
+ static_cast<uint32>(string_buffer_.size());
+ string_buffer_.clear();
+ committed_size_ = 0;
+ selection_start_ = 0;
+ selection_end_ = 0;
+ if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
+ text_store_acp_sink_->OnSelectionChange();
+ if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
+ text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
+ if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
+ TS_TEXTCHANGE textChange = {};
+ textChange.acpStart = 0;
+ textChange.acpOldEnd = previous_buffer_size;
+ textChange.acpNewEnd = 0;
+ text_store_acp_sink_->OnTextChange(0, &textChange);
+ }
+ return true;
+}
+
+bool TextStore::ConfirmComposition() {
+ // If there is an on-going document lock, we must not edit the text.
+ if (edit_flag_)
+ return false;
+
+ if (string_buffer_.empty())
+ return true;
+
+ // See the comment in TextStore::CancelComposition.
+ // This logic is based on the observation about how to emulate
+ // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS.
+
+ const string16& composition_text = string_buffer_.substr(committed_size_);
+ if (!composition_text.empty())
+ delegate_->OnTextCommitted(composition_text);
+
+ const uint32 previous_buffer_size =
+ static_cast<uint32>(string_buffer_.size());
+ string_buffer_.clear();
+ committed_size_ = 0;
+ selection_start_ = 0;
+ selection_end_ = 0;
+ if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
+ text_store_acp_sink_->OnSelectionChange();
+ if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
+ text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
+ if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
+ TS_TEXTCHANGE textChange = {};
+ textChange.acpStart = 0;
+ textChange.acpOldEnd = previous_buffer_size;
+ textChange.acpNewEnd = 0;
+ text_store_acp_sink_->OnTextChange(0, &textChange);
+ }
+ return true;
+}
+
+void TextStore::SendOnLayoutChange() {
+ if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE))
+ text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
+}
+
+bool TextStore::HasReadLock() const {
+ return (current_lock_type_ & TS_LF_READ) == TS_LF_READ;
+}
+
+bool TextStore::HasReadWriteLock() const {
+ return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE;
+}
+
+} // namespace metro_driver
diff --git a/chromium/win8/metro_driver/ime/text_store.h b/chromium/win8/metro_driver/ime/text_store.h
new file mode 100644
index 00000000000..8ed4d612708
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/text_store.h
@@ -0,0 +1,315 @@
+// Copyright 2013 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_METRO_DRIVER_IME_TEXT_STORE_H_
+#define WIN8_METRO_DRIVER_IME_TEXT_STORE_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <initguid.h>
+#include <inputscope.h>
+#include <msctf.h>
+
+#include <deque>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/win/scoped_comptr.h"
+#include "ui/metro_viewer/ime_types.h"
+
+namespace metro_driver {
+
+class TextStoreDelegate;
+
+// TextStore is used to interact with the input method via TSF manager.
+// TextStore have a string buffer which is manipulated by TSF manager through
+// ITextStoreACP interface methods such as SetText().
+// When the input method updates the composition, TextStore calls
+// TextInputClient::SetCompositionText(). And when the input method finishes the
+// composition, TextStore calls TextInputClient::InsertText() and clears the
+// buffer.
+//
+// How TextStore works:
+// - The user enters "a".
+// - The input method set composition as "a".
+// - TSF manager calls TextStore::RequestLock().
+// - TextStore callbacks ITextStoreACPSink::OnLockGranted().
+// - In OnLockGranted(), TSF manager calls
+// - TextStore::OnStartComposition()
+// - TextStore::SetText()
+// The string buffer is set as "a".
+// - TextStore::OnUpdateComposition()
+// - TextStore::OnEndEdit()
+// TextStore can get the composition information such as underlines.
+// - TextStore calls TextInputClient::SetCompositionText().
+// "a" is shown with an underline as composition string.
+// - The user enters <space>.
+// - The input method set composition as "A".
+// - TSF manager calls TextStore::RequestLock().
+// - TextStore callbacks ITextStoreACPSink::OnLockGranted().
+// - In OnLockGranted(), TSF manager calls
+// - TextStore::SetText()
+// The string buffer is set as "A".
+// - TextStore::OnUpdateComposition()
+// - TextStore::OnEndEdit()
+// - TextStore calls TextInputClient::SetCompositionText().
+// "A" is shown with an underline as composition string.
+// - The user enters <enter>.
+// - The input method commits "A".
+// - TSF manager calls TextStore::RequestLock().
+// - TextStore callbacks ITextStoreACPSink::OnLockGranted().
+// - In OnLockGranted(), TSF manager calls
+// - TextStore::OnEndComposition()
+// - TextStore::OnEndEdit()
+// TextStore knows "A" is committed.
+// - TextStore calls TextInputClient::InsertText().
+// "A" is shown as committed string.
+// - TextStore clears the string buffer.
+// - TextStore calls OnSelectionChange(), OnLayoutChange() and
+// OnTextChange() of ITextStoreACPSink to let TSF manager know that the
+// string buffer has been changed.
+//
+// About the locking scheme:
+// When TSF manager manipulates the string buffer it calls RequestLock() to get
+// the lock of the document. If TextStore can grant the lock request, it
+// callbacks ITextStoreACPSink::OnLockGranted().
+// RequestLock() is called from only one thread, but called recursively in
+// OnLockGranted() or OnSelectionChange() or OnLayoutChange() or OnTextChange().
+// If the document is locked and the lock request is asynchronous, TextStore
+// queues the request. The queued requests will be handled after the current
+// lock is removed.
+// More information about document locks can be found here:
+// http://msdn.microsoft.com/en-us/library/ms538064
+//
+// More information about TSF can be found here:
+// http://msdn.microsoft.com/en-us/library/ms629032
+class ATL_NO_VTABLE TextStore
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public ITextStoreACP,
+ public ITfContextOwnerCompositionSink,
+ public ITfTextEditSink {
+ public:
+ virtual ~TextStore();
+
+ BEGIN_COM_MAP(TextStore)
+ COM_INTERFACE_ENTRY(ITextStoreACP)
+ COM_INTERFACE_ENTRY(ITfContextOwnerCompositionSink)
+ COM_INTERFACE_ENTRY(ITfTextEditSink)
+ END_COM_MAP()
+
+ // ITextStoreACP:
+ STDMETHOD(AdviseSink)(REFIID iid, IUnknown* unknown, DWORD mask) OVERRIDE;
+ STDMETHOD(FindNextAttrTransition)(LONG acp_start,
+ LONG acp_halt,
+ ULONG num_filter_attributes,
+ const TS_ATTRID* filter_attributes,
+ DWORD flags,
+ LONG* acp_next,
+ BOOL* found,
+ LONG* found_offset) OVERRIDE;
+ STDMETHOD(GetACPFromPoint)(TsViewCookie view_cookie,
+ const POINT* point,
+ DWORD flags,
+ LONG* acp) OVERRIDE;
+ STDMETHOD(GetActiveView)(TsViewCookie* view_cookie) OVERRIDE;
+ STDMETHOD(GetEmbedded)(LONG acp_pos,
+ REFGUID service,
+ REFIID iid,
+ IUnknown** unknown) OVERRIDE;
+ STDMETHOD(GetEndACP)(LONG* acp) OVERRIDE;
+ STDMETHOD(GetFormattedText)(LONG acp_start,
+ LONG acp_end,
+ IDataObject** data_object) OVERRIDE;
+ STDMETHOD(GetScreenExt)(TsViewCookie view_cookie, RECT* rect) OVERRIDE;
+ STDMETHOD(GetSelection)(ULONG selection_index,
+ ULONG selection_buffer_size,
+ TS_SELECTION_ACP* selection_buffer,
+ ULONG* fetched_count) OVERRIDE;
+ STDMETHOD(GetStatus)(TS_STATUS* pdcs) OVERRIDE;
+ STDMETHOD(GetText)(LONG acp_start,
+ LONG acp_end,
+ wchar_t* text_buffer,
+ ULONG text_buffer_size,
+ ULONG* text_buffer_copied,
+ TS_RUNINFO* run_info_buffer,
+ ULONG run_info_buffer_size,
+ ULONG* run_info_buffer_copied,
+ LONG* next_acp) OVERRIDE;
+ STDMETHOD(GetTextExt)(TsViewCookie view_cookie,
+ LONG acp_start,
+ LONG acp_end,
+ RECT* rect,
+ BOOL* clipped) OVERRIDE;
+ STDMETHOD(GetWnd)(TsViewCookie view_cookie, HWND* window_handle) OVERRIDE;
+ STDMETHOD(InsertEmbedded)(DWORD flags,
+ LONG acp_start,
+ LONG acp_end,
+ IDataObject* data_object,
+ TS_TEXTCHANGE* change) OVERRIDE;
+ STDMETHOD(InsertEmbeddedAtSelection)(DWORD flags,
+ IDataObject* data_object,
+ LONG* acp_start,
+ LONG* acp_end,
+ TS_TEXTCHANGE* change) OVERRIDE;
+ STDMETHOD(InsertTextAtSelection)(DWORD flags,
+ const wchar_t* text_buffer,
+ ULONG text_buffer_size,
+ LONG* acp_start,
+ LONG* acp_end,
+ TS_TEXTCHANGE* text_change) OVERRIDE;
+ STDMETHOD(QueryInsert)(LONG acp_test_start,
+ LONG acp_test_end,
+ ULONG text_size,
+ LONG* acp_result_start,
+ LONG* acp_result_end) OVERRIDE;
+ STDMETHOD(QueryInsertEmbedded)(const GUID* service,
+ const FORMATETC* format,
+ BOOL* insertable) OVERRIDE;
+ STDMETHOD(RequestAttrsAtPosition)(LONG acp_pos,
+ ULONG attribute_buffer_size,
+ const TS_ATTRID* attribute_buffer,
+ DWORD flags) OVERRIDE;
+ STDMETHOD(RequestAttrsTransitioningAtPosition)(
+ LONG acp_pos,
+ ULONG attribute_buffer_size,
+ const TS_ATTRID* attribute_buffer,
+ DWORD flags) OVERRIDE;
+ STDMETHOD(RequestLock)(DWORD lock_flags, HRESULT* result) OVERRIDE;
+ STDMETHOD(RequestSupportedAttrs)(DWORD flags,
+ ULONG attribute_buffer_size,
+ const TS_ATTRID* attribute_buffer) OVERRIDE;
+ STDMETHOD(RetrieveRequestedAttrs)(ULONG attribute_buffer_size,
+ TS_ATTRVAL* attribute_buffer,
+ ULONG* attribute_buffer_copied) OVERRIDE;
+ STDMETHOD(SetSelection)(ULONG selection_buffer_size,
+ const TS_SELECTION_ACP* selection_buffer) OVERRIDE;
+ STDMETHOD(SetText)(DWORD flags,
+ LONG acp_start,
+ LONG acp_end,
+ const wchar_t* text_buffer,
+ ULONG text_buffer_size,
+ TS_TEXTCHANGE* text_change) OVERRIDE;
+ STDMETHOD(UnadviseSink)(IUnknown* unknown) OVERRIDE;
+
+ // ITfContextOwnerCompositionSink:
+ STDMETHOD(OnStartComposition)(ITfCompositionView* composition_view,
+ BOOL* ok) OVERRIDE;
+ STDMETHOD(OnUpdateComposition)(ITfCompositionView* composition_view,
+ ITfRange* range) OVERRIDE;
+ STDMETHOD(OnEndComposition)(ITfCompositionView* composition_view) OVERRIDE;
+
+ // ITfTextEditSink:
+ STDMETHOD(OnEndEdit)(ITfContext* context, TfEditCookie read_only_edit_cookie,
+ ITfEditRecord* edit_record) OVERRIDE;
+
+ // Cancels the ongoing composition if exists.
+ bool CancelComposition();
+
+ // Confirms the ongoing composition if exists.
+ bool ConfirmComposition();
+
+ // Sends OnLayoutChange() via |text_store_acp_sink_|.
+ void SendOnLayoutChange();
+
+ // Creates an instance of TextStore. Returns NULL if fails.
+ static scoped_refptr<TextStore> Create(
+ HWND window_handle,
+ const std::vector<InputScope>& input_scopes,
+ TextStoreDelegate* delegate);
+
+ private:
+ friend CComObject<TextStore>;
+ TextStore();
+
+ void Initialize(HWND window_handle,
+ ITfCategoryMgr* category_manager,
+ ITfDisplayAttributeMgr* display_attribute_manager,
+ ITfInputScope* input_scope,
+ TextStoreDelegate* delegate);
+
+ // Checks if the document has a read-only lock.
+ bool HasReadLock() const;
+
+ // Checks if the document has a read and write lock.
+ bool HasReadWriteLock() const;
+
+ // Gets the display attribute structure.
+ bool GetDisplayAttribute(TfGuidAtom guid_atom,
+ TF_DISPLAYATTRIBUTE* attribute);
+
+ // Gets the committed string size and underline information of the context.
+ bool GetCompositionStatus(
+ ITfContext* context,
+ const TfEditCookie read_only_edit_cookie,
+ uint32* committed_size,
+ std::vector<metro_viewer::UnderlineInfo>* undelines);
+
+ // A pointer of ITextStoreACPSink, this instance is given in AdviseSink.
+ base::win::ScopedComPtr<ITextStoreACPSink> text_store_acp_sink_;
+
+ // The current mask of |text_store_acp_sink_|.
+ DWORD text_store_acp_sink_mask_;
+
+ // HWND of the attached window.
+ HWND window_handle_;
+
+ // |string_buffer_| contains committed string and composition string.
+ // Example: "aoi" is committed, and "umi" is under composition.
+ // |string_buffer_|: "aoiumi"
+ // |committed_size_|: 3
+ string16 string_buffer_;
+ uint32 committed_size_;
+
+ // |selection_start_| and |selection_end_| indicates the selection range.
+ // Example: "iue" is selected
+ // |string_buffer_|: "aiueo"
+ // |selection_start_|: 1
+ // |selection_end_|: 4
+ uint32 selection_start_;
+ uint32 selection_end_;
+
+ // |start_offset| and |end_offset| of |composition_undelines_| indicates
+ // the offsets in |string_buffer_|.
+ // Example: "aoi" is committed. There are two underlines in "umi" and "no".
+ // |string_buffer_|: "aoiumino"
+ // |committed_size_|: 3
+ // underlines_[0].start_offset: 3
+ // underlines_[0].end_offset: 6
+ // underlines_[1].start_offset: 6
+ // underlines_[1].end_offset: 8
+ std::vector<metro_viewer::UnderlineInfo> underlines_;
+
+ // |edit_flag_| indicates that the status is edited during
+ // ITextStoreACPSink::OnLockGranted().
+ bool edit_flag_;
+
+ // The type of current lock.
+ // 0: No lock.
+ // TS_LF_READ: read-only lock.
+ // TS_LF_READWRITE: read/write lock.
+ DWORD current_lock_type_;
+
+ // Queue of the lock request used in RequestLock().
+ std::deque<DWORD> lock_queue_;
+
+ // Category manager and Display attribute manager are used to obtain the
+ // attributes of the composition string.
+ base::win::ScopedComPtr<ITfCategoryMgr> category_manager_;
+ base::win::ScopedComPtr<ITfDisplayAttributeMgr> display_attribute_manager_;
+
+ // Represents the context information of this text.
+ base::win::ScopedComPtr<ITfInputScope> input_scope_;
+
+ // The delegate attached to this text store.
+ TextStoreDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextStore);
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_TEXT_STORE_H_
diff --git a/chromium/win8/metro_driver/ime/text_store_delegate.h b/chromium/win8/metro_driver/ime/text_store_delegate.h
new file mode 100644
index 00000000000..af065164714
--- /dev/null
+++ b/chromium/win8/metro_driver/ime/text_store_delegate.h
@@ -0,0 +1,55 @@
+// Copyright 2013 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_METRO_DRIVER_IME_TEXT_STORE_DELEGATE_H_
+#define WIN8_METRO_DRIVER_IME_TEXT_STORE_DELEGATE_H_
+
+#include <vector>
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+
+namespace metro_viewer {
+struct UnderlineInfo;
+}
+
+namespace metro_driver {
+
+// A delegate which works together with virtual text stores.
+// Objects that implement this delegate will receive notifications from a
+// virtual text store whenever an IME updates the composition or commits text.
+// Objects that implement this delegate are also responsible for calculating
+// the character position of composition and caret position upon request.
+class TextStoreDelegate {
+ public:
+ virtual ~TextStoreDelegate() {}
+
+ // Called when on-going composition is updated. An empty |text| represents
+ // that the composition is canceled.
+ virtual void OnCompositionChanged(
+ const string16& text,
+ int32 selection_start,
+ int32 selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) = 0;
+
+ // Called when |text| is committed.
+ virtual void OnTextCommitted(const string16& text) = 0;
+
+ // Called when an IME requests the caret position. Objects that implement
+ // this method must return the caret position in screen coordinates.
+ virtual RECT GetCaretBounds() = 0;
+
+ // Called when an IME requests the bounding box of an character whose
+ // index is |index| in the on-going composition. position. Objects that
+ // implement this method must return true and fill the character bounds into
+ // |rect| in screen coordinates.
+ // Should return false if |index| is invalid.
+ virtual bool GetCompositionCharacterBounds(uint32 index, RECT* rect) = 0;
+};
+
+} // namespace metro_driver
+
+#endif // WIN8_METRO_DRIVER_IME_TEXT_STORE_DELEGATE_H_
diff --git a/chromium/win8/metro_driver/metro_dialog_box.cc b/chromium/win8/metro_driver/metro_dialog_box.cc
new file mode 100644
index 00000000000..25e70827710
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/metro_dialog_box.h b/chromium/win8/metro_driver/metro_dialog_box.h
new file mode 100644
index 00000000000..5a6a94dbc63
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/metro_driver.cc b/chromium/win8/metro_driver/metro_driver.cc
new file mode 100644
index 00000000000..940b5b141d1
--- /dev/null
+++ b/chromium/win8/metro_driver/metro_driver.cc
@@ -0,0 +1,124 @@
+// 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/metro_driver.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/winrt_utils.h"
+
+#if !defined(USE_AURA)
+#include "win8/metro_driver/chrome_app_view.h"
+#endif
+
+// 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 } };
+
+}
+
+#if !defined(COMPONENT_BUILD)
+// 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;
+#endif
+
+extern "C" __declspec(dllexport)
+int InitMetro(LPTHREAD_START_ROUTINE thread_proc, void* context) {
+ // Initialize the command line.
+ CommandLine::Init(0, NULL);
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(settings);
+
+#if defined(NDEBUG)
+ logging::SetMinLogLevel(logging::LOG_ERROR);
+#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;
+
+ 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/chromium/win8/metro_driver/metro_driver.gyp b/chromium/win8/metro_driver/metro_driver.gyp
index c4797fb5822..cc572d84ebb 100644
--- a/chromium/win8/metro_driver/metro_driver.gyp
+++ b/chromium/win8/metro_driver/metro_driver.gyp
@@ -64,6 +64,7 @@
'metro_driver_version_resources',
],
'sources': [
+ 'display_properties.cc',
'metro_driver.cc',
'metro_driver.h',
'stdafx.h',
@@ -74,7 +75,7 @@
'conditions': [
['use_aura==1', {
'dependencies': [
- '../win8.gyp:metro_viewer',
+ '../win8.gyp:metro_viewer_constants',
],
'sources': [
'chrome_app_view_ash.cc',
@@ -84,6 +85,9 @@
'file_picker_ash.cc',
'file_picker_ash.h',
],
+ 'includes': [
+ 'ime/ime.gypi',
+ ],
}, { # use_aura!=1
'sources': [
'chrome_app_view.cc',
diff --git a/chromium/win8/metro_driver/metro_driver.h b/chromium/win8/metro_driver/metro_driver.h
new file mode 100644
index 00000000000..c4ea2abd636
--- /dev/null
+++ b/chromium/win8/metro_driver/metro_driver.h
@@ -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.
+
+#ifndef WIN8_METRO_DRIVER_METRO_DRIVER_H_
+#define WIN8_METRO_DRIVER_METRO_DRIVER_H_
+
+#include "stdafx.h"
+
+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);
+};
+
+#endif // WIN8_METRO_DRIVER_METRO_DRIVER_H_ \ No newline at end of file
diff --git a/chromium/win8/metro_driver/metro_driver_dll.ver b/chromium/win8/metro_driver/metro_driver_dll.ver
new file mode 100644
index 00000000000..72d1cc8d82e
--- /dev/null
+++ b/chromium/win8/metro_driver/metro_driver_dll.ver
@@ -0,0 +1,2 @@
+INTERNAL_NAME=metro_driver_dll
+ORIGINAL_FILENAME=metro_driver.dll
diff --git a/chromium/win8/metro_driver/metro_driver_win7.cc b/chromium/win8/metro_driver/metro_driver_win7.cc
new file mode 100644
index 00000000000..253e527b8c2
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/print_document_source.cc b/chromium/win8/metro_driver/print_document_source.cc
new file mode 100644
index 00000000000..91842b78c68
--- /dev/null
+++ b/chromium/win8/metro_driver/print_document_source.cc
@@ -0,0 +1,527 @@
+// 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"
+#include "base/safe_numerics.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,
+ base::checked_numeric_cast<UINT32>(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/chromium/win8/metro_driver/print_document_source.h b/chromium/win8/metro_driver/print_document_source.h
new file mode 100644
index 00000000000..fed333e3706
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/print_handler.cc b/chromium/win8/metro_driver/print_handler.cc
new file mode 100644
index 00000000000..3539c9ed2f8
--- /dev/null
+++ b/chromium/win8/metro_driver/print_handler.cc
@@ -0,0 +1,490 @@
+// 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 "base/safe_numerics.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(base::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(base::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(base::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(base::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(base::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(base::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,
+ base::checked_numeric_cast<ULONG>(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/chromium/win8/metro_driver/print_handler.h b/chromium/win8/metro_driver/print_handler.h
new file mode 100644
index 00000000000..f0779cf2ad8
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/resources/Logo.png b/chromium/win8/metro_driver/resources/Logo.png
new file mode 100644
index 00000000000..49d67e4fde6
--- /dev/null
+++ b/chromium/win8/metro_driver/resources/Logo.png
Binary files differ
diff --git a/chromium/win8/metro_driver/resources/SecondaryTile.png b/chromium/win8/metro_driver/resources/SecondaryTile.png
new file mode 100644
index 00000000000..9c9e4532a41
--- /dev/null
+++ b/chromium/win8/metro_driver/resources/SecondaryTile.png
Binary files differ
diff --git a/chromium/win8/metro_driver/resources/SmallLogo.png b/chromium/win8/metro_driver/resources/SmallLogo.png
new file mode 100644
index 00000000000..c25cfb4002e
--- /dev/null
+++ b/chromium/win8/metro_driver/resources/SmallLogo.png
Binary files differ
diff --git a/chromium/win8/metro_driver/resources/VisualElementsManifest.xml b/chromium/win8/metro_driver/resources/VisualElementsManifest.xml
new file mode 100644
index 00000000000..230e787c591
--- /dev/null
+++ b/chromium/win8/metro_driver/resources/VisualElementsManifest.xml
@@ -0,0 +1,17 @@
+<!-- This is only meant to be copied by chrome.exe in developer builds. -->
+<Application
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <VisualElements
+ DisplayName="Developer Chrome"
+ Logo="Logo.png"
+ SmallLogo="SmallLogo.png"
+ ForegroundText="light"
+ BackgroundColor="white">
+ <DefaultTile
+ ShortName="DevChrome"
+ ShowName="allLogos"
+ />
+ <SplashScreen
+ Image="splash-620x300.png"/>
+ </VisualElements>
+</Application>
diff --git a/chromium/win8/metro_driver/resources/splash-620x300.png b/chromium/win8/metro_driver/resources/splash-620x300.png
new file mode 100644
index 00000000000..41a3e02fa91
--- /dev/null
+++ b/chromium/win8/metro_driver/resources/splash-620x300.png
Binary files differ
diff --git a/chromium/win8/metro_driver/run_all_unittests.cc b/chromium/win8/metro_driver/run_all_unittests.cc
new file mode 100644
index 00000000000..4c6a548e2f4
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/secondary_tile.cc b/chromium/win8/metro_driver/secondary_tile.cc
new file mode 100644
index 00000000000..97ff63d1f7f
--- /dev/null
+++ b/chromium/win8/metro_driver/secondary_tile.cc
@@ -0,0 +1,228 @@
+// 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/bind.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "url/gurl.h"
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+using base::win::MetroPinUmaResultCallback;
+
+// Callback for asynchronous pin requests.
+class TileRequestCompleter {
+ public:
+ enum PinType {
+ PIN,
+ UNPIN
+ };
+ TileRequestCompleter(PinType type, const MetroPinUmaResultCallback& callback)
+ : type_(type), callback_(callback) {}
+
+ void Complete(mswr::ComPtr<winfoundtn::IAsyncOperation<bool>>& completion);
+
+ private:
+ // Callback that responds to user input on the pin request pop-up. This will
+ // run |callback_|, then delete |this| before returning.
+ HRESULT Respond(winfoundtn::IAsyncOperation<bool>* async,
+ AsyncStatus status);
+
+ PinType type_;
+ MetroPinUmaResultCallback callback_;
+};
+
+void TileRequestCompleter::Complete(
+ mswr::ComPtr<winfoundtn::IAsyncOperation<bool>>& completion) {
+ typedef winfoundtn::IAsyncOperationCompletedHandler<bool> RequestDoneType;
+ mswr::ComPtr<RequestDoneType> handler(mswr::Callback<RequestDoneType>(
+ this, &TileRequestCompleter::Respond));
+ DCHECK(handler.Get() != NULL);
+ HRESULT hr = completion->put_Completed(handler.Get());
+ CheckHR(hr, "Failed to put_Completed");
+}
+
+HRESULT TileRequestCompleter::Respond(winfoundtn::IAsyncOperation<bool>* async,
+ AsyncStatus status) {
+ base::win::MetroSecondaryTilePinUmaResult pin_state =
+ base::win::METRO_PIN_STATE_NONE;
+
+ if (status == Completed) {
+ unsigned char result;
+ CheckHR(async->GetResults(&result));
+ LOG(INFO) << __FUNCTION__ << " result " << static_cast<int>(result);
+ switch (result) {
+ case 0:
+ pin_state = type_ == PIN ?
+ base::win::METRO_PIN_RESULT_CANCEL :
+ base::win::METRO_UNPIN_RESULT_CANCEL;
+ break;
+ case 1:
+ pin_state = type_ == PIN ?
+ base::win::METRO_PIN_RESULT_OK :
+ base::win::METRO_UNPIN_RESULT_OK;
+ break;
+ default:
+ pin_state = type_ == PIN ?
+ base::win::METRO_PIN_RESULT_OTHER :
+ base::win::METRO_UNPIN_RESULT_OTHER;
+ break;
+ }
+ } else {
+ LOG(ERROR) << __FUNCTION__ << " Unexpected async status "
+ << static_cast<int>(status);
+ pin_state = type_ == PIN ?
+ base::win::METRO_PIN_RESULT_ERROR :
+ base::win::METRO_UNPIN_RESULT_ERROR;
+ }
+ callback_.Run(pin_state);
+
+ delete this;
+ return S_OK;
+}
+
+void DeleteTileFromStartScreen(const string16& tile_id,
+ const MetroPinUmaResultCallback& callback) {
+ 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(tile_id));
+
+ 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");
+
+ if (FAILED(hr)) {
+ callback.Run(base::win::METRO_UNPIN_REQUEST_SHOW_ERROR);
+ return;
+ }
+
+ // Deleted in TileRequestCompleter::Respond when the async operation
+ // completes.
+ TileRequestCompleter* completer =
+ new TileRequestCompleter(TileRequestCompleter::UNPIN, callback);
+ completer->Complete(completion);
+}
+
+void CreateTileOnStartScreen(const string16& tile_id,
+ const string16& title_str,
+ const string16& url_str,
+ const base::FilePath& logo_path,
+ const MetroPinUmaResultCallback& callback) {
+ 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(tile_id));
+
+ mswrw::HString args;
+ // The url is just passed into the tile agruments as is. Metro and desktop
+ // chrome will see the arguments as command line parameters.
+ // A GURL is used to ensure any spaces are properly escaped.
+ GURL url(url_str);
+ args.Attach(MakeHString(UTF8ToUTF16(url.spec())));
+
+ 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(string16(L"file:///").append(logo_path.value())));
+ 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");
+
+ if (FAILED(hr)) {
+ callback.Run(base::win::METRO_PIN_REQUEST_SHOW_ERROR);
+ return;
+ }
+
+ // Deleted in TileRequestCompleter::Respond when the async operation
+ // completes.
+ TileRequestCompleter* completer =
+ new TileRequestCompleter(TileRequestCompleter::PIN, callback);
+ completer->Complete(completion);
+}
+
+} // namespace
+
+BOOL MetroIsPinnedToStartScreen(const string16& tile_id) {
+ 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(tile_id), &exists);
+ CheckHR(hr, "ISecondaryTileStatics.Exists failed");
+ return exists;
+}
+
+void MetroUnPinFromStartScreen(const string16& tile_id,
+ const MetroPinUmaResultCallback& callback) {
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&DeleteTileFromStartScreen,
+ tile_id,
+ callback));
+}
+
+void MetroPinToStartScreen(const string16& tile_id,
+ const string16& title,
+ const string16& url,
+ const base::FilePath& logo_path,
+ const MetroPinUmaResultCallback& callback) {
+ globals.appview_msg_loop->PostTask(
+ FROM_HERE, base::Bind(&CreateTileOnStartScreen,
+ tile_id,
+ title,
+ url,
+ logo_path,
+ callback));
+}
diff --git a/chromium/win8/metro_driver/secondary_tile.h b/chromium/win8/metro_driver/secondary_tile.h
new file mode 100644
index 00000000000..9740864bbdf
--- /dev/null
+++ b/chromium/win8/metro_driver/secondary_tile.h
@@ -0,0 +1,27 @@
+// 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/files/file_path.h"
+#include "base/strings/string16.h"
+#include "base/win/metro.h"
+
+extern "C" __declspec(dllexport)
+BOOL MetroIsPinnedToStartScreen(const string16& tile_id);
+
+extern "C" __declspec(dllexport)
+void MetroUnPinFromStartScreen(
+ const string16& tile_id,
+ const base::win::MetroPinUmaResultCallback& callback);
+
+extern "C" __declspec(dllexport)
+void MetroPinToStartScreen(
+ const string16& tile_id,
+ const string16& title,
+ const string16& url,
+ const base::FilePath& logo_path,
+ const base::win::MetroPinUmaResultCallback& callback);
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_SECONDARY_TILE_H_
diff --git a/chromium/win8/metro_driver/settings_handler.cc b/chromium/win8/metro_driver/settings_handler.cc
new file mode 100644
index 00000000000..6feae24320f
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/settings_handler.h b/chromium/win8/metro_driver/settings_handler.h
new file mode 100644
index 00000000000..5b234ad12e1
--- /dev/null
+++ b/chromium/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/chromium/win8/metro_driver/stdafx.h b/chromium/win8/metro_driver/stdafx.h
new file mode 100644
index 00000000000..1ce382c77b6
--- /dev/null
+++ b/chromium/win8/metro_driver/stdafx.h
@@ -0,0 +1,40 @@
+// 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_METRO_DRIVER_STDAFX_H_
+#define WIN8_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.storage.pickers.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 winxml = ABI::Windows::Data::Xml;
+namespace windevs = ABI::Windows::Devices;
+namespace winfoundtn = ABI::Windows::Foundation;
+namespace wingfx = ABI::Windows::Graphics;
+namespace winui = ABI::Windows::UI;
+namespace winsys = ABI::Windows::System;
+namespace winstorage = ABI::Windows::Storage;
+
+#endif // WIN8_METRO_DRIVER_STDAFX_H_
diff --git a/chromium/win8/metro_driver/toast_notification_handler.cc b/chromium/win8/metro_driver/toast_notification_handler.cc
new file mode 100644
index 00000000000..ab014d4ff06
--- /dev/null
+++ b/chromium/win8/metro_driver/toast_notification_handler.cc
@@ -0,0 +1,252 @@
+// 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/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/shell_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,
+ base::win::MetroNotificationClickedHandler handler,
+ const wchar_t* handler_context)
+ : origin_url(notification_origin),
+ icon_url(notification_icon),
+ title(notification_title),
+ body(notification_body),
+ display_source(notification_display_source),
+ id(notification_id),
+ notification_handler(handler) {
+ if (handler_context)
+ notification_context = handler_context;
+}
+
+ToastNotificationHandler::DesktopNotification::DesktopNotification()
+ : notification_handler(NULL) {
+}
+
+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);
+
+ notification_info_ = notification;
+
+ 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);
+
+ base::FilePath chrome_path;
+ if (!PathService::Get(base::FILE_EXE, &chrome_path)) {
+ NOTREACHED() << "Failed to get chrome exe path";
+ return;
+ }
+
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ bool is_per_user_install = InstallUtil::IsPerUserInstall(
+ chrome_path.value().c_str());
+ string16 appid = ShellUtil::GetBrowserModelId(dist, is_per_user_install);
+ DVLOG(1) << "Chrome Appid is " << appid.c_str();
+
+ 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__;
+
+ if (notification_info_.notification_handler) {
+ notification_info_.notification_handler(
+ notification_info_.notification_context.c_str());
+ }
+ return S_OK;
+}
diff --git a/chromium/win8/metro_driver/toast_notification_handler.h b/chromium/win8/metro_driver/toast_notification_handler.h
new file mode 100644
index 00000000000..8f3587dc75f
--- /dev/null
+++ b/chromium/win8/metro_driver/toast_notification_handler.h
@@ -0,0 +1,55 @@
+// 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/strings/string16.h"
+#include "base/win/metro.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;
+ base::win::MetroNotificationClickedHandler notification_handler;
+ string16 notification_context;
+
+ 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,
+ base::win::MetroNotificationClickedHandler handler,
+ const wchar_t* handler_context);
+
+ DesktopNotification();
+ };
+
+ 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_;
+ DesktopNotification notification_info_;
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_TOAST_NOTIFICATION_HANDLER_H_
diff --git a/chromium/win8/metro_driver/winrt_utils.cc b/chromium/win8/metro_driver/winrt_utils.cc
new file mode 100644
index 00000000000..6d66cec2328
--- /dev/null
+++ b/chromium/win8/metro_driver/winrt_utils.cc
@@ -0,0 +1,226 @@
+// 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/files/file_path.h"
+#include "base/logging.h"
+#include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_comptr.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/install_util.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(), static_cast<UINT32>(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 base::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))) {
+ base::FilePath shortcut(path_buffer);
+ shortcut = shortcut.Append(
+ L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar");
+
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ base::string16 link_name = dist->GetShortcutName(
+ BrowserDistribution::SHORTCUT_CHROME) + installer::kLnkExt;
+ shortcut = shortcut.Append(link_name);
+
+ string16 arguments;
+ if (GetArgumentsFromShortcut(shortcut, &arguments)) {
+ return arguments;
+ }
+ }
+
+ return L"";
+}
+
+} // namespace winrt_utils
diff --git a/chromium/win8/metro_driver/winrt_utils.h b/chromium/win8/metro_driver/winrt_utils.h
new file mode 100644
index 00000000000..3016c852e88
--- /dev/null
+++ b/chromium/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 WIN8_METRO_DRIVER_WINRT_UTILS_H_
+#define WIN8_METRO_DRIVER_WINRT_UTILS_H_
+
+#include <string>
+
+#include <roapi.h>
+#include <windows.applicationmodel.core.h>
+
+#include "base/strings/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 // WIN8_METRO_DRIVER_WINRT_UTILS_H_
diff --git a/chromium/win8/metro_driver/winrt_utils_unittest.cc b/chromium/win8/metro_driver/winrt_utils_unittest.cc
new file mode 100644
index 00000000000..9ae869b2520
--- /dev/null
+++ b/chromium/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/chromium/win8/util/check_sdk_patch.py b/chromium/win8/util/check_sdk_patch.py
new file mode 100755
index 00000000000..0a8cb9ef355
--- /dev/null
+++ b/chromium/win8/util/check_sdk_patch.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# 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.
+
+"""Script to check that the Windows 8 SDK has been appropriately patched so that
+ it can be used with VS 2010.
+
+ In practice, this checks for the presence of 'enum class' in asyncinfo.h.
+ Changing that to 'enum' is the only thing needed to build with the WinRT
+ headers in VS 2010.
+"""
+
+import os
+import sys
+
+
+def main(argv):
+ if len(argv) < 2:
+ print "Usage: check_sdk_patch.py path_to_windows_8_sdk [dummy_output_file]"
+ return 1
+
+ # Look for asyncinfo.h
+ async_info_path = os.path.join(argv[1], 'Include/winrt/asyncinfo.h')
+ if not os.path.exists(async_info_path):
+ print ("Could not find %s in provided SDK path. Please check input." %
+ async_info_path)
+ print "CWD: %s" % os.getcwd()
+ return 2
+ else:
+ if 'enum class' in open(async_info_path).read():
+ print ("\nERROR: You are using an unpatched Windows 8 SDK located at %s."
+ "\nPlease see instructions at"
+ "\nhttp://www.chromium.org/developers/how-tos/"
+ "build-instructions-windows\nfor how to apply the patch to build "
+ "with VS2010.\n" % argv[1])
+ return 3
+ else:
+ if len(argv) > 2:
+ with open(argv[2], 'w') as dummy_file:
+ dummy_file.write('Windows 8 SDK has been patched!')
+
+ # Patched Windows 8 SDK found.
+ return 0
+
+
+if '__main__' == __name__:
+ sys.exit(main(sys.argv))
diff --git a/chromium/win8/util/win8_util.cc b/chromium/win8/util/win8_util.cc
new file mode 100644
index 00000000000..b630b8fdc1e
--- /dev/null
+++ b/chromium/win8/util/win8_util.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 "win8/util/win8_util.h"
+
+#include "base/win/metro.h"
+
+namespace win8 {
+
+bool IsSingleWindowMetroMode() {
+#if defined(USE_ASH)
+ return false;
+#else
+ return base::win::IsMetroProcess();
+#endif
+}
+
+} // namespace win8
diff --git a/chromium/win8/util/win8_util.h b/chromium/win8/util/win8_util.h
new file mode 100644
index 00000000000..25d6ec09a83
--- /dev/null
+++ b/chromium/win8/util/win8_util.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 WIN8_UTIL_WIN8_UTIL_H_
+#define WIN8_UTIL_WIN8_UTIL_H_
+
+namespace win8 {
+
+// Returns true if this process is running in fullscreen Metro mode on Win8+.
+// Callers should always prefer this method to base::win::IsMetroProcess() for
+// UI properties dependent on the single window mode as this method is also
+// aware of the Ash environment in which multiple Chrome windows can live.
+bool IsSingleWindowMetroMode();
+
+} // namespace win8
+
+#endif // WIN8_UTIL_WIN8_UTIL_H_
diff --git a/chromium/win8/viewer/metro_viewer_constants.cc b/chromium/win8/viewer/metro_viewer_constants.cc
new file mode 100644
index 00000000000..fd54fe461d1
--- /dev/null
+++ b/chromium/win8/viewer/metro_viewer_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 2013 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/viewer/metro_viewer_constants.h"
+
+namespace win8 {
+
+const char kMetroViewerIPCChannelName[] = "viewer";
+
+const wchar_t kMetroViewerConnectVerb[] = L"connect";
+
+} // namespace win8
diff --git a/chromium/win8/viewer/metro_viewer_constants.h b/chromium/win8/viewer/metro_viewer_constants.h
new file mode 100644
index 00000000000..560a4508d81
--- /dev/null
+++ b/chromium/win8/viewer/metro_viewer_constants.h
@@ -0,0 +1,20 @@
+// Copyright 2013 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_VIEWER_METRO_VIEWER_CONSTANTS_H_
+#define WIN8_VIEWER_METRO_VIEWER_CONSTANTS_H_
+
+namespace win8 {
+
+// The name of the IPC channel between the browser process and the metro viewer
+// process.
+extern const char kMetroViewerIPCChannelName[];
+
+// Tells the viewer process to simply connect back without needing to launch a
+// browser process itself.
+extern const wchar_t kMetroViewerConnectVerb[];
+
+} // namespace win8
+
+#endif // WIN8_VIEWER_METRO_VIEWER_CONSTANTS_H_
diff --git a/chromium/win8/viewer/metro_viewer_process_host.cc b/chromium/win8/viewer/metro_viewer_process_host.cc
new file mode 100644
index 00000000000..6832dbae6b7
--- /dev/null
+++ b/chromium/win8/viewer/metro_viewer_process_host.cc
@@ -0,0 +1,113 @@
+// Copyright (c) 2013 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/viewer/metro_viewer_process_host.h"
+
+#include <shlobj.h>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "base/win/scoped_comptr.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_macros.h"
+#include "ui/aura/remote_root_window_host_win.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/viewer/metro_viewer_constants.h"
+
+namespace win8 {
+
+MetroViewerProcessHost::InternalMessageFilter::InternalMessageFilter(
+ MetroViewerProcessHost* owner) : owner_(owner) {
+}
+
+void MetroViewerProcessHost::InternalMessageFilter::OnChannelConnected(
+ int32 /* peer_pid */) {
+ owner_->NotifyChannelConnected();
+}
+
+MetroViewerProcessHost::MetroViewerProcessHost(
+ base::SingleThreadTaskRunner* ipc_task_runner) {
+
+ channel_.reset(new IPC::ChannelProxy(
+ kMetroViewerIPCChannelName,
+ IPC::Channel::MODE_NAMED_SERVER,
+ this,
+ ipc_task_runner));
+}
+
+MetroViewerProcessHost::~MetroViewerProcessHost() {
+}
+
+base::ProcessId MetroViewerProcessHost::GetViewerProcessId() {
+ if (channel_)
+ return channel_->peer_pid();
+ return base::kNullProcessId;
+}
+
+bool MetroViewerProcessHost::LaunchViewerAndWaitForConnection(
+ const base::string16& app_user_model_id) {
+ DCHECK_EQ(base::kNullProcessId, channel_->peer_pid());
+
+ channel_connected_event_.reset(new base::WaitableEvent(false, false));
+
+ scoped_refptr<InternalMessageFilter> message_filter(
+ new InternalMessageFilter(this));
+ channel_->AddFilter(message_filter);
+
+ base::win::ScopedComPtr<IApplicationActivationManager> activator;
+ HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
+ if (SUCCEEDED(hr)) {
+ DWORD pid = 0;
+ // Use the "connect" verb to
+ hr = activator->ActivateApplication(
+ app_user_model_id.c_str(), kMetroViewerConnectVerb, AO_NONE, &pid);
+ }
+
+ LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
+ << "hr=" << std::hex << hr;
+
+ // Having launched the viewer process, now we wait for it to connect.
+ bool success =
+ channel_connected_event_->TimedWait(base::TimeDelta::FromSeconds(60));
+ channel_connected_event_.reset();
+
+ // |message_filter| is only used to signal |channel_connected_event_| above
+ // and can thus be removed after |channel_connected_event_| is no longer
+ // waiting.
+ channel_->RemoveFilter(message_filter);
+ return success;
+}
+
+bool MetroViewerProcessHost::Send(IPC::Message* msg) {
+ return channel_->Send(msg);
+}
+
+bool MetroViewerProcessHost::OnMessageReceived(
+ const IPC::Message& message) {
+ DCHECK(CalledOnValidThread());
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MetroViewerProcessHost, message)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetTargetSurface, OnSetTargetSurface)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_OpenURL, OnOpenURL)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SearchRequest, OnHandleSearchRequest)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowSizeChanged,
+ OnWindowSizeChanged)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled ? true :
+ aura::RemoteRootWindowHostWin::Instance()->OnMessageReceived(message);
+}
+
+void MetroViewerProcessHost::NotifyChannelConnected() {
+ if (channel_connected_event_)
+ channel_connected_event_->Signal();
+}
+
+} // namespace win8
diff --git a/chromium/win8/viewer/metro_viewer_process_host.h b/chromium/win8/viewer/metro_viewer_process_host.h
new file mode 100644
index 00000000000..ad034ada07f
--- /dev/null
+++ b/chromium/win8/viewer/metro_viewer_process_host.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2013 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_VIEWER_METRO_VIEWER_PROCESS_HOST_H_
+#define WIN8_VIEWER_METRO_VIEWER_PROCESS_HOST_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/threading/non_thread_safe.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_sender.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class WaitableEvent;
+}
+
+namespace IPC {
+class Message;
+}
+
+namespace win8 {
+
+// Abstract base class for various Metro viewer process host implementations.
+class MetroViewerProcessHost : public IPC::Listener,
+ public IPC::Sender,
+ public base::NonThreadSafe {
+ public:
+ // Initializes a viewer process host to connect to the Metro viewer process
+ // over IPC. The given task runner correspond to a thread on which
+ // IPC::Channel is created and used (e.g. IO thread). Instantly connects to
+ // the viewer process if one is already connected to |ipc_channel_name|; a
+ // viewer can otherwise be launched synchronously via
+ // LaunchViewerAndWaitForConnection().
+ explicit MetroViewerProcessHost(
+ base::SingleThreadTaskRunner* ipc_task_runner);
+ virtual ~MetroViewerProcessHost();
+
+ // Returns the process id of the viewer process if one is connected to this
+ // host, returns base::kNullProcessId otherwise.
+ base::ProcessId GetViewerProcessId();
+
+ // Launches the viewer process associated with the given |app_user_model_id|
+ // and blocks until that viewer process connects or until a timeout is
+ // reached. Returns true if the viewer process connects before the timeout is
+ // reached. NOTE: this assumes that the app referred to by |app_user_model_id|
+ // is registered as the default browser.
+ bool LaunchViewerAndWaitForConnection(
+ const base::string16& app_user_model_id);
+
+ private:
+ // IPC::Sender implementation:
+ virtual bool Send(IPC::Message* msg) OVERRIDE;
+
+ // IPC::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE = 0;
+
+ // Called over IPC by the viewer process to tell this host that it should be
+ // drawing to |target_surface|.
+ virtual void OnSetTargetSurface(gfx::NativeViewId target_surface) = 0;
+
+ // Called over IPC by the viewer process to request that the url passed in be
+ // opened.
+ virtual void OnOpenURL(const string16& url) = 0;
+
+ // Called over IPC by the viewer process to request that the search string
+ // passed in is passed to the default search provider and a URL navigation be
+ // performed.
+ virtual void OnHandleSearchRequest(const string16& search_string) = 0;
+
+ // Called over IPC by the viewer process when the window size has changed.
+ virtual void OnWindowSizeChanged(uint32 width, uint32 height) = 0;
+
+ void NotifyChannelConnected();
+
+ // Inner message filter used to handle connection event on the IPC channel
+ // proxy's background thread. This prevents consumers of
+ // MetroViewerProcessHost from having to pump messages on their own message
+ // loop.
+ class InternalMessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ InternalMessageFilter(MetroViewerProcessHost* owner);
+
+ // IPC::ChannelProxy::MessageFilter implementation.
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+
+ private:
+ MetroViewerProcessHost* owner_;
+ DISALLOW_COPY_AND_ASSIGN(InternalMessageFilter);
+ };
+
+ scoped_ptr<IPC::ChannelProxy> channel_;
+ scoped_ptr<base::WaitableEvent> channel_connected_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetroViewerProcessHost);
+};
+
+} // namespace win8
+
+#endif // WIN8_VIEWER_METRO_VIEWER_PROCESS_HOST_H_
diff --git a/chromium/win8/win8.gyp b/chromium/win8/win8.gyp
index 00bfa3bc663..dc73584f0e3 100644
--- a/chromium/win8/win8.gyp
+++ b/chromium/win8/win8.gyp
@@ -51,6 +51,17 @@
],
},
{
+ 'target_name': 'metro_viewer_constants',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'viewer/metro_viewer_constants.cc',
+ 'viewer/metro_viewer_constants.h',
+ ],
+ },
+ {
'target_name': 'metro_viewer',
'type': 'static_library',
'dependencies': [
@@ -58,10 +69,9 @@
'../ipc/ipc.gyp:ipc',
'../ui/aura/aura.gyp:aura',
'../ui/metro_viewer/metro_viewer.gyp:metro_viewer_messages',
+ 'metro_viewer_constants'
],
'sources': [
- 'viewer/metro_viewer_constants.cc',
- 'viewer/metro_viewer_constants.h',
'viewer/metro_viewer_process_host.cc',
'viewer/metro_viewer_process_host.h',
],