/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=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/. */ #include "sdnTextAccessible.h" #include "ISimpleDOM.h" #include "nsCoreUtils.h" #include "DocAccessible.h" #include "nsIFrame.h" #include "nsFontMetrics.h" #include "nsPresContext.h" #include "nsLayoutUtils.h" #include "nsRange.h" #include "gfxTextRun.h" #include "nsIAccessibleTypes.h" #include "mozilla/gfx/2D.h" using namespace mozilla::a11y; //////////////////////////////////////////////////////////////////////////////// // sdnTextAccessible //////////////////////////////////////////////////////////////////////////////// IMPL_IUNKNOWN_QUERY_HEAD(sdnTextAccessible) IMPL_IUNKNOWN_QUERY_IFACE(ISimpleDOMText) IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mMsaa) STDMETHODIMP sdnTextAccessible::get_domText(BSTR __RPC_FAR* aText) { if (!aText) return E_INVALIDARG; *aText = nullptr; AccessibleWrap* acc = mMsaa->LocalAcc(); if (!acc) return CO_E_OBJNOTCONNECTED; nsAutoString nodeValue; acc->GetContent()->GetNodeValue(nodeValue); if (nodeValue.IsEmpty()) return S_FALSE; *aText = ::SysAllocStringLen(nodeValue.get(), nodeValue.Length()); return *aText ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP sdnTextAccessible::get_clippedSubstringBounds( unsigned int aStartIndex, unsigned int aEndIndex, int __RPC_FAR* aX, int __RPC_FAR* aY, int __RPC_FAR* aWidth, int __RPC_FAR* aHeight) { nscoord x = 0, y = 0, width = 0, height = 0; HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex, &x, &y, &width, &height); if (FAILED(rv)) return rv; DocAccessible* document = mMsaa->LocalAcc()->Document(); NS_ASSERTION( document, "There must always be a doc accessible, but there isn't. Crash!"); LayoutDeviceIntRect docRect = document->Bounds(); LayoutDeviceIntRect unclippedRect(x, y, width, height); LayoutDeviceIntRect clippedRect; clippedRect.IntersectRect(unclippedRect, docRect); *aX = clippedRect.X(); *aY = clippedRect.Y(); *aWidth = clippedRect.Width(); *aHeight = clippedRect.Height(); return S_OK; } STDMETHODIMP sdnTextAccessible::get_unclippedSubstringBounds( unsigned int aStartIndex, unsigned int aEndIndex, int __RPC_FAR* aX, int __RPC_FAR* aY, int __RPC_FAR* aWidth, int __RPC_FAR* aHeight) { if (!aX || !aY || !aWidth || !aHeight) return E_INVALIDARG; *aX = *aY = *aWidth = *aHeight = 0; AccessibleWrap* acc = mMsaa->LocalAcc(); if (!acc) return CO_E_OBJNOTCONNECTED; nsIFrame* frame = acc->GetFrame(); NS_ENSURE_TRUE(frame, E_FAIL); nsPoint startPoint, endPoint; nsIFrame* startFrame = GetPointFromOffset(frame, aStartIndex, true, startPoint); nsIFrame* endFrame = GetPointFromOffset(frame, aEndIndex, false, endPoint); if (!startFrame || !endFrame) return E_FAIL; nsRect sum; nsIFrame* iter = startFrame; nsIFrame* stopLoopFrame = endFrame->GetNextContinuation(); for (; iter != stopLoopFrame; iter = iter->GetNextContinuation()) { nsRect rect = iter->GetScreenRectInAppUnits(); nscoord start = (iter == startFrame) ? startPoint.x : 0; nscoord end = (iter == endFrame) ? endPoint.x : rect.Width(); rect.MoveByX(start); rect.SetWidth(end - start); sum.UnionRect(sum, rect); } nsPresContext* presContext = acc->Document()->PresContext(); *aX = presContext->AppUnitsToDevPixels(sum.X()); *aY = presContext->AppUnitsToDevPixels(sum.Y()); *aWidth = presContext->AppUnitsToDevPixels(sum.Width()); *aHeight = presContext->AppUnitsToDevPixels(sum.Height()); return S_OK; } STDMETHODIMP sdnTextAccessible::scrollToSubstring(unsigned int aStartIndex, unsigned int aEndIndex) { AccessibleWrap* acc = mMsaa->LocalAcc(); if (!acc) return CO_E_OBJNOTCONNECTED; RefPtr range = nsRange::Create(acc->GetContent()); if (NS_FAILED(range->SetStart(acc->GetContent(), aStartIndex))) return E_FAIL; if (NS_FAILED(range->SetEnd(acc->GetContent(), aEndIndex))) return E_FAIL; nsresult rv = nsCoreUtils::ScrollSubstringTo( acc->GetFrame(), range, nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE); return GetHRESULT(rv); } STDMETHODIMP sdnTextAccessible::get_fontFamily(BSTR __RPC_FAR* aFontFamily) { if (!aFontFamily) return E_INVALIDARG; *aFontFamily = nullptr; AccessibleWrap* acc = mMsaa->LocalAcc(); if (!acc) return CO_E_OBJNOTCONNECTED; nsIFrame* frame = acc->GetFrame(); if (!frame) return E_FAIL; RefPtr fm = nsLayoutUtils::GetFontMetricsForFrame(frame, 1.0f); RefPtr font = fm->GetThebesFontGroup()->GetFirstValidFont(); const nsCString& name = font->GetName(); if (name.IsEmpty()) return S_FALSE; NS_ConvertUTF8toUTF16 str(name); *aFontFamily = ::SysAllocStringLen(str.get(), str.Length()); return *aFontFamily ? S_OK : E_OUTOFMEMORY; } nsIFrame* sdnTextAccessible::GetPointFromOffset(nsIFrame* aContainingFrame, int32_t aOffset, bool aPreferNext, nsPoint& aOutPoint) { nsIFrame* textFrame = nullptr; int32_t outOffset; aContainingFrame->GetChildFrameContainingOffset(aOffset, aPreferNext, &outOffset, &textFrame); if (textFrame) textFrame->GetPointFromOffset(aOffset, &aOutPoint); return textFrame; }