1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
|
// Copyright (c) 2010 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.
//
// Window API implementation.
//
// Window IDs are the HWND of the top-level frame window of IE.
#include "ceee/ie/broker/window_api_module.h"
#include <atlbase.h>
#include <iepmapi.h>
#include <sddl.h>
#include <set>
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "ceee/common/com_utils.h"
#include "ceee/common/process_utils_win.h"
#include "ceee/common/windows_constants.h"
#include "ceee/common/window_utils.h"
#include "ceee/ie/broker/api_module_constants.h"
#include "ceee/ie/broker/api_module_util.h"
#include "ceee/ie/broker/tab_api_module.h"
#include "ceee/ie/common/ie_util.h"
#include "ceee/ie/common/api_registration.h"
#include "chrome/browser/extensions/extension_event_names.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "googleurl/src/gurl.h"
namespace ext = extension_tabs_module_constants;
namespace ext_event_names = extension_event_names;
namespace window_api {
// We sometimes need to wait for window creation and removal to be completed
// even though we receive an event about them, these events sometimes arrive
// before the creation/removal is really totally completed.
const int kMaxDelayMs = 5000; // 5 seconds may be needed on slow machines.
const int kDelayMs = 50;
void RegisterInvocations(ApiDispatcher* dispatcher) {
#define REGISTER_API_FUNCTION(func) do { dispatcher->RegisterInvocation(\
func##Function::function_name(), NewApiInvocation< func >); } while (false)
REGISTER_WINDOW_API_FUNCTIONS();
#undef REGISTER_API_FUNCTION
dispatcher->RegisterInvocation(CreateWindowFunction::function_name(),
NewApiInvocation< CreateWindowFunc >);
// And now register the permanent event handlers.
dispatcher->RegisterPermanentEventHandler(ext_event_names::kOnWindowRemoved,
RemoveWindow::EventHandler);
dispatcher->RegisterPermanentEventHandler(ext_event_names::kOnWindowCreated,
CreateWindowFunc::EventHandler);
}
bool WindowApiResult::UpdateWindowRect(HWND window,
const DictionaryValue* window_props) {
ApiDispatcher* dispatcher = GetDispatcher();
DCHECK(dispatcher != NULL);
int window_id = dispatcher->GetWindowIdFromHandle(window);
if (window_utils::WindowHasNoThread(window)) {
PostError(ExtensionErrorUtils::FormatErrorMessage(
ext::kWindowNotFoundError, base::IntToString(window_id)));
return false;
}
if (!IsIeFrameClass(window)) {
PostError(ExtensionErrorUtils::FormatErrorMessage(
ext::kWindowNotFoundError, base::IntToString(window_id)));
return false;
}
// Unspecified entries are set to -1 to let the executor know not to change
// them.
long left = -1;
long top = -1;
long width = -1;
long height = -1;
if (window_props) {
int dim;
if (window_props->HasKey(ext::kLeftKey)) {
if (!window_props->GetInteger(ext::kLeftKey, &dim)) {
PostError("bad request");
return false;
}
left = dim;
}
if (window_props->HasKey(ext::kTopKey)) {
if (!window_props->GetInteger(ext::kTopKey, &dim)) {
PostError("bad request");
return false;
}
top = dim;
}
if (window_props->HasKey(ext::kWidthKey)) {
if (!window_props->GetInteger(ext::kWidthKey, &dim) || dim < 0) {
PostError("bad request");
return false;
}
width = dim;
}
if (window_props->HasKey(ext::kHeightKey)) {
if (!window_props->GetInteger(ext::kHeightKey, &dim) || dim < 0) {
PostError("bad request");
return false;
}
height = dim;
}
}
common_api::WindowInfo window_info;
if (left != -1 || top != -1 || width != -1 || height != -1) {
CComPtr<ICeeeWindowExecutor> executor;
dispatcher->GetExecutor(window, IID_ICeeeWindowExecutor,
reinterpret_cast<void**>(&executor));
if (executor == NULL) {
LOG(WARNING) << "Failed to get an executor to update window.";
PostError(api_module_constants::kInternalErrorError);
return false;
}
HRESULT hr = executor->UpdateWindow(left, top, width, height, &window_info);
if (FAILED(hr)) {
LOG(ERROR) << "Couldn't update window: " << std::hex << window << ". " <<
com::LogHr(hr);
PostError(api_module_constants::kInternalErrorError);
return false;
}
SetResultFromWindowInfo(window, window_info, false);
return true;
} else {
return CreateWindowValue(window, false);
}
}
void GetWindow::Execute(const ListValue& args, int request_id) {
scoped_ptr<WindowApiResult> result(CreateApiResult(request_id));
int window_id = 0;
if (!args.GetInteger(0, &window_id)) {
NOTREACHED() << "bad message";
result->PostError(api_module_constants::kInvalidArgumentsError);
return;
}
// CreateWindowValue validates the HWND.
ApiDispatcher* dispatcher = GetDispatcher();
DCHECK(dispatcher != NULL);
HWND window = dispatcher->GetWindowHandleFromId(window_id);
if (result->CreateWindowValue(window, false))
result->PostResult();
}
void GetCurrentWindow::Execute(const ListValue& args, int request_id) {
// TODO(mad@chromium.org): We currently don't have access to the
// actual 'current' window from the point of view of the extension
// API caller. Use the top window for now. bb2255140
scoped_ptr<WindowApiResult> result(CreateApiResult(request_id));
if (result->CreateWindowValue(result->TopIeWindow(), false))
result->PostResult();
}
void GetLastFocusedWindow::Execute(const ListValue& args, int request_id) {
scoped_ptr<WindowApiResult> result(CreateApiResult(request_id));
if (result->CreateWindowValue(result->TopIeWindow(), false))
result->PostResult();
}
bool CreateWindowFunc::EventHandler(const std::string& input_args,
std::string* converted_args,
ApiDispatcher* dispatcher) {
DCHECK(converted_args);
// We don't need to modify anything in the arguments, we just want to delay.
*converted_args = input_args;
scoped_ptr<ListValue> args_list;
int window_id = 0;
if (!api_module_util::GetListAndDictIntValue(
input_args, ext::kIdKey, &args_list, &window_id)) {
NOTREACHED() << "Event arguments wasn't a dictionary with an ID in it. " <<
input_args;
return false;
}
// The hook may call us before the window is completely created, so we
// must delay the execution until the first tab is completely created.
// TODO(mad@chromium.org): Find a way to do this without blocking
// all other ApiDispatching.
HWND window = dispatcher->GetWindowHandleFromId(window_id);
int waited_ms = 0;
while (waited_ms < kMaxDelayMs &&
::IsWindow(window) &&
!window_utils::FindDescendentWindow(
window, windows::kIeTabWindowClass, false, NULL)) {
::SleepEx(kDelayMs, TRUE); // TRUE = Alertable.
waited_ms += kDelayMs;
}
// We don't DCHECK if the window died, but we must still return false
// if it did, so that we don't broadcast the event back to Chrome.
DCHECK(waited_ms < kMaxDelayMs || !::IsWindow(window));
return waited_ms < kMaxDelayMs && ::IsWindow(window);
}
void CreateWindowFunc::Execute(const ListValue& args, int request_id) {
scoped_ptr<WindowApiResult> result(CreateApiResult(request_id));
scoped_ptr<DictionaryValue> input_dict;
// The input dictionary is optional; if not provided, the args may be
// either an empty list or a list with a single null value element.
if (args.GetSize() > 0) {
Value* first_arg = NULL;
if (!args.Get(0, &first_arg) || first_arg == NULL) {
NOTREACHED() << "bad request";
result->PostError(api_module_constants::kInvalidArgumentsError);
return;
}
if (first_arg->GetType() != Value::TYPE_NULL) {
DictionaryValue* args_dict = NULL;
if (!args.GetDictionary(0, &args_dict) || args_dict == NULL) {
NOTREACHED() << "bad request";
result->PostError(api_module_constants::kInvalidArgumentsError);
return;
}
// Remember the arguments so that we can use them later.
input_dict.reset(static_cast<DictionaryValue*>(args_dict->DeepCopy()));
}
}
// Look for optional url.
scoped_ptr<GURL> spec(new GURL(chrome::kAboutBlankURL));
std::string url_input;
if (input_dict.get() != NULL) {
if (input_dict->HasKey(ext::kUrlKey)) {
if (!input_dict->GetString(ext::kUrlKey, &url_input)) {
NOTREACHED() << "bad request";
result->PostError(api_module_constants::kInvalidArgumentsError);
return;
}
spec.reset(new GURL(url_input));
if (!spec->is_valid()) {
result->PostError(ExtensionErrorUtils::FormatErrorMessage(
ext::kInvalidUrlError, url_input).c_str());
return;
}
}
}
// There are many ways to create new IE windows, but they don't all behave
// the same depending on the IE version, OS or even settings. The most
// reliable way we found was to CoCreate an instance of IE, but navigating
// this instance doesn't behave exactly the same on all platforms.
//
// The main problem is with protected mode. If an instance of IE is launched
// with protected mode on, and we navigate to a URL that doesn't need
// protection (or vice versa), then the navigation will need to occur in
// another browser, not the one that was launched in the inappropriate
// protected mode.
//
// On IE7 if you CoCreate an instance of IE from a process running at an
// elevated integrity level as our Broker is, you get an unprotected mode
// IE, and if you navigate to a URL that needs protected mode, it will either
// create a new IE with the proper protected mode, or re-use an existing one
// if one is already opened.
//
// On IE8 the current process' integrity level is not taken into account
// when CoCreating a new IE, it relies on the CLSID which is the regular one
// (CLSID_InternetExplorer) for protected mode and a new one used for running
// at medium integrity level (CLSID_InternetExplorerMedium). Also, if you
// would then navigate to a URL that has a different protected mode than the
// one used for the CoCreate, then, a new window will always be created, an
// existing one will never be used.
//
// The other alternatives we looked at was to
// 1) Explicitly take the IWebBrowser2 interface of an existing IE.
// But this can cause the navigate to create a new tab instead
// of a new window (even if we specified navOpenInNewWindow) if
// the tab settings specify that popups should be opened in a
// new tab as opposed to a new window.
// 2) Use the IELaunchURL API (available in iepmapi.h).
// Except from the fact that it always creates a set of new
// IE processes in their own individual session this would
// behave exactly as we would like... But the price is too high.
// And it isn't available on XP...
// 3) Still use CoCreateInstance but don't rely on the knowledge of the
// current OS or IE version but rely on notifications sent to the
// DWebBrowser2 events thingy. Experimenting with these was not trivial
// and in some case lead to a non-deterministic way of identifying if
// a new window had been created or not.
// We need to know whether we will navigate to a URL that needs protected
// mode enabled or not. On earlier versions of the OS (e.g., pre-Vista),
// this call simply fails and thus act as if we didn't need protected mode.
std::wstring url = UTF8ToWide(spec->spec());
HRESULT protected_mode_url_hr = ::IEIsProtectedModeURL(url.c_str());
// We default to CLSID_InternetExplorer and CLSCTX_ALL but we may need to
// switch to CLSID_InternetExplorerMedium or add CLSCTX_ENABLE_CLOAKING later.
DWORD class_context = CLSCTX_ALL;
CLSID ie_clsid = CLSID_InternetExplorer;
bool lowered_integrity_level = false;
bool impersonating = false;
if (protected_mode_url_hr != S_OK) { // S_FALSE is returned for no.
// When we don't need protected mode, we need to explicitly
// request that IE8 starts the medium integrity version.
if (ie_util::GetIeVersion() == ie_util::IEVERSION_IE8)
ie_clsid = CLSID_InternetExplorerMedium;
} else if (ie_util::GetIeVersion() == ie_util::IEVERSION_IE7) {
// IE7 in protected mode needs to be started at lower integrity level
// than the broker process. So we must enable cloaking and temporary bring
// down the integrity level of this thread.
class_context |= CLSCTX_ENABLE_CLOAKING;
// We would have liked to use the CAccessToken class from ATL but it doesn't
// support the integrity level impersonation, just the owner, primary group
// and DACL. So we do it ourselves.
if (!::ImpersonateSelf(SecurityImpersonation)) {
DCHECK(false) << com::LogWe();
return;
}
// Remember that we successfully impersonated, so we can RevertToSelf.
impersonating = true;
// This call fails on XP, so we don't look at the error, we just log it and
// remember our success so we can revert it later. Specifying NULL for the
// thread pointer means that we want to affect the current thread.
HRESULT hr_lowered_integrity_level =
process_utils_win::SetThreadIntegrityLevel(NULL, SDDL_ML_LOW);
lowered_integrity_level = SUCCEEDED(hr_lowered_integrity_level);
DLOG_IF(WARNING, !lowered_integrity_level) <<
"SetThreadIntegrityLevelLow" << com::LogHr(hr_lowered_integrity_level);
}
// Now we can create a new web browser and be sure it will be the one that is
// kept (as well as its window) once we navigate.
CComPtr<IWebBrowser2> web_browser;
HRESULT hr = web_browser.CoCreateInstance(ie_clsid, NULL, class_context);
DCHECK(SUCCEEDED(hr)) << "Can't CoCreate IE! " << com::LogHr(hr);
if (FAILED(hr)) {
result->PostError(api_module_constants::kInternalErrorError);
return;
}
// And now we can bring back the integrity level to where it was.
if (lowered_integrity_level) {
HRESULT hr_integrity_level =
process_utils_win::ResetThreadIntegrityLevel(NULL);
DCHECK(SUCCEEDED(hr_integrity_level)) << "Failed to bring back thread " <<
"integrity level! " << com::LogHr(hr_integrity_level);
LOG_IF(WARNING, FAILED(hr)) << "ResetThreadIntegrityLevel(NULL) " <<
com::LogHr(hr_integrity_level);
}
// And stop impersonating.
if (impersonating) {
BOOL success = ::RevertToSelf();
DCHECK(success) << "Failed to stop impersonating! " << com::LogWe();
LOG_IF(WARNING, !success) << "RevertToSelf() " << com::LogWe();
}
// We need the HWND to create the window value to fill the info needed
// by the callback, and also, to potentially resize and position it.
HWND web_browserhwnd = NULL;
hr = web_browser->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&web_browserhwnd));
DCHECK(SUCCEEDED(hr)) << "Can't get HWND!" << com::LogHr(hr);
if (FAILED(hr)) {
result->PostError(api_module_constants::kInternalErrorError);
return;
}
if (input_dict.get() == NULL) {
// No arguments, so no need to popupize, resize or reposition.
if (!result->CreateWindowValue(web_browserhwnd, false)) {
// CreateWindowValue will have posted the error if any.
return;
}
} else {
// Popupize if needed.
std::string window_type;
if (input_dict->GetString(ext::kWindowTypeKey, &window_type) &&
window_type == ext::kWindowTypeValuePopup) {
HRESULT hr = web_browser->put_AddressBar(VARIANT_FALSE);
DCHECK(SUCCEEDED(hr)) << "Failed to hide address bar. " << com::LogHr(hr);
hr = web_browser->put_StatusBar(VARIANT_FALSE);
DCHECK(SUCCEEDED(hr)) << "Failed to hide status bar. " << com::LogHr(hr);
hr = web_browser->put_ToolBar(FALSE);
DCHECK(SUCCEEDED(hr)) << "Failed put_ToolBar. " << com::LogHr(hr);
}
// Reposition and Resize if needed.
if (!result->UpdateWindowRect(web_browserhwnd, input_dict.get())) {
// UpdateWindowRect will have posted the error if any.
return;
}
}
// Now we can Navigate to the requested url.
hr = web_browser->Navigate(CComBSTR(url.c_str()),
&CComVariant(), // unused flags
&CComVariant(L"_top"), // Target frame
&CComVariant(), // Unused POST DATA
&CComVariant()); // Unused Headers
DCHECK(SUCCEEDED(hr)) << "Can't Navigate IE to " << url << com::LogHr(hr);
if (FAILED(hr)) {
result->PostError(api_module_constants::kInternalErrorError);
return;
}
// A CoCreated IE is not visible until we ask it to be.
hr = web_browser->put_Visible(VARIANT_TRUE);
DCHECK(SUCCEEDED(hr)) << "put_Visible: " << com::LogHr(hr);
if (FAILED(hr)) {
result->PostError(api_module_constants::kInternalErrorError);
return;
}
result->PostResult();
}
void UpdateWindow::Execute(const ListValue& args, int request_id) {
scoped_ptr<WindowApiResult> result(CreateApiResult(request_id));
int window_id = 0;
DictionaryValue* update_props;
if (!args.GetInteger(0, &window_id) ||
!args.GetDictionary(1, &update_props)) {
NOTREACHED() << "bad message";
result->PostError(api_module_constants::kInvalidArgumentsError);
return;
}
ApiDispatcher* dispatcher = GetDispatcher();
DCHECK(dispatcher != NULL);
HWND window = dispatcher->GetWindowHandleFromId(window_id);
if (!result->IsIeFrameClass(window)) {
LOG(WARNING) << "Extension trying to access non-IE or dying window: " <<
std::hex << window_id;
result->PostError(ExtensionErrorUtils::FormatErrorMessage(
ext::kWindowNotFoundError, base::IntToString(window_id)).c_str());
return;
}
if (result->UpdateWindowRect(window, update_props))
result->PostResult();
}
void RemoveWindow::Execute(const ListValue& args, int request_id) {
scoped_ptr<WindowApiResult> result(CreateApiResult(request_id));
int window_id = 0;
if (!args.GetInteger(0, &window_id)) {
NOTREACHED() << "bad message";
result->PostError(api_module_constants::kInvalidArgumentsError);
return;
}
ApiDispatcher* dispatcher = GetDispatcher();
DCHECK(dispatcher != NULL);
HWND window = dispatcher->GetWindowHandleFromId(window_id);
if (!result->IsIeFrameClass(window)) {
LOG(WARNING) << "Extension trying to access non-IE or dying window: " <<
std::hex << window_id;
result->PostError(ExtensionErrorUtils::FormatErrorMessage(
ext::kWindowNotFoundError, base::IntToString(window_id)));
return;
}
CComPtr<ICeeeWindowExecutor> executor;
dispatcher->GetExecutor(window, IID_ICeeeWindowExecutor,
reinterpret_cast<void**>(&executor));
if (executor == NULL) {
LOG(WARNING) << "Failed to get an executor to remove window.";
result->PostError(api_module_constants::kInternalErrorError);
return;
}
HRESULT hr = executor->RemoveWindow();
if (FAILED(hr)) {
LOG(ERROR) << "Executor failed to remove window: " << std::hex <<
window_id << ". " << com::LogHr(hr);
result->PostError("Internal Error trying to close window.");
return;
}
// S_FALSE is returned when there are no tabs to close, e.g., the initial
// tab has not finished loading by the time we are asked to close the window.
if (hr == S_FALSE) {
LOG(WARNING) << "Failed to get window manager to close the window, "
"trying WM_CLOSE instead." << com::LogHr(hr);
// We fall back to this to be slightly more robust, but this approach has
// the drawback that it shows a pop-up dialog with a message like "do you
// want to close N tabs" if there is more than one tab. And we may need
// to try a few times because we have seen cases where SendMessage didn't
// return 0 because the window couldn't process the message for some reason.
int waited_ms = 0;
while (waited_ms < kMaxDelayMs && ::SendMessage(window, WM_CLOSE, 0, 0)) {
::SleepEx(kDelayMs, TRUE); // Alertable.
waited_ms += kDelayMs;
}
DCHECK(waited_ms < kMaxDelayMs);
if (waited_ms >= kMaxDelayMs) {
result->PostError(api_module_constants::kInternalErrorError);
return;
}
}
// Now we must wait for the window removal to be completely done before
// posting the response back to Chrome Frame.
// And we remember the window identifier so that we can recognize the event.
result->SetValue(ext::kWindowIdKey, Value::CreateIntegerValue(window_id));
DCHECK(dispatcher != NULL);
dispatcher->RegisterEphemeralEventHandler(ext_event_names::kOnWindowRemoved,
RemoveWindow::ContinueExecution,
result.release());
}
HRESULT RemoveWindow::ContinueExecution(
const std::string& input_args,
ApiDispatcher::InvocationResult* user_data,
ApiDispatcher* dispatcher) {
DCHECK(dispatcher != NULL);
DCHECK(user_data != NULL);
scoped_ptr<WindowApiResult> result(static_cast<WindowApiResult*>(user_data));
scoped_ptr<ListValue> args_list;
int window_id = 0;
if (!api_module_util::GetListAndIntegerValue(input_args, &args_list,
&window_id)) {
NOTREACHED() << "Event arguments are not a list with an integer in it.";
result->PostError(api_module_constants::kInternalErrorError);
return E_INVALIDARG;
}
const Value* saved_window_value = result->GetValue(ext::kWindowIdKey);
DCHECK(saved_window_value != NULL &&
saved_window_value->IsType(Value::TYPE_INTEGER));
int saved_window_id = 0;
bool success = saved_window_value->GetAsInteger(&saved_window_id);
DCHECK(success && saved_window_id != 0);
if (saved_window_id == window_id) {
// The windows.remove callback doesn't have any arguments.
result->set_value(NULL);
result->PostResult();
return S_OK;
} else {
// release doesn't destroy result, we need to keep it for next try.
result.release();
return S_FALSE; // S_FALSE keeps us in the queue.
}
}
bool RemoveWindow::EventHandler(const std::string& input_args,
std::string* converted_args,
ApiDispatcher* dispatcher) {
DCHECK(converted_args);
// We don't need to modify anything in the arguments, we just want to delay.
*converted_args = input_args;
scoped_ptr<Value> input_args_val(base::JSONReader::Read(input_args, true));
DCHECK(input_args_val.get() != NULL &&
input_args_val->IsType(Value::TYPE_LIST));
if (input_args_val.get() == NULL || !input_args_val->IsType(Value::TYPE_LIST))
return false;
int window_id = 0;
bool success = static_cast<ListValue*>(input_args_val.get())->GetInteger(
0, &window_id);
DCHECK(success) << "Couldn't get an int window Id from input args.";
if (!success)
return false;
// The hook may call us before the window has completely disappeared so we
// must delay the execution until the window is completely gone.
// TODO(mad@chromium.org): Find a way to do this without blocking
// all other ApiDispatching.
HWND window = dispatcher->GetWindowHandleFromId(window_id);
int waited_ms = 0;
while (waited_ms < kMaxDelayMs && ::IsWindow(window)) {
::SleepEx(kDelayMs, TRUE); // Alertable.
waited_ms += kDelayMs;
}
DCHECK(waited_ms < kMaxDelayMs);
if (waited_ms < kMaxDelayMs) {
return true;
} else {
return false;
}
}
void GetAllWindows::Execute(const ListValue& args, int request_id) {
scoped_ptr<IterativeWindowApiResult> result(CreateApiResult(request_id));
bool populate_tabs = false;
// The input dictionary is optional; if not provided, the args may be
// either an empty list or a list with a single null value element.
if (args.GetSize() > 0) {
Value* first_arg = NULL;
if (!args.Get(0, &first_arg) || first_arg == NULL) {
NOTREACHED() << "bad request";
result->PostError(api_module_constants::kInvalidArgumentsError);
} else if (first_arg->GetType() != Value::TYPE_NULL) {
DictionaryValue* dict = NULL;
if (!args.GetDictionary(0, &dict) ||
(dict->HasKey(ext::kPopulateKey) &&
!dict->GetBoolean(ext::kPopulateKey, &populate_tabs))) {
NOTREACHED() << "bad message";
result->PostError(api_module_constants::kInvalidArgumentsError);
}
}
if (result->AllFailed()) {
result->FlushAllPosts();
return;
}
}
std::set<HWND> ie_frame_windows;
window_utils::FindTopLevelWindows(windows::kIeFrameWindowClass,
&ie_frame_windows);
if (ie_frame_windows.empty()) {
result->FlushAllPosts();
return;
}
std::set<HWND>::const_iterator iter = ie_frame_windows.begin();
for (; iter != ie_frame_windows.end(); ++iter) {
if (result->CreateWindowValue(*iter, populate_tabs))
result->PostResult();
}
DCHECK(!result->IsEmpty());
if (result->IsEmpty()) // This is an error!
result->PostError(api_module_constants::kInternalErrorError);
result->FlushAllPosts();
}
} // namespace window_api
|