提交 1c145489 编写于 作者: G George Wright 提交者: George Wright

Initial import from upstream Chromium of:

AXActivePopup
AXFragmentRootWin
AXPlatformRelationWin
AXPlatformNodeDelegateUtilsWin
AXPlatformNodeWin
Range
ScopedVariant
ScopedBstr
ScopedSafearray
EnumVariant
UiaRegistrarWin
VariantUtil
VariantVector
atl.h
atl_module.h
base_export.h
上级 2ab18532
// Copyright 2019 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 "ui/accessibility/ax_active_popup.h"
namespace ui {
// Represents a global storage for the view accessibility for an
// autofill popup. It is a singleton wrapper around the ax unique id of the
// autofill popup. This singleton is used for communicating the live status of
// the autofill popup between web contents and views.
// The assumption here is that only one autofill popup can exist at a time.
static base::NoDestructor<base::Optional<int32_t>> g_active_popup_ax_unique_id;
base::Optional<int32_t> GetActivePopupAxUniqueId() {
return *g_active_popup_ax_unique_id;
}
void SetActivePopupAxUniqueId(base::Optional<int32_t> ax_unique_id) {
// When an instance of autofill popup hides, the caller of popup hide should
// make sure g_active_popup_ax_unique_id is cleared. The assumption is that
// there can only be one active autofill popup existing at a time. If on
// popup showing, we encounter g_active_popup_ax_unique_id is already set,
// this would indicate two autofill popups are showing at the same time or
// previous on popup hide call did not clear the variable, so we should fail
// DCHECK here.
DCHECK(!GetActivePopupAxUniqueId());
*g_active_popup_ax_unique_id = ax_unique_id;
}
void ClearActivePopupAxUniqueId() {
*g_active_popup_ax_unique_id = base::nullopt;
}
} // namespace ui
// Copyright 2019 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 UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
#define UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/optional.h"
#include "ui/accessibility/ax_export.h"
namespace ui {
AX_EXPORT base::Optional<int32_t> GetActivePopupAxUniqueId();
AX_EXPORT void SetActivePopupAxUniqueId(base::Optional<int32_t> ax_unique_id);
AX_EXPORT void ClearActivePopupAxUniqueId();
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
// Copyright 2019 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 UI_ACCESSIBILITY_PLATFORM_AX_FRAGMENT_ROOT_DELEGATE_WIN_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_FRAGMENT_ROOT_DELEGATE_WIN_H_
#include "ui/gfx/native_widget_types.h"
namespace ui {
// Delegate interface for clients of AXFragmentRootWin. This allows the client
// to relate the fragment root to its neighbors in a loosely coupled way.
class AXFragmentRootDelegateWin {
public:
// In our design, a fragment root can have at most one child.
// See AXFragmentRootWin for more details.
virtual gfx::NativeViewAccessible GetChildOfAXFragmentRoot() = 0;
// Optionally returns a parent node for the fragment root. This is used, for
// example, to place the web content fragment at the correct spot in the
// browser UI's accessibility tree.
// If a fragment root returns no parent, the OS will use HWND parent-child
// relationships to establish the fragment root's location in the tree.
virtual gfx::NativeViewAccessible GetParentOfAXFragmentRoot() = 0;
// Return true if the window should be treated as an accessible control or
// false if the window should be considered a structural detail that should
// not be exposed to assistive technology users. See AXFragmentRootWin for
// more details.
virtual bool IsAXFragmentRootAControlElement() = 0;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_AX_FRAGMENT_ROOT_DELEGATE_WIN_H_
// Copyright 2019 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 "ui/accessibility/platform/ax_fragment_root_win.h"
#include <unordered_map>
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "ui/accessibility/platform/ax_fragment_root_delegate_win.h"
#include "ui/accessibility/platform/ax_platform_node_win.h"
#include "ui/accessibility/platform/uia_registrar_win.h"
#include "ui/base/win/atl_module.h"
namespace ui {
class AXFragmentRootPlatformNodeWin : public AXPlatformNodeWin,
public IItemContainerProvider,
public IRawElementProviderFragmentRoot,
public IRawElementProviderAdviseEvents {
public:
BEGIN_COM_MAP(AXFragmentRootPlatformNodeWin)
COM_INTERFACE_ENTRY(IItemContainerProvider)
COM_INTERFACE_ENTRY(IRawElementProviderFragmentRoot)
COM_INTERFACE_ENTRY(IRawElementProviderAdviseEvents)
COM_INTERFACE_ENTRY_CHAIN(AXPlatformNodeWin)
END_COM_MAP()
static AXFragmentRootPlatformNodeWin* Create(
AXPlatformNodeDelegate* delegate) {
// Make sure ATL is initialized in this module.
win::CreateATLModuleIfNeeded();
CComObject<AXFragmentRootPlatformNodeWin>* instance = nullptr;
HRESULT hr =
CComObject<AXFragmentRootPlatformNodeWin>::CreateInstance(&instance);
DCHECK(SUCCEEDED(hr));
instance->Init(delegate);
instance->AddRef();
return instance;
}
//
// IItemContainerProvider methods.
//
IFACEMETHODIMP FindItemByProperty(
IRawElementProviderSimple* start_after_element,
PROPERTYID property_id,
VARIANT value,
IRawElementProviderSimple** result) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ITEMCONTAINER_FINDITEMBYPROPERTY);
UIA_VALIDATE_CALL_1_ARG(result);
*result = nullptr;
// We currently only support the custom UIA property ID for unique id.
if (property_id ==
UiaRegistrarWin::GetInstance().GetUiaUniqueIdPropertyId() &&
value.vt == VT_BSTR) {
int32_t ax_unique_id;
if (!base::StringToInt(value.bstrVal, &ax_unique_id))
return S_OK;
// In the Windows accessibility platform implementation, id 0 represents
// self; a positive id represents the immediate descendants; and a
// negative id represents a unique id that can be mapped to any node.
if (AXPlatformNodeWin* result_platform_node =
static_cast<AXPlatformNodeWin*>(GetFromUniqueId(-ax_unique_id))) {
if (start_after_element) {
Microsoft::WRL::ComPtr<AXPlatformNodeWin> start_after_platform_node;
if (!SUCCEEDED(start_after_element->QueryInterface(
IID_PPV_ARGS(&start_after_platform_node))))
return E_INVALIDARG;
// We want |result| to be nullptr if it comes before or is equal to
// |start_after_element|.
if (start_after_platform_node->CompareTo(*result_platform_node) >= 0)
return S_OK;
}
return result_platform_node->QueryInterface(IID_PPV_ARGS(result));
}
}
return E_INVALIDARG;
}
//
// IRawElementProviderSimple methods.
//
IFACEMETHODIMP get_HostRawElementProvider(
IRawElementProviderSimple** host_element_provider) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_HOST_RAW_ELEMENT_PROVIDER);
UIA_VALIDATE_CALL_1_ARG(host_element_provider);
HWND hwnd = GetDelegate()->GetTargetForNativeAccessibilityEvent();
return UiaHostProviderFromHwnd(hwnd, host_element_provider);
}
IFACEMETHODIMP GetPatternProvider(PATTERNID pattern_id,
IUnknown** result) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PATTERN_PROVIDER);
UIA_VALIDATE_CALL_1_ARG(result);
*result = nullptr;
if (pattern_id == UIA_ItemContainerPatternId) {
AddRef();
*result = static_cast<IItemContainerProvider*>(this);
return S_OK;
}
return AXPlatformNodeWin::GetPatternProviderImpl(pattern_id, result);
}
IFACEMETHODIMP GetPropertyValue(PROPERTYID property_id,
VARIANT* result) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_PROPERTY_VALUE);
UIA_VALIDATE_CALL_1_ARG(result);
switch (property_id) {
default:
// UIA has a built-in provider that will expose values for several
// properties based on the HWND. This information is useful to someone
// examining the accessibility tree using tools such as Inspect. Return
// VT_EMPTY for most properties so that we don't override values from
// the default provider with blank data.
result->vt = VT_EMPTY;
break;
case UIA_IsControlElementPropertyId:
case UIA_IsContentElementPropertyId:
// Override IsControlElement and IsContentElement to fine tune which
// fragment roots appear in the control and content views.
result->vt = VT_BOOL;
result->boolVal =
static_cast<AXFragmentRootWin*>(GetDelegate())->IsControlElement()
? VARIANT_TRUE
: VARIANT_FALSE;
break;
}
return S_OK;
}
//
// IRawElementProviderFragment methods.
//
IFACEMETHODIMP get_FragmentRoot(
IRawElementProviderFragmentRoot** fragment_root) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_FRAGMENTROOT);
UIA_VALIDATE_CALL_1_ARG(fragment_root);
QueryInterface(IID_PPV_ARGS(fragment_root));
return S_OK;
}
//
// IRawElementProviderFragmentRoot methods.
//
IFACEMETHODIMP ElementProviderFromPoint(
double screen_physical_pixel_x,
double screen_physical_pixel_y,
IRawElementProviderFragment** element_provider) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ELEMENT_PROVIDER_FROM_POINT);
WIN_ACCESSIBILITY_API_PERF_HISTOGRAM(UMA_API_ELEMENT_PROVIDER_FROM_POINT);
UIA_VALIDATE_CALL_1_ARG(element_provider);
*element_provider = nullptr;
gfx::NativeViewAccessible hit_element = nullptr;
// Descend the tree until we get a non-hit or can't go any further.
AXPlatformNode* node_to_test = this;
do {
gfx::NativeViewAccessible test_result =
node_to_test->GetDelegate()->HitTestSync(screen_physical_pixel_x,
screen_physical_pixel_y);
if (test_result != nullptr && test_result != hit_element) {
hit_element = test_result;
node_to_test = AXPlatformNode::FromNativeViewAccessible(test_result);
} else {
node_to_test = nullptr;
}
} while (node_to_test);
if (hit_element)
hit_element->QueryInterface(element_provider);
return S_OK;
}
IFACEMETHODIMP GetFocus(IRawElementProviderFragment** focus) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_FOCUS);
UIA_VALIDATE_CALL_1_ARG(focus);
*focus = nullptr;
gfx::NativeViewAccessible focused_element = nullptr;
// GetFocus() can return a node at the root of a subtree, for example when
// transitioning from Views into web content. In such cases we want to
// continue drilling to retrieve the actual focused element.
AXPlatformNode* node_to_test = this;
do {
gfx::NativeViewAccessible test_result =
node_to_test->GetDelegate()->GetFocus();
if (test_result != nullptr && test_result != focused_element) {
focused_element = test_result;
node_to_test =
AXPlatformNode::FromNativeViewAccessible(focused_element);
} else {
node_to_test = nullptr;
}
} while (node_to_test);
if (focused_element)
focused_element->QueryInterface(IID_PPV_ARGS(focus));
return S_OK;
}
//
// IRawElementProviderAdviseEvents methods.
//
IFACEMETHODIMP AdviseEventAdded(EVENTID event_id,
SAFEARRAY* property_ids) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ADVISE_EVENT_ADDED);
if (event_id == UIA_LiveRegionChangedEventId) {
live_region_change_listeners_++;
if (live_region_change_listeners_ == 1) {
// Fire a LiveRegionChangedEvent for each live-region to tell the
// newly-attached assistive technology about the regions.
//
// Ideally we'd be able to direct these events to only the
// newly-attached AT, but we don't have that capability, so we only
// fire events when the *first* AT attaches. (A common scenario will
// be an attached screen-reader, then a software-keyboard attaches to
// handle an input field; we don't want the screen-reader to announce
// that every live-region has changed.) There isn't a perfect solution,
// but this heuristic seems to work well in practice.
FireLiveRegionChangeRecursive();
}
}
return S_OK;
}
IFACEMETHODIMP AdviseEventRemoved(EVENTID event_id,
SAFEARRAY* property_ids) override {
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_ADVISE_EVENT_REMOVED);
if (event_id == UIA_LiveRegionChangedEventId) {
DCHECK(live_region_change_listeners_ > 0);
live_region_change_listeners_--;
}
return S_OK;
}
private:
int32_t live_region_change_listeners_ = 0;
};
class AXFragmentRootMapWin {
public:
static AXFragmentRootMapWin& GetInstance() {
static base::NoDestructor<AXFragmentRootMapWin> instance;
return *instance;
}
void AddFragmentRoot(gfx::AcceleratedWidget widget,
AXFragmentRootWin* fragment_root) {
map_[widget] = fragment_root;
}
void RemoveFragmentRoot(gfx::AcceleratedWidget widget) { map_.erase(widget); }
ui::AXFragmentRootWin* GetFragmentRoot(gfx::AcceleratedWidget widget) const {
const auto& entry = map_.find(widget);
if (entry != map_.end())
return entry->second;
return nullptr;
}
ui::AXFragmentRootWin* GetFragmentRootParentOf(
gfx::NativeViewAccessible accessible) const {
for (const auto& entry : map_) {
AXPlatformNodeDelegate* child = entry.second->GetChildNodeDelegate();
if (child && (child->GetNativeViewAccessible() == accessible))
return entry.second;
}
return nullptr;
}
private:
std::unordered_map<gfx::AcceleratedWidget, AXFragmentRootWin*> map_;
};
AXFragmentRootWin::AXFragmentRootWin(gfx::AcceleratedWidget widget,
AXFragmentRootDelegateWin* delegate)
: widget_(widget), delegate_(delegate) {
platform_node_ = ui::AXFragmentRootPlatformNodeWin::Create(this);
AXFragmentRootMapWin::GetInstance().AddFragmentRoot(widget, this);
}
AXFragmentRootWin::~AXFragmentRootWin() {
AXFragmentRootMapWin::GetInstance().RemoveFragmentRoot(widget_);
platform_node_->Destroy();
platform_node_ = nullptr;
}
AXFragmentRootWin* AXFragmentRootWin::GetForAcceleratedWidget(
gfx::AcceleratedWidget widget) {
return AXFragmentRootMapWin::GetInstance().GetFragmentRoot(widget);
}
// static
AXFragmentRootWin* AXFragmentRootWin::GetFragmentRootParentOf(
gfx::NativeViewAccessible accessible) {
return AXFragmentRootMapWin::GetInstance().GetFragmentRootParentOf(
accessible);
}
gfx::NativeViewAccessible AXFragmentRootWin::GetNativeViewAccessible() {
// The fragment root is the entry point from the operating system for UI
// Automation. Signal observers when we're asked for a platform object on it.
for (WinAccessibilityAPIUsageObserver& observer :
GetWinAccessibilityAPIUsageObserverList()) {
observer.OnUIAutomationUsed();
}
return platform_node_.Get();
}
bool AXFragmentRootWin::IsControlElement() {
return delegate_->IsAXFragmentRootAControlElement();
}
gfx::NativeViewAccessible AXFragmentRootWin::GetParent() {
return delegate_->GetParentOfAXFragmentRoot();
}
int AXFragmentRootWin::GetChildCount() const {
return delegate_->GetChildOfAXFragmentRoot() ? 1 : 0;
}
gfx::NativeViewAccessible AXFragmentRootWin::ChildAtIndex(int index) {
if (index == 0) {
return delegate_->GetChildOfAXFragmentRoot();
}
return nullptr;
}
gfx::NativeViewAccessible AXFragmentRootWin::GetNextSibling() {
int child_index = GetIndexInParentOfChild();
if (child_index >= 0) {
AXPlatformNodeDelegate* parent = GetParentNodeDelegate();
if (parent && child_index < (parent->GetChildCount() - 1))
return GetParentNodeDelegate()->ChildAtIndex(child_index + 1);
}
return nullptr;
}
gfx::NativeViewAccessible AXFragmentRootWin::GetPreviousSibling() {
int child_index = GetIndexInParentOfChild();
if (child_index > 0)
return GetParentNodeDelegate()->ChildAtIndex(child_index - 1);
return nullptr;
}
gfx::NativeViewAccessible AXFragmentRootWin::HitTestSync(int x, int y) const {
AXPlatformNodeDelegate* child_delegate = GetChildNodeDelegate();
if (child_delegate)
return child_delegate->HitTestSync(x, y);
return nullptr;
}
gfx::NativeViewAccessible AXFragmentRootWin::GetFocus() {
AXPlatformNodeDelegate* child_delegate = GetChildNodeDelegate();
if (child_delegate)
return child_delegate->GetFocus();
return nullptr;
}
const ui::AXUniqueId& AXFragmentRootWin::GetUniqueId() const {
return unique_id_;
}
gfx::AcceleratedWidget
AXFragmentRootWin::GetTargetForNativeAccessibilityEvent() {
return widget_;
}
AXPlatformNode* AXFragmentRootWin::GetFromTreeIDAndNodeID(
const ui::AXTreeID& ax_tree_id,
int32_t node_id) {
AXPlatformNodeDelegate* child_delegate = GetChildNodeDelegate();
if (child_delegate)
return child_delegate->GetFromTreeIDAndNodeID(ax_tree_id, node_id);
return nullptr;
}
AXPlatformNodeDelegate* AXFragmentRootWin::GetParentNodeDelegate() const {
gfx::NativeViewAccessible parent = delegate_->GetParentOfAXFragmentRoot();
if (parent)
return ui::AXPlatformNode::FromNativeViewAccessible(parent)->GetDelegate();
return nullptr;
}
AXPlatformNodeDelegate* AXFragmentRootWin::GetChildNodeDelegate() const {
gfx::NativeViewAccessible child = delegate_->GetChildOfAXFragmentRoot();
if (child)
return ui::AXPlatformNode::FromNativeViewAccessible(child)->GetDelegate();
return nullptr;
}
int AXFragmentRootWin::GetIndexInParentOfChild() const {
AXPlatformNodeDelegate* parent = GetParentNodeDelegate();
if (!parent)
return 0;
AXPlatformNodeDelegate* child = GetChildNodeDelegate();
if (child) {
int child_count = parent->GetChildCount();
for (int child_index = 0; child_index < child_count; child_index++) {
if (ui::AXPlatformNode::FromNativeViewAccessible(
parent->ChildAtIndex(child_index))
->GetDelegate() == child)
return child_index;
}
}
return 0;
}
} // namespace ui
// Copyright 2019 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 UI_ACCESSIBILITY_PLATFORM_AX_FRAGMENT_ROOT_WIN_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_FRAGMENT_ROOT_WIN_H_
#include "ui/accessibility/platform/ax_platform_node_delegate_base.h"
#include <wrl/client.h>
namespace ui {
class AXFragmentRootDelegateWin;
class AXFragmentRootPlatformNodeWin;
// UI Automation on Windows requires the root of a multi-element provider to
// implement IRawElementProviderFragmentRoot. Our internal accessibility trees
// may not know their roots for right away; for example, web content may
// deserialize the document for an iframe before the host document. Because of
// this, and because COM rules require that the list of interfaces returned by
// QueryInterface remain static over the lifetime of an object instance, we
// implement IRawElementProviderFragmentRoot on its own node for each HWND, with
// the root of our internal accessibility tree for that HWND as its sole child.
//
// Since UIA derives some information from the underlying HWND hierarchy, we
// expose one fragment root per HWND. The class that owns the HWND is expected
// to own the corresponding AXFragmentRootWin.
class AX_EXPORT AXFragmentRootWin : public ui::AXPlatformNodeDelegateBase {
public:
AXFragmentRootWin(gfx::AcceleratedWidget widget,
AXFragmentRootDelegateWin* delegate);
~AXFragmentRootWin() override;
// Fragment roots register themselves in a map upon creation and unregister
// upon destruction. This method provides a lookup, which allows the internal
// accessibility root to navigate back to the corresponding fragment root.
static AXFragmentRootWin* GetForAcceleratedWidget(
gfx::AcceleratedWidget widget);
// If the given NativeViewAccessible is the direct descendant of a fragment
// root, return the corresponding fragment root.
static AXFragmentRootWin* GetFragmentRootParentOf(
gfx::NativeViewAccessible accessible);
// Returns the NativeViewAccessible for this fragment root.
gfx::NativeViewAccessible GetNativeViewAccessible() override;
// Assistive technologies will typically use UI Automation's control or
// content view rather than the raw view.
// Returns true if the fragment root should be included in the control and
// content views or false if it should be excluded.
bool IsControlElement();
// If a child node is available, return its delegate.
AXPlatformNodeDelegate* GetChildNodeDelegate() const;
private:
// AXPlatformNodeDelegate overrides.
gfx::NativeViewAccessible GetParent() override;
int GetChildCount() const override;
gfx::NativeViewAccessible ChildAtIndex(int index) override;
gfx::NativeViewAccessible GetNextSibling() override;
gfx::NativeViewAccessible GetPreviousSibling() override;
gfx::NativeViewAccessible HitTestSync(int x, int y) const override;
gfx::NativeViewAccessible GetFocus() override;
const ui::AXUniqueId& GetUniqueId() const override;
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
AXPlatformNode* GetFromTreeIDAndNodeID(const ui::AXTreeID& ax_tree_id,
int32_t id) override;
// A fragment root does not correspond to any node in the platform neutral
// accessibility tree. Rather, the fragment root's child is a child of the
// fragment root's parent. This helper computes the child's index in the
// parent's array of children.
int GetIndexInParentOfChild() const;
// If a parent node is available, return its delegate.
AXPlatformNodeDelegate* GetParentNodeDelegate() const;
gfx::AcceleratedWidget widget_;
AXFragmentRootDelegateWin* const delegate_;
Microsoft::WRL::ComPtr<ui::AXFragmentRootPlatformNodeWin> platform_node_;
ui::AXUniqueId unique_id_;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_AX_FRAGMENT_ROOT_WIN_H_
// Copyright 2019 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 "ui/accessibility/platform/ax_platform_node_delegate_utils_win.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
namespace ui {
bool IsValuePatternSupported(AXPlatformNodeDelegate* delegate) {
// https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-implementingvalue
// The Value control pattern is used to support controls that have an
// intrinsic value not spanning a range and that can be represented as
// a string.
//
// IValueProvider must be implemented by controls such as the color picker
// selection control [...]
// https://www.w3.org/TR/html-aam-1.0/
// The HTML AAM maps "href [a; area]" to UIA Value.Value
return delegate->GetData().IsRangeValueSupported() ||
IsReadOnlySupported(delegate->GetData().role) ||
IsLink(delegate->GetData().role) ||
delegate->GetData().role == ax::mojom::Role::kColorWell ||
delegate->IsCellOrHeaderOfARIAGrid();
}
} // namespace ui
// Copyright 2019 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 UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_UTILS_WIN_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_UTILS_WIN_H_
#include "ui/accessibility/ax_export.h"
namespace ui {
class AXPlatformNodeDelegate;
// Returns true if the value pattern is supported
AX_EXPORT bool IsValuePatternSupported(AXPlatformNodeDelegate*);
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_UTILS_WIN_H_
// Copyright 2019 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 UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_WIN_UNITTEST_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_WIN_UNITTEST_H_
#include "ui/accessibility/platform/ax_platform_node_unittest.h"
#include <memory>
#include <unordered_set>
#include "base/test/scoped_feature_list.h"
#include "ui/accessibility/platform/ax_fragment_root_delegate_win.h"
#include "ui/base/win/accessibility_misc_utils.h"
struct IAccessible;
struct IAccessible2;
struct IAccessible2_2;
struct IAccessibleTableCell;
struct IRawElementProviderFragment;
struct IRawElementProviderFragmentRoot;
struct IRawElementProviderSimple;
struct IUnknown;
namespace base {
namespace win {
class ScopedVariant;
} // namespace win
} // namespace base
namespace ui {
class AXFragmentRootWin;
class AXPlatformNode;
class TestFragmentRootDelegate : public AXFragmentRootDelegateWin {
public:
TestFragmentRootDelegate();
virtual ~TestFragmentRootDelegate();
gfx::NativeViewAccessible GetChildOfAXFragmentRoot() override;
gfx::NativeViewAccessible GetParentOfAXFragmentRoot() override;
bool IsAXFragmentRootAControlElement() override;
gfx::NativeViewAccessible child_ = nullptr;
gfx::NativeViewAccessible parent_ = nullptr;
bool is_control_element_ = true;
};
class MockIRawElementProviderSimple
: public CComObjectRootEx<CComMultiThreadModel>,
public IRawElementProviderSimple {
public:
BEGIN_COM_MAP(MockIRawElementProviderSimple)
COM_INTERFACE_ENTRY(IRawElementProviderSimple)
END_COM_MAP()
MockIRawElementProviderSimple();
~MockIRawElementProviderSimple();
static HRESULT CreateMockIRawElementProviderSimple(
IRawElementProviderSimple** provider);
//
// IRawElementProviderSimple methods.
//
IFACEMETHODIMP GetPatternProvider(PATTERNID pattern_id,
IUnknown** result) override;
IFACEMETHODIMP GetPropertyValue(PROPERTYID property_id,
VARIANT* result) override;
IFACEMETHODIMP
get_ProviderOptions(enum ProviderOptions* ret) override;
IFACEMETHODIMP
get_HostRawElementProvider(IRawElementProviderSimple** provider) override;
};
class AXPlatformNodeWinTest : public AXPlatformNodeTest {
public:
AXPlatformNodeWinTest();
~AXPlatformNodeWinTest() override;
void SetUp() override;
void TearDown() override;
protected:
static const base::string16 kEmbeddedCharacterAsString;
AXPlatformNode* AXPlatformNodeFromNode(AXNode* node);
template <typename T>
Microsoft::WRL::ComPtr<T> QueryInterfaceFromNodeId(AXNode::AXID id);
template <typename T>
Microsoft::WRL::ComPtr<T> QueryInterfaceFromNode(AXNode* node);
Microsoft::WRL::ComPtr<IRawElementProviderSimple>
GetRootIRawElementProviderSimple();
Microsoft::WRL::ComPtr<IRawElementProviderSimple>
GetIRawElementProviderSimpleFromChildIndex(int child_index);
Microsoft::WRL::ComPtr<IRawElementProviderSimple>
GetIRawElementProviderSimpleFromTree(const ui::AXTreeID tree_id,
const AXNode::AXID node_id);
Microsoft::WRL::ComPtr<IRawElementProviderFragment>
GetRootIRawElementProviderFragment();
Microsoft::WRL::ComPtr<IRawElementProviderFragment>
IRawElementProviderFragmentFromNode(AXNode* node);
Microsoft::WRL::ComPtr<IAccessible> IAccessibleFromNode(AXNode* node);
Microsoft::WRL::ComPtr<IAccessible> GetRootIAccessible();
Microsoft::WRL::ComPtr<IAccessible2> ToIAccessible2(
Microsoft::WRL::ComPtr<IUnknown> unknown);
Microsoft::WRL::ComPtr<IAccessible2> ToIAccessible2(
Microsoft::WRL::ComPtr<IAccessible> accessible);
Microsoft::WRL::ComPtr<IAccessible2_2> ToIAccessible2_2(
Microsoft::WRL::ComPtr<IAccessible> accessible);
void CheckVariantHasName(const base::win::ScopedVariant& variant,
const wchar_t* expected_name);
void CheckIUnknownHasName(Microsoft::WRL::ComPtr<IUnknown> unknown,
const wchar_t* expected_name);
Microsoft::WRL::ComPtr<IAccessibleTableCell> GetCellInTable();
void InitFragmentRoot();
AXFragmentRootWin* InitNodeAsFragmentRoot(AXNode* node,
TestFragmentRootDelegate* delegate);
Microsoft::WRL::ComPtr<IRawElementProviderFragmentRoot> GetFragmentRoot();
using PatternSet = std::unordered_set<LONG>;
PatternSet GetSupportedPatternsFromNodeId(AXNode::AXID id);
std::unique_ptr<AXFragmentRootWin> ax_fragment_root_;
std::unique_ptr<TestFragmentRootDelegate> test_fragment_root_delegate_;
base::test::ScopedFeatureList scoped_feature_list_;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_WIN_UNITTEST_H_
// Copyright 2017 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 "ui/accessibility/platform/ax_platform_relation_win.h"
#include <wrl/client.h>
#include <algorithm>
#include <vector>
#include "base/lazy_instance.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/enum_variant.h"
#include "base/win/scoped_variant.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_mode_observer.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/ax_text_utils.h"
#include "ui/accessibility/ax_tree_data.h"
#include "ui/accessibility/platform/ax_platform_node_base.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
#include "ui/accessibility/platform/ax_unique_id.h"
#include "ui/base/win/atl_module.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace ui {
AXPlatformRelationWin::AXPlatformRelationWin() {
win::CreateATLModuleIfNeeded();
}
AXPlatformRelationWin::~AXPlatformRelationWin() {}
base::string16 GetIA2RelationFromIntAttr(ax::mojom::IntAttribute attribute) {
switch (attribute) {
case ax::mojom::IntAttribute::kMemberOfId:
return IA2_RELATION_MEMBER_OF;
case ax::mojom::IntAttribute::kErrormessageId:
return IA2_RELATION_ERROR;
case ax::mojom::IntAttribute::kPopupForId:
// Map "popup for" to "controlled by".
// Unlike ATK there is no special IA2 popup-for relationship, but it can
// be exposed via the controlled by relation, which is also computed for
// content as the reverse of the controls relationship.
return IA2_RELATION_CONTROLLED_BY;
default:
break;
}
return base::string16();
}
base::string16 GetIA2RelationFromIntListAttr(
ax::mojom::IntListAttribute attribute) {
switch (attribute) {
case ax::mojom::IntListAttribute::kControlsIds:
return IA2_RELATION_CONTROLLER_FOR;
case ax::mojom::IntListAttribute::kDescribedbyIds:
return IA2_RELATION_DESCRIBED_BY;
case ax::mojom::IntListAttribute::kDetailsIds:
return IA2_RELATION_DETAILS;
case ax::mojom::IntListAttribute::kFlowtoIds:
return IA2_RELATION_FLOWS_TO;
case ax::mojom::IntListAttribute::kLabelledbyIds:
return IA2_RELATION_LABELLED_BY;
default:
break;
}
return base::string16();
}
base::string16 GetIA2ReverseRelationFromIntAttr(
ax::mojom::IntAttribute attribute) {
switch (attribute) {
case ax::mojom::IntAttribute::kErrormessageId:
return IA2_RELATION_ERROR_FOR;
default:
break;
}
return base::string16();
}
base::string16 GetIA2ReverseRelationFromIntListAttr(
ax::mojom::IntListAttribute attribute) {
switch (attribute) {
case ax::mojom::IntListAttribute::kControlsIds:
return IA2_RELATION_CONTROLLED_BY;
case ax::mojom::IntListAttribute::kDescribedbyIds:
return IA2_RELATION_DESCRIPTION_FOR;
case ax::mojom::IntListAttribute::kDetailsIds:
return IA2_RELATION_DETAILS_FOR;
case ax::mojom::IntListAttribute::kFlowtoIds:
return IA2_RELATION_FLOWS_FROM;
case ax::mojom::IntListAttribute::kLabelledbyIds:
return IA2_RELATION_LABEL_FOR;
default:
break;
}
return base::string16();
}
// static
int AXPlatformRelationWin::EnumerateRelationships(
AXPlatformNodeBase* node,
int desired_index,
const base::string16& desired_ia2_relation,
base::string16* out_ia2_relation,
std::set<AXPlatformNode*>* out_targets) {
const AXNodeData& node_data = node->GetData();
AXPlatformNodeDelegate* delegate = node->GetDelegate();
// The first time this is called, populate vectors with all of the
// int attributes and intlist attributes that have reverse relations
// we care about on Windows. Computing these by calling
// GetIA2ReverseRelationFrom{Int|IntList}Attr on every possible attribute
// simplifies the work needed to support an additional relation
// attribute in the future.
static std::vector<ax::mojom::IntAttribute>
int_attributes_with_reverse_relations;
static std::vector<ax::mojom::IntListAttribute>
intlist_attributes_with_reverse_relations;
static bool first_time = true;
if (first_time) {
for (int32_t attr_index =
static_cast<int32_t>(ax::mojom::IntAttribute::kNone);
attr_index <= static_cast<int32_t>(ax::mojom::IntAttribute::kMaxValue);
++attr_index) {
auto attr = static_cast<ax::mojom::IntAttribute>(attr_index);
if (!GetIA2ReverseRelationFromIntAttr(attr).empty())
int_attributes_with_reverse_relations.push_back(attr);
}
for (int32_t attr_index =
static_cast<int32_t>(ax::mojom::IntListAttribute::kNone);
attr_index <=
static_cast<int32_t>(ax::mojom::IntListAttribute::kMaxValue);
++attr_index) {
auto attr = static_cast<ax::mojom::IntListAttribute>(attr_index);
if (!GetIA2ReverseRelationFromIntListAttr(attr).empty())
intlist_attributes_with_reverse_relations.push_back(attr);
}
first_time = false;
}
// Enumerate all of the relations and reverse relations that
// are exposed via IAccessible2 on Windows. We do this with a series
// of loops. Every time we encounter one, we check if the caller
// requested that particular relation by index, and return it.
// Otherwise we build up and return the total number of relations found.
int total_count = 0;
// Iterate over all int attributes on this node to check the ones
// that correspond to IAccessible2 relations.
for (size_t i = 0; i < node_data.int_attributes.size(); ++i) {
ax::mojom::IntAttribute int_attribute = node_data.int_attributes[i].first;
base::string16 relation = GetIA2RelationFromIntAttr(int_attribute);
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
// Skip reflexive relations
if (node_data.int_attributes[i].second == node_data.id)
continue;
if (desired_index == total_count) {
*out_ia2_relation = relation;
out_targets->insert(
delegate->GetFromNodeID(node_data.int_attributes[i].second));
return 1;
}
total_count++;
}
}
// Iterate over all of the int attributes that have reverse relations
// in IAccessible2, and query AXTree to see if the reverse relation exists.
for (ax::mojom::IntAttribute int_attribute :
int_attributes_with_reverse_relations) {
base::string16 relation = GetIA2ReverseRelationFromIntAttr(int_attribute);
std::set<AXPlatformNode*> targets =
delegate->GetReverseRelations(int_attribute);
// Erase reflexive relations.
targets.erase(node);
if (targets.size()) {
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
if (desired_index == total_count) {
*out_ia2_relation = relation;
*out_targets = targets;
return 1;
}
total_count++;
}
}
}
// Iterate over all intlist attributes on this node to check the ones
// that correspond to IAccessible2 relations.
for (size_t i = 0; i < node_data.intlist_attributes.size(); ++i) {
ax::mojom::IntListAttribute intlist_attribute =
node_data.intlist_attributes[i].first;
base::string16 relation = GetIA2RelationFromIntListAttr(intlist_attribute);
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
if (desired_index == total_count) {
*out_ia2_relation = relation;
for (int32_t target_id : node_data.intlist_attributes[i].second) {
// Skip reflexive relations
if (target_id == node_data.id)
continue;
out_targets->insert(delegate->GetFromNodeID(target_id));
}
if (out_targets->size() == 0)
continue;
return 1;
}
total_count++;
}
}
// Iterate over all of the intlist attributes that have reverse relations
// in IAccessible2, and query AXTree to see if the reverse relation exists.
for (ax::mojom::IntListAttribute intlist_attribute :
intlist_attributes_with_reverse_relations) {
base::string16 relation =
GetIA2ReverseRelationFromIntListAttr(intlist_attribute);
std::set<AXPlatformNode*> targets =
delegate->GetReverseRelations(intlist_attribute);
// Erase reflexive relations.
targets.erase(node);
if (targets.size()) {
if (!relation.empty() &&
(desired_ia2_relation.empty() || desired_ia2_relation == relation)) {
if (desired_index == total_count) {
*out_ia2_relation = relation;
*out_targets = targets;
return 1;
}
total_count++;
}
}
}
return total_count;
}
void AXPlatformRelationWin::Initialize(const base::string16& type) {
type_ = type;
}
void AXPlatformRelationWin::Invalidate() {
targets_.clear();
}
void AXPlatformRelationWin::AddTarget(AXPlatformNodeWin* target) {
targets_.push_back(target);
}
IFACEMETHODIMP AXPlatformRelationWin::get_relationType(BSTR* relation_type) {
if (!relation_type)
return E_INVALIDARG;
*relation_type = SysAllocString(type_.c_str());
DCHECK(*relation_type);
return S_OK;
}
IFACEMETHODIMP AXPlatformRelationWin::get_nTargets(LONG* n_targets) {
if (!n_targets)
return E_INVALIDARG;
*n_targets = static_cast<LONG>(targets_.size());
return S_OK;
}
IFACEMETHODIMP AXPlatformRelationWin::get_target(LONG target_index,
IUnknown** target) {
if (!target)
return E_INVALIDARG;
if (target_index < 0 || target_index >= static_cast<LONG>(targets_.size())) {
return E_INVALIDARG;
}
*target = static_cast<IAccessible*>(targets_[target_index].Get());
(*target)->AddRef();
return S_OK;
}
IFACEMETHODIMP AXPlatformRelationWin::get_targets(LONG max_targets,
IUnknown** targets,
LONG* n_targets) {
if (!targets || !n_targets)
return E_INVALIDARG;
LONG count = static_cast<LONG>(targets_.size());
if (count > max_targets)
count = max_targets;
*n_targets = count;
if (count == 0)
return S_FALSE;
for (LONG i = 0; i < count; ++i) {
HRESULT result = get_target(i, &targets[i]);
if (result != S_OK)
return result;
}
return S_OK;
}
IFACEMETHODIMP
AXPlatformRelationWin::get_localizedRelationType(BSTR* relation_type) {
return E_NOTIMPL;
}
} // namespace ui
// Copyright 2017 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 UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_RELATION_WIN_H_
#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_RELATION_WIN_H_
#include <oleacc.h>
#include <wrl/client.h>
#include <set>
#include <vector>
#include "base/compiler_specific.h"
#include "base/metrics/histogram_macros.h"
#include "base/observer_list.h"
#include "base/win/atl.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_text_utils.h"
#include "ui/accessibility/platform/ax_platform_node_win.h"
namespace ui {
//
// AXPlatformRelationWin
//
// A simple implementation of IAccessibleRelation, used to represent a
// relationship between one accessible node in the tree and
// potentially multiple target nodes. Also contains a utility function
// to compute all of the possible IAccessible2 relations and reverse
// relations given the internal relation id attributes.
class AXPlatformRelationWin : public CComObjectRootEx<CComMultiThreadModel>,
public IAccessibleRelation {
public:
BEGIN_COM_MAP(AXPlatformRelationWin)
COM_INTERFACE_ENTRY(IAccessibleRelation)
END_COM_MAP()
AXPlatformRelationWin();
virtual ~AXPlatformRelationWin();
// This is the main utility function that enumerates all of the possible
// IAccessible2 relations between one node and any other node in the tree.
// Forward relations come from the int_attributes and intlist_attributes
// in node_data. Reverse relations come from querying |delegate| for the
// reverse relations given |node_data.id|.
//
// If you pass -1 for |desired_index| and "" for |desired_ia2_relation|,
// it will return a count of all relations.
//
// If you pass either an index in |desired_index| or a specific relation
// in |desired_ia2_relation|, the first matching relation will be returned in
// |out_ia2_relation| and |out_targets| (both of which must not be null),
// and it will return 1 on success, and 0 if none were found matching that
// criteria.
static int EnumerateRelationships(AXPlatformNodeBase* node,
int desired_index,
const base::string16& desired_ia2_relation,
base::string16* out_ia2_relation,
std::set<AXPlatformNode*>* out_targets);
void Initialize(const base::string16& type);
void Invalidate();
void AddTarget(AXPlatformNodeWin* target);
// IAccessibleRelation methods.
IFACEMETHODIMP get_relationType(BSTR* relation_type) override;
IFACEMETHODIMP get_nTargets(LONG* n_targets) override;
IFACEMETHODIMP get_target(LONG target_index, IUnknown** target) override;
IFACEMETHODIMP get_targets(LONG max_targets,
IUnknown** targets,
LONG* n_targets) override;
IFACEMETHODIMP get_localizedRelationType(BSTR* relation_type) override;
private:
base::string16 type_;
std::vector<Microsoft::WRL::ComPtr<AXPlatformNodeWin>> targets_;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_RELATION_WIN_H_
// Copyright 2020 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 "uia_registrar_win.h"
#include <wrl/implements.h>
#include "base/stl_util.h"
namespace ui {
UiaRegistrarWin::UiaRegistrarWin() {
// Create the registrar object and get the IUIAutomationRegistrar
// interface pointer.
Microsoft::WRL::ComPtr<IUIAutomationRegistrar> registrar;
if (FAILED(CoCreateInstance(CLSID_CUIAutomationRegistrar, nullptr,
CLSCTX_INPROC_SERVER, IID_IUIAutomationRegistrar,
&registrar)))
return;
// Register the custom UIA property that represents the unique id of an UIA
// element which also matches its corresponding IA2 element's unique id.
UIAutomationPropertyInfo unique_id_property_info = {
kUiaPropertyUniqueIdGuid, L"UniqueId", UIAutomationType_String};
registrar->RegisterProperty(&unique_id_property_info,
&uia_unique_id_property_id_);
// Register the custom UIA event that represents the test end event for the
// UIA test suite.
UIAutomationEventInfo test_complete_event_info = {
kUiaEventTestCompleteSentinelGuid, L"kUiaTestCompleteSentinel"};
registrar->RegisterEvent(&test_complete_event_info,
&uia_test_complete_event_id_);
}
UiaRegistrarWin::~UiaRegistrarWin() = default;
PROPERTYID UiaRegistrarWin::GetUiaUniqueIdPropertyId() const {
return uia_unique_id_property_id_;
}
EVENTID UiaRegistrarWin::GetUiaTestCompleteEventId() const {
return uia_test_complete_event_id_;
}
const UiaRegistrarWin& UiaRegistrarWin::GetInstance() {
static base::NoDestructor<UiaRegistrarWin> instance;
return *instance;
}
} // namespace ui
// Copyright 2020 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 UI_ACCESSIBILITY_PLATFORM_UIA_REGISTRAR_WIN_H_
#define UI_ACCESSIBILITY_PLATFORM_UIA_REGISTRAR_WIN_H_
#include <objbase.h>
#include <uiautomation.h>
#include "base/macros.h"
#include "base/no_destructor.h"
#include "ax/ax_export.h"
namespace ui {
// {3761326A-34B2-465A-835D-7A3D8F4EFB92}
static const GUID kUiaEventTestCompleteSentinelGuid = {
0x3761326a,
0x34b2,
0x465a,
{0x83, 0x5d, 0x7a, 0x3d, 0x8f, 0x4e, 0xfb, 0x92}};
// {cc7eeb32-4b62-4f4c-aff6-1c2e5752ad8e}
static const GUID kUiaPropertyUniqueIdGuid = {
0xcc7eeb32,
0x4b62,
0x4f4c,
{0xaf, 0xf6, 0x1c, 0x2e, 0x57, 0x52, 0xad, 0x8e}};
class AX_EXPORT UiaRegistrarWin {
public:
UiaRegistrarWin();
~UiaRegistrarWin();
PROPERTYID GetUiaUniqueIdPropertyId() const;
EVENTID GetUiaTestCompleteEventId() const;
static const UiaRegistrarWin& GetInstance();
private:
PROPERTYID uia_unique_id_property_id_ = 0;
EVENTID uia_test_complete_event_id_ = 0;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_PLATFORM_UIA_REGISTRAR_WIN_H_
// 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 BASE_BASE_EXPORT_H_
#define BASE_BASE_EXPORT_H_
#if defined(COMPONENT_BUILD)
#if defined(WIN32)
#if defined(BASE_IMPLEMENTATION)
#define BASE_EXPORT __declspec(dllexport)
#else
#define BASE_EXPORT __declspec(dllimport)
#endif // defined(BASE_IMPLEMENTATION)
#else // defined(WIN32)
#if defined(BASE_IMPLEMENTATION)
#define BASE_EXPORT __attribute__((visibility("default")))
#else
#define BASE_EXPORT
#endif // defined(BASE_IMPLEMENTATION)
#endif
#else // defined(COMPONENT_BUILD)
#define BASE_EXPORT
#endif
#endif // BASE_BASE_EXPORT_H_
// Copyright 2018 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 BASE_WIN_ATL_H_
#define BASE_WIN_ATL_H_
// Check no prior poisonous defines were made.
#include "base/win/windows_defines.inc"
// Undefine before windows header will make the poisonous defines
#include "base/win/windows_undefines.inc"
// atlwin.h relies on std::void_t, but libc++ doesn't define it unless
// _LIBCPP_STD_VER > 14. Workaround this by manually defining it.
#include <type_traits>
#if defined(_LIBCPP_STD_VER) && _LIBCPP_STD_VER <= 14
namespace std {
template <class...>
using void_t = void;
}
#endif
// Declare our own exception thrower (atl_throw.h includes atldef.h).
#include "base/win/atl_throw.h"
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atlhost.h>
#include <atlsecurity.h>
#include <atlwin.h>
// Undefine the poisonous defines
#include "base/win/windows_undefines.inc"
// Check no poisonous defines follow this include
#include "base/win/windows_defines.inc"
#endif // BASE_WIN_ATL_H_
// Copyright (c) 2011 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 UI_BASE_WIN_ATL_MODULE_H_
#define UI_BASE_WIN_ATL_MODULE_H_
namespace ui {
namespace win {
// Ensure that we have exactly one ATL module registered. It's safe to
// call this more than once. ATL functions will crash if there's no
// ATL module registered, or if you try to register two of them, so
// dynamically registering one if needed makes it much easier for us
// to support different build configurations like multi-dll without
// worrying about which side of a module boundary each ATL module object
// belongs on.
//
// This function must be implemented in this header file rather than a
// source file so that it's inlined into the module where it's included,
// rather than in the "ui" module.
inline void CreateATLModuleIfNeeded() {
if (_pAtlModule == NULL) {
// This creates the module and automatically updates _pAtlModule.
new CComModule;
}
}
} // namespace win
} // namespace ui
#endif // UI_BASE_WIN_ATL_MODULE_H_
// Copyright (c) 2011 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 "base/win/enum_variant.h"
#include <wrl/client.h>
#include <algorithm>
#include "base/check_op.h"
namespace base {
namespace win {
EnumVariant::EnumVariant(ULONG count) : current_index_(0) {
for (ULONG i = 0; i < count; ++i)
items_.emplace_back(ScopedVariant::kEmptyVariant);
}
EnumVariant::~EnumVariant() = default;
VARIANT* EnumVariant::ItemAt(ULONG index) {
DCHECK_LT(index, items_.size());
// This is a hack to return a mutable pointer to the ScopedVariant, even
// though the original intent of the AsInput method was to allow only readonly
// access to the wrapped variant.
return items_[index].AsInput();
}
HRESULT EnumVariant::Next(ULONG requested_count,
VARIANT* out_elements,
ULONG* out_elements_received) {
if (!out_elements)
return E_INVALIDARG;
DCHECK_LE(current_index_, items_.size());
ULONG available_count = ULONG{items_.size()} - current_index_;
ULONG count = std::min(requested_count, available_count);
for (ULONG i = 0; i < count; ++i)
out_elements[i] = items_[current_index_ + i].Copy();
current_index_ += count;
// The caller can choose not to get the number of received elements by setting
// |out_elements_received| to nullptr.
if (out_elements_received)
*out_elements_received = count;
return (count == requested_count ? S_OK : S_FALSE);
}
HRESULT EnumVariant::Skip(ULONG skip_count) {
ULONG count = skip_count;
if (current_index_ + count > ULONG{items_.size()})
count = ULONG{items_.size()} - current_index_;
current_index_ += count;
return (count == skip_count ? S_OK : S_FALSE);
}
HRESULT EnumVariant::Reset() {
current_index_ = 0;
return S_OK;
}
HRESULT EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
if (!out_cloned_object)
return E_INVALIDARG;
size_t count = items_.size();
Microsoft::WRL::ComPtr<EnumVariant> other =
Microsoft::WRL::Make<EnumVariant>(ULONG{count});
for (size_t i = 0; i < count; ++i)
other->items_[i] = static_cast<const VARIANT&>(items_[i]);
other->Skip(current_index_);
return other.CopyTo(IID_PPV_ARGS(out_cloned_object));
}
} // namespace win
} // namespace base
// Copyright (c) 2011 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 BASE_WIN_ENUM_VARIANT_H_
#define BASE_WIN_ENUM_VARIANT_H_
#include <wrl/implements.h>
#include <memory>
#include <vector>
#include "base/win/scoped_variant.h"
namespace base {
namespace win {
// A simple implementation of IEnumVARIANT.
class BASE_EXPORT EnumVariant
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
IEnumVARIANT> {
public:
// The constructor allocates a vector of empty ScopedVariants of size |count|.
// Use ItemAt to set the value of each item in the array.
explicit EnumVariant(ULONG count);
// IEnumVARIANT:
IFACEMETHODIMP Next(ULONG requested_count,
VARIANT* out_elements,
ULONG* out_elements_received) override;
IFACEMETHODIMP Skip(ULONG skip_count) override;
IFACEMETHODIMP Reset() override;
IFACEMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override;
// Returns a mutable pointer to the item at position |index|.
VARIANT* ItemAt(ULONG index);
private:
~EnumVariant() override;
std::vector<ScopedVariant> items_;
ULONG current_index_;
};
} // namespace win
} // namespace base
#endif // BASE_WIN_ENUM_VARIANT_H_
// Copyright (c) 2011 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 "base/win/enum_variant.h"
#include <wrl/client.h>
#include <wrl/implements.h>
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_variant.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace win {
TEST(EnumVariantTest, EmptyEnumVariant) {
ScopedCOMInitializer com_initializer;
Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(0);
Microsoft::WRL::ComPtr<IEnumVARIANT> ienumvariant;
ASSERT_TRUE(SUCCEEDED(ev->QueryInterface(IID_PPV_ARGS(&ienumvariant))));
{
base::win::ScopedVariant out_element;
ULONG out_received = 0;
EXPECT_EQ(S_FALSE, ev->Next(1, out_element.Receive(), &out_received));
EXPECT_EQ(0u, out_received);
}
EXPECT_EQ(S_FALSE, ev->Skip(1));
EXPECT_EQ(S_OK, ev->Reset());
Microsoft::WRL::ComPtr<IEnumVARIANT> ev2;
EXPECT_EQ(S_OK, ev->Clone(&ev2));
EXPECT_NE(nullptr, ev2);
EXPECT_NE(ev, ev2);
EXPECT_EQ(S_FALSE, ev2->Skip(1));
EXPECT_EQ(S_OK, ev2->Reset());
}
TEST(EnumVariantTest, SimpleEnumVariant) {
ScopedCOMInitializer com_initializer;
Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3);
ev->ItemAt(0)->vt = VT_I4;
ev->ItemAt(0)->lVal = 10;
ev->ItemAt(1)->vt = VT_I4;
ev->ItemAt(1)->lVal = 20;
ev->ItemAt(2)->vt = VT_I4;
ev->ItemAt(2)->lVal = 30;
// Get elements one at a time from index 0 and 2.
base::win::ScopedVariant out_element_0;
ULONG out_received_0 = 0;
EXPECT_EQ(S_OK, ev->Next(1, out_element_0.Receive(), &out_received_0));
EXPECT_EQ(1u, out_received_0);
EXPECT_EQ(VT_I4, out_element_0.ptr()->vt);
EXPECT_EQ(10, out_element_0.ptr()->lVal);
EXPECT_EQ(S_OK, ev->Skip(1));
base::win::ScopedVariant out_element_2;
ULONG out_received_2 = 0;
EXPECT_EQ(S_OK, ev->Next(1, out_element_2.Receive(), &out_received_2));
EXPECT_EQ(1u, out_received_2);
EXPECT_EQ(VT_I4, out_element_2.ptr()->vt);
EXPECT_EQ(30, out_element_2.ptr()->lVal);
base::win::ScopedVariant placeholder_variant;
EXPECT_EQ(S_FALSE, ev->Next(1, placeholder_variant.Receive(), nullptr));
// Verify the reset works for the next step.
ASSERT_EQ(S_OK, ev->Reset());
// Get all elements at once.
VARIANT out_elements[3];
ULONG out_received_multiple;
for (int i = 0; i < 3; ++i)
::VariantInit(&out_elements[i]);
EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received_multiple));
EXPECT_EQ(3u, out_received_multiple);
EXPECT_EQ(VT_I4, out_elements[0].vt);
EXPECT_EQ(10, out_elements[0].lVal);
EXPECT_EQ(VT_I4, out_elements[1].vt);
EXPECT_EQ(20, out_elements[1].lVal);
EXPECT_EQ(VT_I4, out_elements[2].vt);
EXPECT_EQ(30, out_elements[2].lVal);
for (int i = 0; i < 3; ++i)
::VariantClear(&out_elements[i]);
base::win::ScopedVariant placeholder_variant_multiple;
EXPECT_EQ(S_FALSE,
ev->Next(1, placeholder_variant_multiple.Receive(), nullptr));
}
TEST(EnumVariantTest, Clone) {
ScopedCOMInitializer com_initializer;
Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3);
ev->ItemAt(0)->vt = VT_I4;
ev->ItemAt(0)->lVal = 10;
ev->ItemAt(1)->vt = VT_I4;
ev->ItemAt(1)->lVal = 20;
ev->ItemAt(2)->vt = VT_I4;
ev->ItemAt(2)->lVal = 30;
// Clone it.
Microsoft::WRL::ComPtr<IEnumVARIANT> ev2;
EXPECT_EQ(S_OK, ev->Clone(&ev2));
EXPECT_TRUE(ev2 != nullptr);
VARIANT out_elements[3];
for (int i = 0; i < 3; ++i)
::VariantInit(&out_elements[i]);
EXPECT_EQ(S_OK, ev2->Next(3, out_elements, nullptr));
EXPECT_EQ(VT_I4, out_elements[0].vt);
EXPECT_EQ(10, out_elements[0].lVal);
EXPECT_EQ(VT_I4, out_elements[1].vt);
EXPECT_EQ(20, out_elements[1].lVal);
EXPECT_EQ(VT_I4, out_elements[2].vt);
EXPECT_EQ(30, out_elements[2].lVal);
for (int i = 0; i < 3; ++i)
::VariantClear(&out_elements[i]);
}
} // namespace win
} // namespace base
// 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.
#include "base/win/scoped_bstr.h"
#include <stdint.h>
#include "base/check.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/memory.h"
#include "base/strings/string_util.h"
namespace base {
namespace win {
namespace {
BSTR AllocBstrOrDie(WStringPiece non_bstr) {
BSTR result = ::SysAllocStringLen(non_bstr.data(),
checked_cast<UINT>(non_bstr.length()));
if (!result) {
base::TerminateBecauseOutOfMemory((non_bstr.length() + 1) *
sizeof(wchar_t));
}
return result;
}
BSTR AllocBstrBytesOrDie(size_t bytes) {
BSTR result = ::SysAllocStringByteLen(nullptr, checked_cast<UINT>(bytes));
if (!result)
base::TerminateBecauseOutOfMemory(bytes + sizeof(wchar_t));
return result;
}
} // namespace
ScopedBstr::ScopedBstr(WStringPiece non_bstr)
: bstr_(AllocBstrOrDie(non_bstr)) {}
ScopedBstr::~ScopedBstr() {
static_assert(sizeof(ScopedBstr) == sizeof(BSTR), "ScopedBstrSize");
::SysFreeString(bstr_);
}
void ScopedBstr::Reset(BSTR bstr) {
if (bstr != bstr_) {
// SysFreeString handles null properly.
::SysFreeString(bstr_);
bstr_ = bstr;
}
}
BSTR ScopedBstr::Release() {
BSTR bstr = bstr_;
bstr_ = nullptr;
return bstr;
}
void ScopedBstr::Swap(ScopedBstr& bstr2) {
BSTR tmp = bstr_;
bstr_ = bstr2.bstr_;
bstr2.bstr_ = tmp;
}
BSTR* ScopedBstr::Receive() {
DCHECK(!bstr_) << "BSTR leak.";
return &bstr_;
}
BSTR ScopedBstr::Allocate(WStringPiece str) {
Reset(AllocBstrOrDie(str));
return bstr_;
}
BSTR ScopedBstr::AllocateBytes(size_t bytes) {
Reset(AllocBstrBytesOrDie(bytes));
return bstr_;
}
void ScopedBstr::SetByteLen(size_t bytes) {
DCHECK(bstr_);
uint32_t* data = reinterpret_cast<uint32_t*>(bstr_);
data[-1] = checked_cast<uint32_t>(bytes);
}
size_t ScopedBstr::Length() const {
return ::SysStringLen(bstr_);
}
size_t ScopedBstr::ByteLength() const {
return ::SysStringByteLen(bstr_);
}
} // namespace win
} // namespace base
// Copyright (c) 2011 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 BASE_WIN_SCOPED_BSTR_H_
#define BASE_WIN_SCOPED_BSTR_H_
#include <windows.h>
#include <oleauto.h>
#include <stddef.h>
#include "base/base_export.h"
#include "base/check.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
namespace base {
namespace win {
// Manages a BSTR string pointer.
// The class interface is based on unique_ptr.
class BASE_EXPORT ScopedBstr {
public:
ScopedBstr() = default;
// Constructor to create a new BSTR.
//
// NOTE: Do not pass a BSTR to this constructor expecting ownership to
// be transferred - even though it compiles! ;-)
explicit ScopedBstr(WStringPiece non_bstr);
~ScopedBstr();
BSTR Get() const { return bstr_; }
// Give ScopedBstr ownership over an already allocated BSTR or null.
// If you need to allocate a new BSTR instance, use |allocate| instead.
void Reset(BSTR bstr = nullptr);
// Releases ownership of the BSTR to the caller.
BSTR Release();
// Creates a new BSTR from a 16-bit C-style string.
//
// If you already have a BSTR and want to transfer ownership to the
// ScopedBstr instance, call |reset| instead.
//
// Returns a pointer to the new BSTR.
BSTR Allocate(WStringPiece str);
// Allocates a new BSTR with the specified number of bytes.
// Returns a pointer to the new BSTR.
BSTR AllocateBytes(size_t bytes);
// Sets the allocated length field of the already-allocated BSTR to be
// |bytes|. This is useful when the BSTR was preallocated with e.g.
// SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and then
// not all the bytes are being used.
//
// Note that if you want to set the length to a specific number of
// characters, you need to multiply by sizeof(wchar_t). Oddly, there's no
// public API to set the length, so we do this ourselves by hand.
//
// NOTE: The actual allocated size of the BSTR MUST be >= bytes. That
// responsibility is with the caller.
void SetByteLen(size_t bytes);
// Swap values of two ScopedBstr's.
void Swap(ScopedBstr& bstr2);
// Retrieves the pointer address.
// Used to receive BSTRs as out arguments (and take ownership).
// The function DCHECKs on the current value being null.
// Usage: GetBstr(bstr.Receive());
BSTR* Receive();
// Returns number of chars in the BSTR.
size_t Length() const;
// Returns the number of bytes allocated for the BSTR.
size_t ByteLength() const;
// Forbid comparison of ScopedBstr types. You should never have the same
// BSTR owned by two different scoped_ptrs.
bool operator==(const ScopedBstr& bstr2) const = delete;
bool operator!=(const ScopedBstr& bstr2) const = delete;
protected:
BSTR bstr_ = nullptr;
private:
DISALLOW_COPY_AND_ASSIGN(ScopedBstr);
};
} // namespace win
} // namespace base
#endif // BASE_WIN_SCOPED_BSTR_H_
// 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.
#include "base/win/scoped_bstr.h"
#include <stddef.h>
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace win {
namespace {
constexpr wchar_t kTestString1[] = L"123";
constexpr wchar_t kTestString2[] = L"456789";
constexpr size_t test1_len = size(kTestString1) - 1;
constexpr size_t test2_len = size(kTestString2) - 1;
} // namespace
TEST(ScopedBstrTest, Empty) {
ScopedBstr b;
EXPECT_EQ(nullptr, b.Get());
EXPECT_EQ(0u, b.Length());
EXPECT_EQ(0u, b.ByteLength());
b.Reset(nullptr);
EXPECT_EQ(nullptr, b.Get());
EXPECT_EQ(nullptr, b.Release());
ScopedBstr b2;
b.Swap(b2);
EXPECT_EQ(nullptr, b.Get());
}
TEST(ScopedBstrTest, Basic) {
ScopedBstr b(kTestString1);
EXPECT_EQ(test1_len, b.Length());
EXPECT_EQ(test1_len * sizeof(kTestString1[0]), b.ByteLength());
}
namespace {
void CreateTestString1(BSTR* ret) {
*ret = SysAllocString(kTestString1);
}
} // namespace
TEST(ScopedBstrTest, Swap) {
ScopedBstr b1(kTestString1);
ScopedBstr b2;
b1.Swap(b2);
EXPECT_EQ(test1_len, b2.Length());
EXPECT_EQ(0u, b1.Length());
EXPECT_STREQ(kTestString1, b2.Get());
BSTR tmp = b2.Release();
EXPECT_NE(nullptr, tmp);
EXPECT_STREQ(kTestString1, tmp);
EXPECT_EQ(nullptr, b2.Get());
SysFreeString(tmp);
}
TEST(ScopedBstrTest, OutParam) {
ScopedBstr b;
CreateTestString1(b.Receive());
EXPECT_STREQ(kTestString1, b.Get());
}
TEST(ScopedBstrTest, AllocateBytesAndSetByteLen) {
constexpr size_t num_bytes = 100;
ScopedBstr b;
EXPECT_NE(nullptr, b.AllocateBytes(num_bytes));
EXPECT_EQ(num_bytes, b.ByteLength());
EXPECT_EQ(num_bytes / sizeof(kTestString1[0]), b.Length());
lstrcpy(b.Get(), kTestString1);
EXPECT_EQ(test1_len, static_cast<size_t>(lstrlen(b.Get())));
EXPECT_EQ(num_bytes / sizeof(kTestString1[0]), b.Length());
b.SetByteLen(lstrlen(b.Get()) * sizeof(kTestString2[0]));
EXPECT_EQ(b.Length(), static_cast<size_t>(lstrlen(b.Get())));
}
TEST(ScopedBstrTest, AllocateAndSetByteLen) {
ScopedBstr b;
EXPECT_NE(nullptr, b.Allocate(kTestString2));
EXPECT_EQ(test2_len, b.Length());
b.SetByteLen((test2_len - 1) * sizeof(kTestString2[0]));
EXPECT_EQ(test2_len - 1, b.Length());
}
} // namespace win
} // namespace base
// Copyright 2019 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 BASE_WIN_SCOPED_SAFEARRAY_H_
#define BASE_WIN_SCOPED_SAFEARRAY_H_
#include <objbase.h>
#include "base/base_export.h"
#include "base/check_op.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/win/variant_util.h"
namespace base {
namespace win {
// Manages a Windows SAFEARRAY. This is a minimal wrapper that simply provides
// RAII semantics and does not duplicate the extensive functionality that
// CComSafeArray offers.
class BASE_EXPORT ScopedSafearray {
public:
// LockScope<VARTYPE> class for automatically managing the lifetime of a
// SAFEARRAY lock, and granting easy access to the underlying data either
// through random access or as an iterator.
// It is undefined behavior if the underlying SAFEARRAY is destroyed
// before the LockScope.
// LockScope implements std::iterator_traits as a random access iterator, so
// that LockScope is compatible with STL methods that require these traits.
template <VARTYPE ElementVartype>
class BASE_EXPORT LockScope final {
public:
// Type declarations to support std::iterator_traits
using iterator_category = std::random_access_iterator_tag;
using value_type = typename internal::VariantUtil<ElementVartype>::Type;
using difference_type = ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
LockScope()
: safearray_(nullptr),
vartype_(VT_EMPTY),
array_(nullptr),
array_size_(0U) {}
LockScope(LockScope<ElementVartype>&& other)
: safearray_(std::exchange(other.safearray_, nullptr)),
vartype_(std::exchange(other.vartype_, VT_EMPTY)),
array_(std::exchange(other.array_, nullptr)),
array_size_(std::exchange(other.array_size_, 0U)) {}
LockScope<ElementVartype>& operator=(LockScope<ElementVartype>&& other) {
DCHECK_NE(this, &other);
Reset();
safearray_ = std::exchange(other.safearray_, nullptr);
vartype_ = std::exchange(other.vartype_, VT_EMPTY);
array_ = std::exchange(other.array_, nullptr);
array_size_ = std::exchange(other.array_size_, 0U);
return *this;
}
~LockScope() { Reset(); }
VARTYPE Type() const { return vartype_; }
size_t size() const { return array_size_; }
pointer begin() { return array_; }
pointer end() { return array_ + array_size_; }
const_pointer begin() const { return array_; }
const_pointer end() const { return array_ + array_size_; }
pointer data() { return array_; }
const_pointer data() const { return array_; }
reference operator[](int index) { return at(index); }
const_reference operator[](int index) const { return at(index); }
reference at(size_t index) {
DCHECK_NE(array_, nullptr);
DCHECK_LT(index, array_size_);
return array_[index];
}
const_reference at(size_t index) const {
return const_cast<LockScope<ElementVartype>*>(this)->at(index);
}
private:
LockScope(SAFEARRAY* safearray,
VARTYPE vartype,
pointer array,
size_t array_size)
: safearray_(safearray),
vartype_(vartype),
array_(array),
array_size_(array_size) {}
void Reset() {
if (safearray_)
SafeArrayUnaccessData(safearray_);
safearray_ = nullptr;
vartype_ = VT_EMPTY;
array_ = nullptr;
array_size_ = 0U;
}
SAFEARRAY* safearray_;
VARTYPE vartype_;
pointer array_;
size_t array_size_;
friend class ScopedSafearray;
DISALLOW_COPY_AND_ASSIGN(LockScope);
};
explicit ScopedSafearray(SAFEARRAY* safearray = nullptr)
: safearray_(safearray) {}
// Move constructor
ScopedSafearray(ScopedSafearray&& r) noexcept : safearray_(r.safearray_) {
r.safearray_ = nullptr;
}
// Move operator=. Allows assignment from a ScopedSafearray rvalue.
ScopedSafearray& operator=(ScopedSafearray&& rvalue) {
Reset(rvalue.Release());
return *this;
}
~ScopedSafearray() { Destroy(); }
// Creates a LockScope for accessing the contents of a
// single-dimensional SAFEARRAYs.
template <VARTYPE ElementVartype>
base::Optional<LockScope<ElementVartype>> CreateLockScope() const {
if (!safearray_ || SafeArrayGetDim(safearray_) != 1)
return base::nullopt;
VARTYPE vartype;
HRESULT hr = SafeArrayGetVartype(safearray_, &vartype);
if (FAILED(hr) ||
!internal::VariantUtil<ElementVartype>::IsConvertibleTo(vartype)) {
return base::nullopt;
}
typename LockScope<ElementVartype>::pointer array = nullptr;
hr = SafeArrayAccessData(safearray_, reinterpret_cast<void**>(&array));
if (FAILED(hr))
return base::nullopt;
const size_t array_size = GetCount();
return LockScope<ElementVartype>(safearray_, vartype, array, array_size);
}
void Destroy() {
if (safearray_) {
HRESULT hr = SafeArrayDestroy(safearray_);
DCHECK_EQ(S_OK, hr);
safearray_ = nullptr;
}
}
// Give ScopedSafearray ownership over an already allocated SAFEARRAY or
// nullptr.
void Reset(SAFEARRAY* safearray = nullptr) {
if (safearray != safearray_) {
Destroy();
safearray_ = safearray;
}
}
// Releases ownership of the SAFEARRAY to the caller.
SAFEARRAY* Release() {
SAFEARRAY* safearray = safearray_;
safearray_ = nullptr;
return safearray;
}
// Retrieves the pointer address.
// Used to receive SAFEARRAYs as out arguments (and take ownership).
// This function releases any existing references because it will leak
// the existing ref otherwise.
// Usage: GetSafearray(safearray.Receive());
SAFEARRAY** Receive() {
Destroy();
return &safearray_;
}
// Returns the number of elements in a dimension of the array.
size_t GetCount(UINT dimension = 0) const {
DCHECK(safearray_);
// Initialize |lower| and |upper| so this method will return zero if either
// SafeArrayGetLBound or SafeArrayGetUBound returns failure because they
// only write to the output parameter when successful.
LONG lower = 0;
LONG upper = -1;
DCHECK_LT(dimension, SafeArrayGetDim(safearray_));
HRESULT hr = SafeArrayGetLBound(safearray_, dimension + 1, &lower);
DCHECK(SUCCEEDED(hr));
hr = SafeArrayGetUBound(safearray_, dimension + 1, &upper);
DCHECK(SUCCEEDED(hr));
return (upper - lower + 1);
}
// Returns the internal pointer.
SAFEARRAY* Get() const { return safearray_; }
// Forbid comparison of ScopedSafearray types. You should never have the same
// SAFEARRAY owned by two different scoped_ptrs.
bool operator==(const ScopedSafearray& safearray2) const = delete;
bool operator!=(const ScopedSafearray& safearray2) const = delete;
private:
SAFEARRAY* safearray_;
DISALLOW_COPY_AND_ASSIGN(ScopedSafearray);
};
} // namespace win
} // namespace base
#endif // BASE_WIN_SCOPED_SAFEARRAY_H_
// Copyright 2019 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 "base/win/scoped_safearray.h"
#include <stddef.h>
#include <array>
#include <vector>
#include "base/stl_util.h"
#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace win {
namespace {
static constexpr std::array<int, 5> kInputValues = {0, 1, 2, 1, 0};
static void PopulateScopedSafearrayOfInts(ScopedSafearray& scoped_safe_array) {
// TODO(crbug.com/1082005): Create a safer alternative to SAFEARRAY methods.
scoped_safe_array.Reset(SafeArrayCreateVector(
/*vartype=*/VT_I4, /*lower_bound=*/2,
/*element_count=*/kInputValues.size()));
ASSERT_NE(scoped_safe_array.Get(), nullptr);
ASSERT_EQ(SafeArrayGetDim(scoped_safe_array.Get()), 1U);
ASSERT_EQ(scoped_safe_array.GetCount(), kInputValues.size());
int* int_array;
ASSERT_HRESULT_SUCCEEDED(SafeArrayAccessData(
scoped_safe_array.Get(), reinterpret_cast<void**>(&int_array)));
for (size_t i = 0; i < kInputValues.size(); ++i)
int_array[i] = kInputValues[i];
ASSERT_HRESULT_SUCCEEDED(SafeArrayUnaccessData(scoped_safe_array.Get()));
}
} // namespace
TEST(ScopedSafearrayTest, ScopedSafearrayMethods) {
ScopedSafearray empty_safe_array;
EXPECT_EQ(empty_safe_array.Get(), nullptr);
EXPECT_EQ(empty_safe_array.Release(), nullptr);
EXPECT_NE(empty_safe_array.Receive(), nullptr);
SAFEARRAY* safe_array = SafeArrayCreateVector(
VT_R8 /* element type */, 0 /* lower bound */, 4 /* elements */);
ScopedSafearray scoped_safe_array(safe_array);
EXPECT_EQ(scoped_safe_array.Get(), safe_array);
EXPECT_EQ(scoped_safe_array.Release(), safe_array);
EXPECT_NE(scoped_safe_array.Receive(), nullptr);
// The Release() call should have set the internal pointer to nullptr
EXPECT_EQ(scoped_safe_array.Get(), nullptr);
scoped_safe_array.Reset(safe_array);
EXPECT_EQ(scoped_safe_array.Get(), safe_array);
ScopedSafearray moved_safe_array(std::move(scoped_safe_array));
EXPECT_EQ(moved_safe_array.Get(), safe_array);
EXPECT_EQ(moved_safe_array.Release(), safe_array);
EXPECT_NE(moved_safe_array.Receive(), nullptr);
// std::move should have cleared the values of scoped_safe_array
EXPECT_EQ(scoped_safe_array.Get(), nullptr);
EXPECT_EQ(scoped_safe_array.Release(), nullptr);
EXPECT_NE(scoped_safe_array.Receive(), nullptr);
scoped_safe_array.Reset(safe_array);
EXPECT_EQ(scoped_safe_array.Get(), safe_array);
ScopedSafearray assigment_moved_safe_array = std::move(scoped_safe_array);
EXPECT_EQ(assigment_moved_safe_array.Get(), safe_array);
EXPECT_EQ(assigment_moved_safe_array.Release(), safe_array);
EXPECT_NE(assigment_moved_safe_array.Receive(), nullptr);
// The move-assign operator= should have cleared the values of
// scoped_safe_array
EXPECT_EQ(scoped_safe_array.Get(), nullptr);
EXPECT_EQ(scoped_safe_array.Release(), nullptr);
EXPECT_NE(scoped_safe_array.Receive(), nullptr);
// Calling Receive() will free the existing reference
ScopedSafearray safe_array_received(SafeArrayCreateVector(
VT_R8 /* element type */, 0 /* lower bound */, 4 /* elements */));
EXPECT_NE(safe_array_received.Receive(), nullptr);
EXPECT_EQ(safe_array_received.Get(), nullptr);
}
TEST(ScopedSafearrayTest, ScopedSafearrayMoveConstructor) {
ScopedSafearray first;
PopulateScopedSafearrayOfInts(first);
EXPECT_NE(first.Get(), nullptr);
EXPECT_EQ(first.GetCount(), kInputValues.size());
SAFEARRAY* safearray = first.Get();
ScopedSafearray second(std::move(first));
EXPECT_EQ(first.Get(), nullptr);
EXPECT_EQ(second.Get(), safearray);
}
TEST(ScopedSafearrayTest, ScopedSafearrayMoveAssignOperator) {
ScopedSafearray first, second;
PopulateScopedSafearrayOfInts(first);
EXPECT_NE(first.Get(), nullptr);
EXPECT_EQ(first.GetCount(), kInputValues.size());
SAFEARRAY* safearray = first.Get();
second = std::move(first);
EXPECT_EQ(first.Get(), nullptr);
EXPECT_EQ(second.Get(), safearray);
// Indirectly move |second| into itself.
ScopedSafearray& reference_to_second = second;
second = std::move(reference_to_second);
EXPECT_EQ(second.GetCount(), kInputValues.size());
EXPECT_EQ(second.Get(), safearray);
}
TEST(ScopedSafearrayTest, ScopedSafearrayCast) {
SAFEARRAY* safe_array = SafeArrayCreateVector(
VT_R8 /* element type */, 1 /* lower bound */, 5 /* elements */);
ScopedSafearray scoped_safe_array(safe_array);
EXPECT_EQ(SafeArrayGetDim(scoped_safe_array.Get()), 1U);
LONG lower_bound;
EXPECT_HRESULT_SUCCEEDED(
SafeArrayGetLBound(scoped_safe_array.Get(), 1, &lower_bound));
EXPECT_EQ(lower_bound, 1);
LONG upper_bound;
EXPECT_HRESULT_SUCCEEDED(
SafeArrayGetUBound(scoped_safe_array.Get(), 1, &upper_bound));
EXPECT_EQ(upper_bound, 5);
VARTYPE variable_type;
EXPECT_HRESULT_SUCCEEDED(
SafeArrayGetVartype(scoped_safe_array.Get(), &variable_type));
EXPECT_EQ(variable_type, VT_R8);
}
TEST(ScopedSafearrayTest, InitiallyEmpty) {
ScopedSafearray empty_safe_array;
EXPECT_EQ(empty_safe_array.Get(), nullptr);
EXPECT_DCHECK_DEATH(empty_safe_array.GetCount());
}
TEST(ScopedSafearrayTest, ScopedSafearrayGetCount) {
// TODO(crbug.com/1082005): Create a safer alternative to SAFEARRAY methods.
ScopedSafearray scoped_safe_array(SafeArrayCreateVector(
/*vartype=*/VT_I4, /*lower_bound=*/2, /*element_count=*/5));
ASSERT_NE(scoped_safe_array.Get(), nullptr);
EXPECT_EQ(SafeArrayGetDim(scoped_safe_array.Get()), 1U);
LONG lower_bound;
EXPECT_HRESULT_SUCCEEDED(
SafeArrayGetLBound(scoped_safe_array.Get(), 1, &lower_bound));
EXPECT_EQ(lower_bound, 2);
LONG upper_bound;
EXPECT_HRESULT_SUCCEEDED(
SafeArrayGetUBound(scoped_safe_array.Get(), 1, &upper_bound));
EXPECT_EQ(upper_bound, 6);
EXPECT_EQ(scoped_safe_array.GetCount(), 5U);
}
TEST(ScopedSafearrayTest, ScopedSafearrayInitialLockScope) {
ScopedSafearray scoped_safe_array;
base::Optional<ScopedSafearray::LockScope<VT_I4>> lock_scope =
scoped_safe_array.CreateLockScope<VT_I4>();
EXPECT_FALSE(lock_scope.has_value());
}
TEST(ScopedSafearrayTest, ScopedSafearrayLockScopeMoveConstructor) {
ScopedSafearray scoped_safe_array;
PopulateScopedSafearrayOfInts(scoped_safe_array);
base::Optional<ScopedSafearray::LockScope<VT_I4>> first =
scoped_safe_array.CreateLockScope<VT_I4>();
ASSERT_TRUE(first.has_value());
EXPECT_EQ(first->Type(), VT_I4);
EXPECT_EQ(first->size(), kInputValues.size());
ScopedSafearray::LockScope<VT_I4> second(std::move(*first));
EXPECT_EQ(first->Type(), VT_EMPTY);
EXPECT_EQ(first->size(), 0U);
EXPECT_EQ(second.Type(), VT_I4);
EXPECT_EQ(second.size(), kInputValues.size());
}
TEST(ScopedSafearrayTest, ScopedSafearrayLockScopeMoveAssignOperator) {
ScopedSafearray scoped_safe_array;
PopulateScopedSafearrayOfInts(scoped_safe_array);
base::Optional<ScopedSafearray::LockScope<VT_I4>> first =
scoped_safe_array.CreateLockScope<VT_I4>();
ASSERT_TRUE(first.has_value());
EXPECT_EQ(first->Type(), VT_I4);
EXPECT_EQ(first->size(), kInputValues.size());
ScopedSafearray::LockScope<VT_I4> second;
second = std::move(*first);
EXPECT_EQ(first->Type(), VT_EMPTY);
EXPECT_EQ(first->size(), 0U);
EXPECT_EQ(second.Type(), VT_I4);
EXPECT_EQ(second.size(), kInputValues.size());
// Indirectly move |second| into itself.
ScopedSafearray::LockScope<VT_I4>& reference_to_second = second;
EXPECT_DCHECK_DEATH(second = std::move(reference_to_second));
}
TEST(ScopedSafearrayTest, ScopedSafearrayLockScopeTypeMismatch) {
ScopedSafearray scoped_safe_array;
PopulateScopedSafearrayOfInts(scoped_safe_array);
{
base::Optional<ScopedSafearray::LockScope<VT_BSTR>> invalid_lock_scope =
scoped_safe_array.CreateLockScope<VT_BSTR>();
EXPECT_FALSE(invalid_lock_scope.has_value());
}
{
base::Optional<ScopedSafearray::LockScope<VT_UI4>> invalid_lock_scope =
scoped_safe_array.CreateLockScope<VT_UI4>();
EXPECT_FALSE(invalid_lock_scope.has_value());
}
}
TEST(ScopedSafearrayTest, ScopedSafearrayLockScopeRandomAccess) {
ScopedSafearray scoped_safe_array;
PopulateScopedSafearrayOfInts(scoped_safe_array);
base::Optional<ScopedSafearray::LockScope<VT_I4>> lock_scope =
scoped_safe_array.CreateLockScope<VT_I4>();
ASSERT_TRUE(lock_scope.has_value());
EXPECT_EQ(lock_scope->Type(), VT_I4);
EXPECT_EQ(lock_scope->size(), kInputValues.size());
for (size_t i = 0; i < kInputValues.size(); ++i) {
EXPECT_EQ(lock_scope->at(i), kInputValues[i]);
EXPECT_EQ((*lock_scope)[i], kInputValues[i]);
}
}
TEST(ScopedSafearrayTest, ScopedSafearrayLockScopeIterator) {
ScopedSafearray scoped_safe_array;
PopulateScopedSafearrayOfInts(scoped_safe_array);
base::Optional<ScopedSafearray::LockScope<VT_I4>> lock_scope =
scoped_safe_array.CreateLockScope<VT_I4>();
std::vector<int> unpacked_vector(lock_scope->begin(), lock_scope->end());
ASSERT_EQ(unpacked_vector.size(), kInputValues.size());
for (size_t i = 0; i < kInputValues.size(); ++i)
EXPECT_EQ(unpacked_vector[i], kInputValues[i]);
}
} // namespace win
} // namespace base
// 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.
#include "base/win/scoped_variant.h"
#include <wrl/client.h>
#include <algorithm>
#include <functional>
#include "base/check.h"
#include "base/logging.h"
#include "base/numerics/ranges.h"
#include "base/win/propvarutil.h"
#include "base/win/variant_util.h"
namespace base {
namespace win {
// Global, const instance of an empty variant.
const VARIANT ScopedVariant::kEmptyVariant = {{{VT_EMPTY}}};
ScopedVariant::ScopedVariant(ScopedVariant&& var) {
var_.vt = VT_EMPTY;
Reset(var.Release());
}
ScopedVariant::~ScopedVariant() {
static_assert(sizeof(ScopedVariant) == sizeof(VARIANT), "ScopedVariantSize");
::VariantClear(&var_);
}
ScopedVariant::ScopedVariant(const wchar_t* str) {
var_.vt = VT_EMPTY;
Set(str);
}
ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) {
var_.vt = VT_BSTR;
var_.bstrVal = ::SysAllocStringLen(str, length);
}
ScopedVariant::ScopedVariant(long value, VARTYPE vt) {
var_.vt = vt;
var_.lVal = value;
}
ScopedVariant::ScopedVariant(int value) {
var_.vt = VT_I4;
var_.lVal = value;
}
ScopedVariant::ScopedVariant(bool value) {
var_.vt = VT_BOOL;
var_.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
}
ScopedVariant::ScopedVariant(double value, VARTYPE vt) {
DCHECK(vt == VT_R8 || vt == VT_DATE);
var_.vt = vt;
var_.dblVal = value;
}
ScopedVariant::ScopedVariant(IDispatch* dispatch) {
var_.vt = VT_EMPTY;
Set(dispatch);
}
ScopedVariant::ScopedVariant(IUnknown* unknown) {
var_.vt = VT_EMPTY;
Set(unknown);
}
ScopedVariant::ScopedVariant(SAFEARRAY* safearray) {
var_.vt = VT_EMPTY;
Set(safearray);
}
ScopedVariant::ScopedVariant(const VARIANT& var) {
var_.vt = VT_EMPTY;
Set(var);
}
void ScopedVariant::Reset(const VARIANT& var) {
if (&var != &var_) {
::VariantClear(&var_);
var_ = var;
}
}
VARIANT ScopedVariant::Release() {
VARIANT var = var_;
var_.vt = VT_EMPTY;
return var;
}
void ScopedVariant::Swap(ScopedVariant& var) {
VARIANT tmp = var_;
var_ = var.var_;
var.var_ = tmp;
}
VARIANT* ScopedVariant::Receive() {
DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt;
return &var_;
}
VARIANT ScopedVariant::Copy() const {
VARIANT ret = {{{VT_EMPTY}}};
::VariantCopy(&ret, &var_);
return ret;
}
int ScopedVariant::Compare(const VARIANT& other, bool ignore_case) const {
DCHECK(!V_ISARRAY(&var_))
<< "Comparison is not supported when |this| owns a SAFEARRAY";
DCHECK(!V_ISARRAY(&other))
<< "Comparison is not supported when |other| owns a SAFEARRAY";
const bool this_is_empty = var_.vt == VT_EMPTY || var_.vt == VT_NULL;
const bool other_is_empty = other.vt == VT_EMPTY || other.vt == VT_NULL;
// 1. VT_NULL and VT_EMPTY is always considered less-than any other VARTYPE.
if (this_is_empty)
return other_is_empty ? 0 : -1;
if (other_is_empty)
return 1;
// 2. If both VARIANTS have either VT_UNKNOWN or VT_DISPATCH even if the
// VARTYPEs do not match, the address of its IID_IUnknown is compared to
// guarantee a logical ordering even though it is not a meaningful order.
// e.g. (a.Compare(b) != b.Compare(a)) unless (a == b).
const bool this_is_unknown = var_.vt == VT_UNKNOWN || var_.vt == VT_DISPATCH;
const bool other_is_unknown =
other.vt == VT_UNKNOWN || other.vt == VT_DISPATCH;
if (this_is_unknown && other_is_unknown) {
// https://docs.microsoft.com/en-us/windows/win32/com/rules-for-implementing-queryinterface
// Query IID_IUnknown to determine whether the two variants point
// to the same instance of an object
Microsoft::WRL::ComPtr<IUnknown> this_unknown;
Microsoft::WRL::ComPtr<IUnknown> other_unknown;
V_UNKNOWN(&var_)->QueryInterface(IID_PPV_ARGS(&this_unknown));
V_UNKNOWN(&other)->QueryInterface(IID_PPV_ARGS(&other_unknown));
if (this_unknown.Get() == other_unknown.Get())
return 0;
// std::less for any pointer type yields a strict total order even if the
// built-in operator< does not.
return std::less<>{}(this_unknown.Get(), other_unknown.Get()) ? -1 : 1;
}
// 3. If the VARTYPEs do not match, then the value of the VARTYPE is compared.
if (V_VT(&var_) != V_VT(&other))
return (V_VT(&var_) < V_VT(&other)) ? -1 : 1;
const VARTYPE shared_vartype = V_VT(&var_);
// 4. Comparing VT_BSTR values is a lexicographical comparison of the contents
// of the BSTR, taking into account |ignore_case|.
if (shared_vartype == VT_BSTR) {
ULONG flags = ignore_case ? NORM_IGNORECASE : 0;
HRESULT hr =
::VarBstrCmp(V_BSTR(&var_), V_BSTR(&other), LOCALE_USER_DEFAULT, flags);
DCHECK(SUCCEEDED(hr) && hr != VARCMP_NULL)
<< "unsupported variant comparison: " << var_.vt << " and " << other.vt;
switch (hr) {
case VARCMP_LT:
return -1;
case VARCMP_GT:
case VARCMP_NULL:
return 1;
default:
return 0;
}
}
// 5. Otherwise returns the lexicographical comparison of the values held by
// the two VARIANTS that share the same VARTYPE.
return ::VariantCompare(var_, other);
}
void ScopedVariant::Set(const wchar_t* str) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_BSTR;
var_.bstrVal = ::SysAllocString(str);
}
void ScopedVariant::Set(int8_t i8) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_I1;
var_.cVal = i8;
}
void ScopedVariant::Set(uint8_t ui8) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_UI1;
var_.bVal = ui8;
}
void ScopedVariant::Set(int16_t i16) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_I2;
var_.iVal = i16;
}
void ScopedVariant::Set(uint16_t ui16) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_UI2;
var_.uiVal = ui16;
}
void ScopedVariant::Set(int32_t i32) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_I4;
var_.lVal = i32;
}
void ScopedVariant::Set(uint32_t ui32) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_UI4;
var_.ulVal = ui32;
}
void ScopedVariant::Set(int64_t i64) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_I8;
var_.llVal = i64;
}
void ScopedVariant::Set(uint64_t ui64) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_UI8;
var_.ullVal = ui64;
}
void ScopedVariant::Set(float r32) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_R4;
var_.fltVal = r32;
}
void ScopedVariant::Set(double r64) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_R8;
var_.dblVal = r64;
}
void ScopedVariant::SetDate(DATE date) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_DATE;
var_.date = date;
}
void ScopedVariant::Set(IDispatch* disp) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_DISPATCH;
var_.pdispVal = disp;
if (disp)
disp->AddRef();
}
void ScopedVariant::Set(bool b) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_BOOL;
var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE;
}
void ScopedVariant::Set(IUnknown* unk) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
var_.vt = VT_UNKNOWN;
var_.punkVal = unk;
if (unk)
unk->AddRef();
}
void ScopedVariant::Set(SAFEARRAY* array) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) {
var_.vt |= VT_ARRAY;
var_.parray = array;
} else {
DCHECK(!array) << "Unable to determine safearray vartype";
var_.vt = VT_EMPTY;
}
}
void ScopedVariant::Set(const VARIANT& var) {
DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
if (FAILED(::VariantCopy(&var_, &var))) {
DLOG(ERROR) << "VariantCopy failed";
var_.vt = VT_EMPTY;
}
}
ScopedVariant& ScopedVariant::operator=(ScopedVariant&& var) {
if (var.ptr() != &var_)
Reset(var.Release());
return *this;
}
ScopedVariant& ScopedVariant::operator=(const VARIANT& var) {
if (&var != &var_) {
VariantClear(&var_);
Set(var);
}
return *this;
}
bool ScopedVariant::IsLeakableVarType(VARTYPE vt) {
bool leakable = false;
switch (vt & VT_TYPEMASK) {
case VT_BSTR:
case VT_DISPATCH:
// we treat VT_VARIANT as leakable to err on the safe side.
case VT_VARIANT:
case VT_UNKNOWN:
case VT_SAFEARRAY:
// very rarely used stuff (if ever):
case VT_VOID:
case VT_PTR:
case VT_CARRAY:
case VT_USERDEFINED:
case VT_LPSTR:
case VT_LPWSTR:
case VT_RECORD:
case VT_INT_PTR:
case VT_UINT_PTR:
case VT_FILETIME:
case VT_BLOB:
case VT_STREAM:
case VT_STORAGE:
case VT_STREAMED_OBJECT:
case VT_STORED_OBJECT:
case VT_BLOB_OBJECT:
case VT_VERSIONED_STREAM:
case VT_BSTR_BLOB:
leakable = true;
break;
}
if (!leakable && (vt & VT_ARRAY) != 0) {
leakable = true;
}
return leakable;
}
} // namespace win
} // namespace base
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
// Copyright 2016 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 GFX_RANGE_EXPORT_H_
#define GFX_RANGE_EXPORT_H_
#if defined(COMPONENT_BUILD)
#if defined(WIN32)
#if defined(GFX_RANGE_IMPLEMENTATION)
#define GFX_RANGE_EXPORT __declspec(dllexport)
#else
#define GFX_RANGE_EXPORT __declspec(dllimport)
#endif // defined(GFX_RANGE_IMPLEMENTATION)
#else // defined(WIN32)
#if defined(GFX_RANGE_IMPLEMENTATION)
#define GFX_RANGE_EXPORT __attribute__((visibility("default")))
#else
#define GFX_RANGE_EXPORT
#endif
#endif
#else // defined(COMPONENT_BUILD)
#define GFX_RANGE_EXPORT
#endif
#endif // GFX_RANGE_EXPORT_H_
// 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 "ui/gfx/range/range.h"
#include <inttypes.h>
#include <algorithm>
#include "base/strings/stringprintf.h"
namespace gfx {
std::string Range::ToString() const {
return base::StringPrintf("{%" PRIu32 ",%" PRIu32 "}", start(), end());
}
std::ostream& operator<<(std::ostream& os, const Range& range) {
return os << range.ToString();
}
} // namespace gfx
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册