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.
5 #include "chrome_frame/test/chrome_frame_ui_test_utils.h"
12 #include "base/bind.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/win/scoped_bstr.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome_frame/test/win_event_receiver.h"
22 #include "chrome_frame/utils.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "third_party/iaccessible2/ia2_api_all.h"
25 #include "ui/gfx/point.h"
26 #include "ui/gfx/rect.h"
28 namespace chrome_frame_test
{
30 // Timeout for waiting on Chrome to create the accessibility tree for the DOM.
31 const int kChromeDOMAccessibilityTreeTimeoutMs
= 10 * 1000;
33 // Timeout for waiting on a menu to popup.
34 const int kMenuPopupTimeoutMs
= 10 * 1000;
37 AccObject::AccObject(IAccessible
* accessible
, int child_id
)
38 : accessible_(accessible
), child_id_(child_id
) {
40 if (child_id
!= CHILDID_SELF
) {
41 base::win::ScopedComPtr
<IDispatch
> dispatch
;
42 // This class does not support referring to a full MSAA object using the
43 // parent object and the child id.
44 HRESULT result
= accessible_
->get_accChild(child_id_
, dispatch
.Receive());
45 if (result
!= S_FALSE
&& result
!= E_NOINTERFACE
) {
46 LOG(ERROR
) << "AccObject created which refers to full MSAA object using "
47 "parent object and child id. This should NOT be done.";
49 DCHECK(result
== S_FALSE
|| result
== E_NOINTERFACE
);
54 AccObject
* AccObject::CreateFromWindow(HWND hwnd
) {
55 base::win::ScopedComPtr
<IAccessible
> accessible
;
56 ::AccessibleObjectFromWindow(hwnd
, OBJID_CLIENT
,
57 IID_IAccessible
, reinterpret_cast<void**>(accessible
.Receive()));
59 return new AccObject(accessible
);
64 AccObject
* AccObject::CreateFromEvent(HWND hwnd
, LONG object_id
,
66 base::win::ScopedComPtr
<IAccessible
> accessible
;
67 base::win::ScopedVariant acc_child_id
;
68 ::AccessibleObjectFromEvent(hwnd
, object_id
, child_id
, accessible
.Receive(),
69 acc_child_id
.Receive());
70 if (accessible
&& acc_child_id
.type() == VT_I4
)
71 return new AccObject(accessible
, V_I4(&acc_child_id
));
76 AccObject
* AccObject::CreateFromDispatch(IDispatch
* dispatch
) {
78 base::win::ScopedComPtr
<IAccessible
> accessible
;
79 accessible
.QueryFrom(dispatch
);
81 return new AccObject(accessible
);
87 AccObject
* AccObject::CreateFromPoint(int x
, int y
) {
88 base::win::ScopedComPtr
<IAccessible
> accessible
;
89 base::win::ScopedVariant child_id
;
91 ::AccessibleObjectFromPoint(point
, accessible
.Receive(), child_id
.Receive());
92 if (accessible
&& child_id
.type() == VT_I4
)
93 return new AccObject(accessible
, V_I4(&child_id
));
97 bool AccObject::DoDefaultAction() {
98 // Prevent clients from using this method to try to select menu items, which
99 // does not work with a locked desktop.
100 std::wstring class_name
;
101 if (GetWindowClassName(&class_name
)) {
102 DCHECK(class_name
!= L
"#32768") << "Do not use DoDefaultAction with menus";
105 HRESULT result
= accessible_
->accDoDefaultAction(child_id_
);
106 EXPECT_HRESULT_SUCCEEDED(result
)
107 << "Could not do default action for AccObject: " << GetDescription();
108 return SUCCEEDED(result
);
111 bool AccObject::LeftClick() {
112 return PostMouseClickAtCenter(WM_LBUTTONDOWN
, WM_LBUTTONUP
);
115 bool AccObject::RightClick() {
116 return PostMouseClickAtCenter(WM_RBUTTONDOWN
, WM_RBUTTONUP
);
119 bool AccObject::Focus() {
120 EXPECT_HRESULT_SUCCEEDED(
121 accessible_
->accSelect(SELFLAG_TAKEFOCUS
, child_id_
));
123 // Double check that the object actually received focus. In some cases
124 // the parent object must have the focus first.
125 bool did_focus
= false;
126 base::win::ScopedVariant focused
;
127 if (SUCCEEDED(accessible_
->get_accFocus(focused
.Receive()))) {
128 if (focused
.type() != VT_EMPTY
)
131 EXPECT_TRUE(did_focus
) << "Could not focus AccObject: " << GetDescription();
135 bool AccObject::Select() {
136 // SELFLAG_TAKESELECTION needs to be combined with the focus in order to
138 int selection_flag
= SELFLAG_TAKEFOCUS
| SELFLAG_TAKESELECTION
;
139 EXPECT_HRESULT_SUCCEEDED(accessible_
->accSelect(selection_flag
, child_id_
));
141 // Double check that the object actually received selection.
142 bool did_select
= false;
143 base::win::ScopedVariant selected
;
144 if (SUCCEEDED(accessible_
->get_accSelection(selected
.Receive()))) {
145 if (selected
.type() != VT_EMPTY
)
148 EXPECT_TRUE(did_select
) << "Could not select AccObject: " << GetDescription();
152 bool AccObject::SetValue(const std::wstring
& value
) {
153 base::win::ScopedBstr
value_bstr(value
.c_str());
154 EXPECT_HRESULT_SUCCEEDED(accessible_
->put_accValue(child_id_
, value_bstr
));
156 // Double check that the object's value has actually changed. Some objects'
157 // values can not be changed.
158 bool did_set_value
= false;
159 std::wstring actual_value
= L
"-";
160 if (GetValue(&actual_value
) && value
== actual_value
) {
161 did_set_value
= true;
163 EXPECT_TRUE(did_set_value
) << "Could not set value for AccObject: "
165 return did_set_value
;
168 bool AccObject::GetName(std::wstring
* name
) {
170 base::win::ScopedBstr name_bstr
;
171 HRESULT result
= accessible_
->get_accName(child_id_
, name_bstr
.Receive());
172 if (SUCCEEDED(result
))
173 name
->assign(name_bstr
, name_bstr
.Length());
174 return SUCCEEDED(result
);
177 bool AccObject::GetRoleText(std::wstring
* role_text
) {
179 base::win::ScopedVariant role_variant
;
180 if (SUCCEEDED(accessible_
->get_accRole(child_id_
, role_variant
.Receive()))) {
181 if (role_variant
.type() == VT_I4
) {
182 wchar_t role_text_array
[50];
183 UINT characters
= ::GetRoleText(V_I4(&role_variant
), role_text_array
,
184 arraysize(role_text_array
));
186 *role_text
= role_text_array
;
189 LOG(ERROR
) << "GetRoleText failed for role: " << V_I4(&role_variant
);
191 } else if (role_variant
.type() == VT_BSTR
) {
192 *role_text
= V_BSTR(&role_variant
);
195 LOG(ERROR
) << "Role was unexpected variant type: "
196 << role_variant
.type();
202 bool AccObject::GetValue(std::wstring
* value
) {
204 base::win::ScopedBstr value_bstr
;
205 HRESULT result
= accessible_
->get_accValue(child_id_
, value_bstr
.Receive());
206 if (SUCCEEDED(result
))
207 value
->assign(value_bstr
, value_bstr
.Length());
208 return SUCCEEDED(result
);
211 bool AccObject::GetState(int* state
) {
213 base::win::ScopedVariant state_variant
;
214 if (SUCCEEDED(accessible_
->get_accState(child_id_
,
215 state_variant
.Receive()))) {
216 if (state_variant
.type() == VT_I4
) {
217 *state
= V_I4(&state_variant
);
224 bool AccObject::GetLocation(gfx::Rect
* location
) {
226 long left
, top
, width
, height
; // NOLINT
227 HRESULT result
= accessible_
->accLocation(&left
, &top
, &width
, &height
,
229 if (SUCCEEDED(result
))
230 *location
= gfx::Rect(left
, top
, width
, height
);
231 return SUCCEEDED(result
);
234 bool AccObject::GetLocationInClient(gfx::Rect
* client_location
) {
235 DCHECK(client_location
);
237 if (!GetLocation(&location
))
239 HWND container_window
= NULL
;
240 if (!GetWindow(&container_window
))
242 POINT offset
= {0, 0};
243 if (!::ScreenToClient(container_window
, &offset
)) {
244 LOG(ERROR
) << "Could not convert from screen to client coordinates for "
245 "window containing accessibility object: "
249 location
.Offset(offset
.x
, offset
.y
);
250 *client_location
= location
;
254 AccObject
* AccObject::GetParent() {
255 if (IsSimpleElement())
256 return new AccObject(accessible_
);
257 base::win::ScopedComPtr
<IDispatch
> dispatch
;
258 if (FAILED(accessible_
->get_accParent(dispatch
.Receive())))
260 return AccObject::CreateFromDispatch(dispatch
.get());
263 bool AccObject::GetChildren(RefCountedAccObjectVector
* client_objects
) {
264 DCHECK(client_objects
);
266 if (!GetChildCount(&child_count
)) {
267 LOG(ERROR
) << "Failed to get child count of AccObject";
270 if (child_count
== 0)
273 RefCountedAccObjectVector objects
;
274 // Find children using |AccessibleChildren|.
275 scoped_ptr
<VARIANT
[]> children(new VARIANT
[child_count
]);
276 long found_child_count
; // NOLINT
277 if (FAILED(AccessibleChildren(accessible_
, 0L, child_count
,
279 &found_child_count
))) {
280 LOG(ERROR
) << "Failed to get children of accessible object";
283 if (found_child_count
> 0) {
284 for (int i
= 0; i
< found_child_count
; i
++) {
285 scoped_refptr
<AccObject
> obj
= CreateFromVariant(this, children
[i
]);
287 objects
.push_back(obj
);
288 ::VariantClear(&children
[i
]);
292 // In some cases, there are more children which can be found only by using
293 // the deprecated |accNavigate| method. Many of the menus, such as
294 // 'Favorites', cannot be found in IE6 using |AccessibileChildren|. Here we
295 // attempt a best effort at finding some remaining children.
296 int remaining_child_count
= child_count
- found_child_count
;
297 scoped_refptr
<AccObject
> child_object
;
298 if (remaining_child_count
> 0) {
299 GetFromNavigation(NAVDIR_FIRSTCHILD
, &child_object
);
301 while (remaining_child_count
> 0 && child_object
) {
302 // Add to the children list if this child was not found earlier.
303 bool already_found
= false;
304 for (size_t i
= 0; i
< objects
.size(); ++i
) {
305 if (child_object
->Equals(objects
[i
])) {
306 already_found
= true;
310 if (!already_found
) {
311 objects
.push_back(child_object
);
312 remaining_child_count
--;
314 scoped_refptr
<AccObject
> next_child_object
;
315 child_object
->GetFromNavigation(NAVDIR_NEXT
, &next_child_object
);
316 child_object
= next_child_object
;
319 client_objects
->insert(client_objects
->end(), objects
.begin(), objects
.end());
323 bool AccObject::GetChildCount(int* child_count
) {
326 if (!IsSimpleElement()) {
327 long long_child_count
; // NOLINT
328 if (FAILED(accessible_
->get_accChildCount(&long_child_count
)))
330 *child_count
= static_cast<int>(long_child_count
);
335 bool AccObject::GetFromNavigation(long navigation_type
,
336 scoped_refptr
<AccObject
>* object
) {
338 bool is_child_navigation
= navigation_type
== NAVDIR_FIRSTCHILD
||
339 navigation_type
== NAVDIR_LASTCHILD
;
340 DCHECK(!is_child_navigation
|| !IsSimpleElement());
341 base::win::ScopedVariant object_variant
;
342 HRESULT result
= accessible_
->accNavigate(navigation_type
,
344 object_variant
.Receive());
345 if (FAILED(result
)) {
346 LOG(WARNING
) << "Navigation from accessibility object failed";
349 if (result
== S_FALSE
|| object_variant
.type() == VT_EMPTY
) {
350 // This indicates that there was no accessibility object found by the
354 AccObject
* navigated_to_object
;
355 if (!is_child_navigation
&& !IsSimpleElement()) {
356 scoped_refptr
<AccObject
> parent
= GetParent();
358 LOG(WARNING
) << "Could not get parent for accessibiliy navigation";
361 navigated_to_object
= CreateFromVariant(parent
, object_variant
);
363 navigated_to_object
= CreateFromVariant(this, object_variant
);
365 if (!navigated_to_object
)
367 *object
= navigated_to_object
;
371 bool AccObject::GetWindow(HWND
* window
) {
373 return SUCCEEDED(::WindowFromAccessibleObject(accessible_
, window
)) && window
;
376 bool AccObject::GetWindowClassName(std::wstring
* class_name
) {
378 HWND container_window
= NULL
;
379 if (GetWindow(&container_window
)) {
380 wchar_t class_arr
[MAX_PATH
];
381 if (::GetClassName(container_window
, class_arr
, arraysize(class_arr
))) {
382 *class_name
= class_arr
;
389 bool AccObject::GetSelectionRange(int* start_offset
, int* end_offset
) {
390 DCHECK(start_offset
);
392 base::win::ScopedComPtr
<IAccessibleText
> accessible_text
;
393 HRESULT hr
= DoQueryService(IID_IAccessibleText
,
395 accessible_text
.Receive());
397 LOG(ERROR
) << "Could not get IAccessibleText interface. Error: " << hr
398 << "\nIs IAccessible2Proxy.dll registered?";
402 LONG selection_count
= 0;
403 accessible_text
->get_nSelections(&selection_count
);
404 LONG start
= 0, end
= 0;
405 if (selection_count
> 0) {
406 if (FAILED(accessible_text
->get_selection(0, &start
, &end
))) {
407 LOG(WARNING
) << "Could not get first selection";
411 *start_offset
= start
;
416 bool AccObject::GetSelectedText(std::wstring
* text
) {
418 int start
= 0, end
= 0;
419 if (!GetSelectionRange(&start
, &end
))
421 base::win::ScopedComPtr
<IAccessibleText
> accessible_text
;
422 HRESULT hr
= DoQueryService(IID_IAccessibleText
,
424 accessible_text
.Receive());
426 LOG(ERROR
) << "Could not get IAccessibleText interface. Error: " << hr
427 << "\nIs IAccessible2Proxy.dll registered?";
430 base::win::ScopedBstr text_bstr
;
431 if (FAILED(accessible_text
->get_text(start
, end
, text_bstr
.Receive()))) {
432 LOG(WARNING
) << "Could not get text from selection range";
435 text
->assign(text_bstr
, text_bstr
.Length());
439 bool AccObject::IsSimpleElement() {
440 return V_I4(&child_id_
) != CHILDID_SELF
;
443 bool AccObject::Equals(AccObject
* other
) {
445 DCHECK(child_id_
.type() == VT_I4
&& other
->child_id_
.type() == VT_I4
);
446 return accessible_
.get() == other
->accessible_
.get() &&
447 V_I4(&child_id_
) == V_I4(&other
->child_id_
);
452 std::wstring
AccObject::GetDescription() {
453 std::wstring name
= L
"-", role_text
= L
"-", value
= L
"-";
455 name
= L
"'" + name
+ L
"'";
456 if (GetRoleText(&role_text
))
457 role_text
= L
"'" + role_text
+ L
"'";
458 if (GetValue(&value
))
459 value
= L
"'" + value
+ L
"'";
462 return base::StringPrintf(L
"[%ls, %ls, %ls, 0x%x]", name
.c_str(),
463 role_text
.c_str(), value
.c_str(), state
);
466 std::wstring
AccObject::GetTree() {
467 std::wostringstream string_stream
;
468 string_stream
<< L
"Accessibility object tree:" << std::endl
;
469 string_stream
<< L
"[name, role_text, value, state]" << std::endl
;
471 std::stack
<std::pair
<scoped_refptr
<AccObject
>, int> > pairs
;
472 pairs
.push(std::make_pair(this, 0));
473 while (!pairs
.empty()) {
474 scoped_refptr
<AccObject
> object
= pairs
.top().first
;
475 int depth
= pairs
.top().second
;
478 for (int i
= 0; i
< depth
; ++i
)
479 string_stream
<< L
" ";
480 string_stream
<< object
->GetDescription() << std::endl
;
482 RefCountedAccObjectVector children
;
483 if (object
->GetChildren(&children
)) {
484 for (int i
= static_cast<int>(children
.size()) - 1; i
>= 0; --i
)
485 pairs
.push(std::make_pair(children
[i
], depth
+ 1));
488 return string_stream
.str();
492 AccObject
* AccObject::CreateFromVariant(AccObject
* object
,
493 const VARIANT
& variant
) {
494 IAccessible
* accessible
= object
->accessible_
;
495 if (V_VT(&variant
) == VT_I4
) {
496 // According to MSDN, a server is allowed to return a full Accessibility
497 // object using the parent object and the child id. If get_accChild is
498 // called with the id, the server must return the actual IAccessible
499 // interface. Do that here to get an actual IAccessible interface if
500 // possible, since this class operates under the assumption that if the
501 // child id is not CHILDID_SELF, the object is a simple element. See the
502 // DCHECK in the constructor.
503 base::win::ScopedComPtr
<IDispatch
> dispatch
;
504 HRESULT result
= accessible
->get_accChild(variant
,
506 if (result
== S_FALSE
|| result
== E_NOINTERFACE
) {
507 // The object in question really is a simple element.
508 return new AccObject(accessible
, V_I4(&variant
));
509 } else if (SUCCEEDED(result
)) {
510 // The object in question was actually a full object.
511 return CreateFromDispatch(dispatch
.get());
513 VLOG(1) << "Failed to determine if child id refers to a full "
514 << "object. Error: " << result
<< std::endl
515 << "Parent object: " << WideToUTF8(object
->GetDescription())
516 << std::endl
<< "Child ID: " << V_I4(&variant
);
518 } else if (V_VT(&variant
) == VT_DISPATCH
) {
519 return CreateFromDispatch(V_DISPATCH(&variant
));
521 LOG(WARNING
) << "Unrecognizable child type";
525 bool AccObject::PostMouseClickAtCenter(int button_down
, int button_up
) {
526 std::wstring class_name
;
527 if (!GetWindowClassName(&class_name
)) {
528 LOG(ERROR
) << "Could not get class name of window for accessibility "
529 << "object: " << GetDescription();
533 if (class_name
== L
"#32768") {
534 // For some reason, it seems that menus expect screen coordinates.
535 if (!GetLocation(&location
))
538 if (!GetLocationInClient(&location
))
542 gfx::Point center
= location
.CenterPoint();
543 return PostMouseButtonMessages(button_down
, button_up
,
544 center
.x(), center
.y());
547 bool AccObject::PostMouseButtonMessages(
548 int button_down
, int button_up
, int x
, int y
) {
549 HWND container_window
;
550 if (!GetWindow(&container_window
))
553 LPARAM coordinates
= MAKELPARAM(x
, y
);
554 ::PostMessage(container_window
, button_down
, 0, coordinates
);
555 ::PostMessage(container_window
, button_up
, 0, coordinates
);
559 // AccObjectMatcher methods
560 AccObjectMatcher::AccObjectMatcher(const std::wstring
& name
,
561 const std::wstring
& role_text
,
562 const std::wstring
& value
)
563 : name_(name
), role_text_(role_text
), value_(value
) {
566 bool AccObjectMatcher::FindHelper(AccObject
* object
,
567 scoped_refptr
<AccObject
>* match
) const {
568 if (DoesMatch(object
)) {
571 // Try to match the children of |object|.
572 AccObject::RefCountedAccObjectVector children
;
573 if (!object
->GetChildren(&children
)) {
574 LOG(ERROR
) << "Could not get children of AccObject";
577 for (size_t i
= 0; i
< children
.size(); ++i
) {
578 if (!FindHelper(children
[i
], match
)) {
588 bool AccObjectMatcher::Find(AccObject
* object
,
589 scoped_refptr
<AccObject
>* match
) const {
593 return FindHelper(object
, match
);
596 bool AccObjectMatcher::FindInWindow(HWND hwnd
,
597 scoped_refptr
<AccObject
>* match
) const {
598 scoped_refptr
<AccObject
> object(AccObject::CreateFromWindow(hwnd
));
600 VLOG(1) << "Failed to get accessible object from window";
603 return Find(object
.get(), match
);
606 bool AccObjectMatcher::DoesMatch(AccObject
* object
) const {
608 bool does_match
= true;
609 std::wstring name
, role_text
, value
;
610 if (name_
.length()) {
611 object
->GetName(&name
);
612 does_match
= MatchPattern(StringToUpperASCII(name
),
613 StringToUpperASCII(name_
));
615 if (does_match
&& role_text_
.length()) {
616 object
->GetRoleText(&role_text
);
617 does_match
= MatchPattern(role_text
, role_text_
);
619 if (does_match
&& value_
.length()) {
620 object
->GetValue(&value
);
621 does_match
= MatchPattern(value
, value_
);
626 std::wstring
AccObjectMatcher::GetDescription() const {
627 std::wostringstream ss
;
630 ss
<< L
"Name: '" << name_
<< L
"', ";
631 if (role_text_
.length())
632 ss
<< L
"Role: '" << role_text_
<< L
"', ";
634 ss
<< L
"Value: '" << value_
<< L
"'";
639 // AccEventObserver methods
640 AccEventObserver::AccEventObserver()
641 : event_handler_(new EventHandler(this)),
642 is_watching_(false) {
643 event_receiver_
.SetListenerForEvents(this, EVENT_SYSTEM_MENUPOPUPSTART
,
644 EVENT_OBJECT_VALUECHANGE
);
647 AccEventObserver::~AccEventObserver() {
648 event_handler_
->observer_
= NULL
;
651 void AccEventObserver::WatchForOneValueChange(const AccObjectMatcher
& matcher
) {
653 watching_for_matcher_
= matcher
;
656 void AccEventObserver::OnEventReceived(DWORD event
,
660 // Process events in a separate task to stop reentrancy problems.
661 DCHECK(base::MessageLoop::current());
662 base::MessageLoop::current()->PostTask(
663 FROM_HERE
, base::Bind(&EventHandler::Handle
, event_handler_
.get(), event
,
664 hwnd
, object_id
, child_id
));
667 // AccEventObserver::EventHandler methods
668 AccEventObserver::EventHandler::EventHandler(AccEventObserver
* observer
)
669 : observer_(observer
) {
672 void AccEventObserver::EventHandler::Handle(DWORD event
,
680 case EVENT_SYSTEM_MENUPOPUPSTART
:
681 observer_
->OnMenuPopup(hwnd
);
683 case IA2_EVENT_DOCUMENT_LOAD_COMPLETE
:
684 observer_
->OnAccDocLoad(hwnd
);
686 case IA2_EVENT_TEXT_CARET_MOVED
: {
687 scoped_refptr
<AccObject
> object(
688 AccObject::CreateFromEvent(hwnd
, object_id
, child_id
));
690 observer_
->OnTextCaretMoved(hwnd
, object
.get());
693 case EVENT_OBJECT_VALUECHANGE
:
694 if (observer_
->is_watching_
) {
695 scoped_refptr
<AccObject
> object(
696 AccObject::CreateFromEvent(hwnd
, object_id
, child_id
));
698 if (observer_
->watching_for_matcher_
.DoesMatch(object
.get())) {
699 // Stop watching before calling OnAccValueChange in case the
700 // client invokes our watch method during the call.
701 observer_
->is_watching_
= false;
702 std::wstring new_value
;
703 if (object
->GetValue(&new_value
)) {
704 observer_
->OnAccValueChange(hwnd
, object
.get(), new_value
);
716 bool FindAccObjectInWindow(HWND hwnd
, const AccObjectMatcher
& matcher
,
717 scoped_refptr
<AccObject
>* object
) {
719 EXPECT_TRUE(matcher
.FindInWindow(hwnd
, object
));
720 EXPECT_TRUE(*object
) << "Element not found for matcher: "
721 << matcher
.GetDescription();
723 DumpAccessibilityTreeForWindow(hwnd
);
727 void DumpAccessibilityTreeForWindow(HWND hwnd
) {
728 scoped_refptr
<AccObject
> object(AccObject::CreateFromWindow(hwnd
));
730 std::wcout
<< object
->GetTree();
732 std::cout
<< "Could not get IAccessible for window" << std::endl
;
735 bool IsDesktopUnlocked() {
736 HDESK desk
= ::OpenInputDesktop(0, FALSE
, DESKTOP_SWITCHDESKTOP
);
738 ::CloseDesktop(desk
);
742 base::FilePath
GetIAccessible2ProxyStubPath() {
744 PathService::Get(chrome::DIR_APP
, &path
);
745 return path
.AppendASCII("IAccessible2Proxy.dll");
748 } // namespace chrome_frame_test