1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "stdafx.h"
6#include "chrome_url_launch_handler.h"
7#include "chrome_app_view.h"
8
9#include <assert.h>
10#include <shellapi.h>
11#include <shlobj.h>
12#include <string>
13
14#include "base/command_line.h"
15
16#include "winrt_utils.h"
17
18typedef winfoundtn::ITypedEventHandler<
19    winapp::Search::SearchPane*,
20    winapp::Search::SearchPaneQuerySubmittedEventArgs*> QuerySubmittedHandler;
21
22ChromeUrlLaunchHandler::ChromeUrlLaunchHandler() {
23  globals.is_initial_activation = true;
24  globals.initial_activation_kind = winapp::Activation::ActivationKind_Launch;
25  DVLOG(1) << __FUNCTION__;
26}
27
28// TODO(ananta)
29// Remove this once we consolidate metro driver with chrome.
30const wchar_t kMetroNavigationAndSearchMessage[] =
31    L"CHROME_METRO_NAV_SEARCH_REQUEST";
32
33ChromeUrlLaunchHandler::~ChromeUrlLaunchHandler() {
34  DVLOG(1) << __FUNCTION__;
35  search_pane_->remove_QuerySubmitted(query_submitted_token_);
36}
37
38HRESULT ChromeUrlLaunchHandler::Initialize() {
39  mswr::ComPtr<winapp::Search::ISearchPaneStatics> search_pane_statics;
40  HRESULT hr = winrt_utils::CreateActivationFactory(
41      RuntimeClass_Windows_ApplicationModel_Search_SearchPane,
42      search_pane_statics.GetAddressOf());
43  CheckHR(hr, "Failed to activate ISearchPaneStatics");
44
45  hr = search_pane_statics->GetForCurrentView(&search_pane_);
46  if (FAILED(hr)) {
47    LOG(ERROR) << "Failed to get search pane for current view";
48    return hr;
49  }
50
51  hr = search_pane_->add_QuerySubmitted(mswr::Callback<QuerySubmittedHandler>(
52      this,
53      &ChromeUrlLaunchHandler::OnQuerySubmitted).Get(),
54      &query_submitted_token_);
55  if (FAILED(hr)) {
56    LOG(ERROR) << "Failed to register for Query Submitted event";
57    return hr;
58  }
59  return hr;
60}
61
62HRESULT ChromeUrlLaunchHandler::OnQuerySubmitted(
63    winapp::Search::ISearchPane* search_pane,
64    winapp::Search::ISearchPaneQuerySubmittedEventArgs* args) {
65  DVLOG(1) << "OnQuerySubmitted";
66  HandleSearchRequest(args);
67  return S_OK;
68}
69
70template<class T>
71void ChromeUrlLaunchHandler::HandleSearchRequest(T* args) {
72  DVLOG(1) << __FUNCTION__;
73  mswrw::HString search_string;
74  args->get_QueryText(search_string.GetAddressOf());
75  base::string16 search_text(MakeStdWString(search_string.Get()));
76  globals.search_string = search_text;
77  DVLOG(1) << search_text.c_str();
78  // If this is the initial activation then we wait for Chrome to initiate the
79  // navigation. In all other cases navigate right away.
80  if (!globals.is_initial_activation)
81    InitiateNavigationOrSearchRequest(NULL, globals.search_string.c_str());
82}
83
84void ChromeUrlLaunchHandler::HandleProtocolLaunch(
85    winapp::Activation::IProtocolActivatedEventArgs* args) {
86  DVLOG(1) << __FUNCTION__;
87  mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
88  args->get_Uri(&uri);
89  mswrw::HString url;
90  uri->get_AbsoluteUri(url.GetAddressOf());
91  base::string16 actual_url(MakeStdWString(url.Get()));
92  globals.navigation_url = actual_url;
93
94  // If this is the initial activation then we wait for Chrome to initiate the
95  // navigation. In all other cases navigate right away.
96  if (!globals.is_initial_activation)
97    InitiateNavigationOrSearchRequest(globals.navigation_url.c_str(), 0);
98}
99
100// |launch_args| is an encoded command line, minus the executable name. To
101// find the URL to launch the first argument is used. If any other parameters
102// are encoded in |launch_args| they are ignored.
103base::string16 ChromeUrlLaunchHandler::GetUrlFromLaunchArgs(
104    const base::string16& launch_args) {
105  if (launch_args == L"opennewwindow") {
106    VLOG(1) << "Returning new tab url";
107    return L"chrome://newtab";
108  }
109  base::string16 dummy_command_line(L"dummy.exe ");
110  dummy_command_line.append(launch_args);
111  CommandLine command_line = CommandLine::FromString(dummy_command_line);
112  CommandLine::StringVector args = command_line.GetArgs();
113  if (args.size() > 0)
114    return args[0];
115
116  return base::string16();
117}
118
119void ChromeUrlLaunchHandler::HandleLaunch(
120    winapp::Activation::ILaunchActivatedEventArgs* args) {
121  mswrw::HString launch_args;
122  args->get_Arguments(launch_args.GetAddressOf());
123  base::string16 actual_launch_args(MakeStdWString(launch_args.Get()));
124  globals.navigation_url = GetUrlFromLaunchArgs(actual_launch_args);
125  DVLOG(1) << __FUNCTION__ << ", launch_args=" << actual_launch_args
126           << ", url=" << globals.navigation_url
127           << ", is_initial_activation=" << globals.is_initial_activation;
128
129  // If this is the initial launch then we wait for Chrome to initiate the
130  // navigation. In all other cases navigate right away.
131  if (!globals.is_initial_activation)
132    InitiateNavigationOrSearchRequest(globals.navigation_url.c_str(), 0);
133}
134
135void ChromeUrlLaunchHandler::Activate(
136    winapp::Activation::IActivatedEventArgs* args) {
137  winapp::Activation::ActivationKind activation_kind;
138  CheckHR(args->get_Kind(&activation_kind));
139
140  DVLOG(1) << __FUNCTION__ << ", activation_kind=" << activation_kind;
141
142  if (globals.is_initial_activation)
143    globals.initial_activation_kind = activation_kind;
144
145  if (activation_kind == winapp::Activation::ActivationKind_Launch) {
146    mswr::ComPtr<winapp::Activation::ILaunchActivatedEventArgs> launch_args;
147    if (args->QueryInterface(winapp::Activation::IID_ILaunchActivatedEventArgs,
148                             &launch_args) == S_OK) {
149      DVLOG(1) << "Activate: ActivationKind_Launch";
150      HandleLaunch(launch_args.Get());
151    }
152  } else if (activation_kind ==
153             winapp::Activation::ActivationKind_Search) {
154    mswr::ComPtr<winapp::Activation::ISearchActivatedEventArgs> search_args;
155    if (args->QueryInterface(winapp::Activation::IID_ISearchActivatedEventArgs,
156                             &search_args) == S_OK) {
157      DVLOG(1) << "Activate: ActivationKind_Search";
158      HandleSearchRequest(search_args.Get());
159    }
160  } else if (activation_kind ==
161             winapp::Activation::ActivationKind_Protocol) {
162    mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs>
163        protocol_args;
164    if (args->QueryInterface(
165            winapp::Activation::IID_IProtocolActivatedEventArgs,
166            &protocol_args) == S_OK) {
167      DVLOG(1) << "Activate: ActivationKind_Protocol";
168      HandleProtocolLaunch(protocol_args.Get());
169    }
170  } else {
171    DVLOG(1) << "Activate: Unhandled mode: " << activation_kind;
172  }
173}
174
175void ChromeUrlLaunchHandler::InitiateNavigationOrSearchRequest(
176    const wchar_t* url, const wchar_t* search_string) {
177  DVLOG(1) << __FUNCTION__;
178  if (!url && !search_string) {
179    NOTREACHED();
180    return;
181  }
182
183  DVLOG(1) << (url ? url : L"NULL url");
184  DVLOG(1) << (search_string ? search_string : L"NULL search string");
185
186  if (globals.host_windows.empty()) {
187    DVLOG(1) << "No chrome windows registered. Ignoring nav request";
188    return;
189  }
190
191  // Custom registered message to navigate or search in chrome. WPARAM
192  // points to the URL and LPARAM contains the search string. They are
193  // mutually exclusive.
194  static const UINT navigation_search_message =
195      RegisterWindowMessage(kMetroNavigationAndSearchMessage);
196
197  if (url) {
198    VLOG(1) << "Posting url:" << url;
199    PostMessage(globals.host_windows.front().first, navigation_search_message,
200                reinterpret_cast<WPARAM>(url), 0);
201  } else {
202    VLOG(1) << "Posting search string:" << search_string;
203    PostMessage(globals.host_windows.front().first, navigation_search_message,
204                0, reinterpret_cast<LPARAM>(search_string));
205  }
206}
207