diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /accessible/ipc/win/handler/AccessibleHandlerControl.cpp | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | accessible/ipc/win/handler/AccessibleHandlerControl.cpp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/accessible/ipc/win/handler/AccessibleHandlerControl.cpp b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp new file mode 100644 index 0000000000..0d0bd4d71d --- /dev/null +++ b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(MOZILLA_INTERNAL_API) +# error This code is NOT for internal Gecko use! +#endif // defined(MOZILLA_INTERNAL_API) + +#include "AccessibleHandlerControl.h" + +#include <utility> + +#include "AccessibleEventId.h" +#include "AccessibleHandler.h" +#include "mozilla/RefPtr.h" + +namespace mozilla { +namespace a11y { + +mscom::SingletonFactory<AccessibleHandlerControl> gControlFactory; + +namespace detail { + +TextChange::TextChange() : mIA2UniqueId(0), mIsInsert(false), mText() {} + +TextChange::TextChange(long aIA2UniqueId, bool aIsInsert, + NotNull<IA2TextSegment*> aText) + : mIA2UniqueId(aIA2UniqueId), + mIsInsert(aIsInsert), + mText{BSTRCopy(aText->text), aText->start, aText->end} {} + +TextChange::TextChange(TextChange&& aOther) : mText() { + *this = std::move(aOther); +} + +TextChange::TextChange(const TextChange& aOther) : mText() { *this = aOther; } + +TextChange& TextChange::operator=(TextChange&& aOther) { + mIA2UniqueId = aOther.mIA2UniqueId; + mIsInsert = aOther.mIsInsert; + aOther.mIA2UniqueId = 0; + ::SysFreeString(mText.text); + mText = aOther.mText; + aOther.mText.text = nullptr; + return *this; +} + +TextChange& TextChange::operator=(const TextChange& aOther) { + mIA2UniqueId = aOther.mIA2UniqueId; + mIsInsert = aOther.mIsInsert; + ::SysFreeString(mText.text); + mText = {BSTRCopy(aOther.mText.text), aOther.mText.start, aOther.mText.end}; + return *this; +} + +TextChange::~TextChange() { ::SysFreeString(mText.text); } + +HRESULT +TextChange::GetOld(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldSegment) { + if (mIsInsert || aIA2UniqueId != mIA2UniqueId) { + return S_OK; + } + + return SegCopy(*aOutOldSegment, mText); +} + +HRESULT +TextChange::GetNew(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewSegment) { + if (!mIsInsert || aIA2UniqueId != mIA2UniqueId) { + return S_OK; + } + + return SegCopy(*aOutNewSegment, mText); +} + +/* static */ +BSTR TextChange::BSTRCopy(const BSTR& aIn) { + return ::SysAllocStringLen(aIn, ::SysStringLen(aIn)); +} + +/* static */ +HRESULT TextChange::SegCopy(IA2TextSegment& aDest, const IA2TextSegment& aSrc) { + aDest = {BSTRCopy(aSrc.text), aSrc.start, aSrc.end}; + if (aSrc.text && !aDest.text) { + return E_OUTOFMEMORY; + } + if (!::SysStringLen(aDest.text)) { + return S_FALSE; + } + return S_OK; +} + +} // namespace detail + +HRESULT +AccessibleHandlerControl::Create(AccessibleHandlerControl** aOutObject) { + if (!aOutObject) { + return E_INVALIDARG; + } + + RefPtr<AccessibleHandlerControl> ctl(new AccessibleHandlerControl()); + ctl.forget(aOutObject); + return S_OK; +} + +AccessibleHandlerControl::AccessibleHandlerControl() + : mIsRegistered(false), + mCacheGen(0), + mIA2Proxy(mscom::RegisterProxy(L"ia2marshal.dll")), + mHandlerProxy(mscom::RegisterProxy()) { + MOZ_ASSERT(mIA2Proxy); +} + +IMPL_IUNKNOWN1(AccessibleHandlerControl, IHandlerControl) + +HRESULT +AccessibleHandlerControl::Invalidate() { + ++mCacheGen; + // We can't just call mAccessibleCache.clear() because doing so would release + // remote objects, making remote COM calls. Since this is an STA, an incoming + // COM call might be handled which might marshal an AccessibleHandler, + // which in turn might add itself to mAccessibleCache. Since we'd be in the + // middle of mutating mAccessibleCache, that might cause a crash. Instead, + // swap mAccessibleCache into a temporary map first, which will empty + // mAccessibleCache without releasing remote objects. Once mAccessibleCache + // is empty, it's safe to let the temporary map be destroyed when it goes + // out of scope. Remote calls will be made, but nothing will re-enter + // the temporary map while it's being destroyed. + AccessibleCache oldCache; + mAccessibleCache.swap(oldCache); + return S_OK; +} + +HRESULT +AccessibleHandlerControl::OnTextChange(long aHwnd, long aIA2UniqueId, + VARIANT_BOOL aIsInsert, + IA2TextSegment* aText) { + if (!aText) { + return E_INVALIDARG; + } + + mTextChange = detail::TextChange(aIA2UniqueId, aIsInsert, WrapNotNull(aText)); + NotifyWinEvent(aIsInsert ? IA2_EVENT_TEXT_INSERTED : IA2_EVENT_TEXT_REMOVED, + reinterpret_cast<HWND>(static_cast<uintptr_t>(aHwnd)), + OBJID_CLIENT, aIA2UniqueId); + return S_OK; +} + +HRESULT +AccessibleHandlerControl::GetNewText(long aIA2UniqueId, + NotNull<IA2TextSegment*> aOutNewText) { + return mTextChange.GetNew(aIA2UniqueId, aOutNewText); +} + +HRESULT +AccessibleHandlerControl::GetOldText(long aIA2UniqueId, + NotNull<IA2TextSegment*> aOutOldText) { + return mTextChange.GetOld(aIA2UniqueId, aOutOldText); +} + +HRESULT +AccessibleHandlerControl::GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo) { + if (!mHandlerProxy) { + return E_UNEXPECTED; + } + + return mHandlerProxy->GetTypeInfoForGuid(CLSID_AccessibleHandler, + aOutTypeInfo); +} + +HRESULT +AccessibleHandlerControl::Register(NotNull<IGeckoBackChannel*> aGecko) { + if (mIsRegistered) { + return S_OK; + } + + long pid = static_cast<long>(::GetCurrentProcessId()); + HRESULT hr = aGecko->put_HandlerControl(pid, this); + mIsRegistered = SUCCEEDED(hr); + MOZ_ASSERT(mIsRegistered); + return hr; +} + +void AccessibleHandlerControl::CacheAccessible(long aUniqueId, + AccessibleHandler* aAccessible) { + MOZ_ASSERT(aUniqueId && aAccessible); + mAccessibleCache[aUniqueId] = aAccessible; +} + +HRESULT AccessibleHandlerControl::GetCachedAccessible( + long aUniqueId, AccessibleHandler** aAccessible) { + MOZ_ASSERT(aUniqueId && aAccessible); + auto it = mAccessibleCache.find(aUniqueId); + if (it == mAccessibleCache.end()) { + return E_INVALIDARG; + } + RefPtr<AccessibleHandler> ref = it->second; + ref.forget(aAccessible); + return S_OK; +} + +HRESULT AccessibleHandlerControl::SuppressA11yForClipboardCopy() { + mA11yClipboardCopySuppressionStartTime = ::GetTickCount(); + return S_OK; +} + +bool AccessibleHandlerControl::IsA11ySuppressedForClipboardCopy() { + // Must be kept in sync with kSuppressTimeout in + // accessible/windows/msaa/Compatibility.cpp. + constexpr DWORD kSuppressTimeout = 1500; // ms + if (!mA11yClipboardCopySuppressionStartTime) { + return false; + } + return ::GetTickCount() - mA11yClipboardCopySuppressionStartTime < + kSuppressTimeout; +} + +} // namespace a11y +} // namespace mozilla |