michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "sdnTextAccessible.h" michael@0: michael@0: #include "ISimpleDOMText_i.c" michael@0: michael@0: #include "nsCoreUtils.h" michael@0: #include "DocAccessible.h" michael@0: michael@0: #include "nsIFrame.h" michael@0: #include "nsFontMetrics.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "gfxFont.h" michael@0: #include "nsIAccessibleTypes.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: using namespace mozilla::a11y; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // sdnTextAccessible michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: IMPL_IUNKNOWN_QUERY_HEAD(sdnTextAccessible) michael@0: IMPL_IUNKNOWN_QUERY_IFACE(ISimpleDOMText) michael@0: IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAccessible) michael@0: michael@0: STDMETHODIMP michael@0: sdnTextAccessible::get_domText(BSTR __RPC_FAR* aText) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aText) michael@0: return E_INVALIDARG; michael@0: *aText = nullptr; michael@0: michael@0: if (mAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsAutoString nodeValue; michael@0: michael@0: nsCOMPtr DOMNode(do_QueryInterface(mAccessible->GetContent())); michael@0: DOMNode->GetNodeValue(nodeValue); michael@0: if (nodeValue.IsEmpty()) michael@0: return S_FALSE; michael@0: michael@0: *aText = ::SysAllocStringLen(nodeValue.get(), nodeValue.Length()); michael@0: return *aText ? S_OK : E_OUTOFMEMORY; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnTextAccessible::get_clippedSubstringBounds(unsigned int aStartIndex, michael@0: unsigned int aEndIndex, michael@0: int __RPC_FAR* aX, michael@0: int __RPC_FAR* aY, michael@0: int __RPC_FAR* aWidth, michael@0: int __RPC_FAR* aHeight) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: nscoord x = 0, y = 0, width = 0, height = 0; michael@0: HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex, michael@0: &x, &y, &width, &height); michael@0: if (FAILED(rv)) michael@0: return rv; michael@0: michael@0: DocAccessible* document = mAccessible->Document(); michael@0: NS_ASSERTION(document, michael@0: "There must always be a doc accessible, but there isn't. Crash!"); michael@0: michael@0: nscoord docX = 0, docY = 0, docWidth = 0, docHeight = 0; michael@0: document->GetBounds(&docX, &docY, &docWidth, &docHeight); michael@0: michael@0: nsIntRect unclippedRect(x, y, width, height); michael@0: nsIntRect docRect(docX, docY, docWidth, docHeight); michael@0: michael@0: nsIntRect clippedRect; michael@0: clippedRect.IntersectRect(unclippedRect, docRect); michael@0: michael@0: *aX = clippedRect.x; michael@0: *aY = clippedRect.y; michael@0: *aWidth = clippedRect.width; michael@0: *aHeight = clippedRect.height; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnTextAccessible::get_unclippedSubstringBounds(unsigned int aStartIndex, michael@0: unsigned int aEndIndex, michael@0: int __RPC_FAR* aX, michael@0: int __RPC_FAR* aY, michael@0: int __RPC_FAR* aWidth, michael@0: int __RPC_FAR* aHeight) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aX || !aY || !aWidth || !aHeight) michael@0: return E_INVALIDARG; michael@0: *aX = *aY = *aWidth = *aHeight = 0; michael@0: michael@0: if (mAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsIFrame *frame = mAccessible->GetFrame(); michael@0: NS_ENSURE_TRUE(frame, E_FAIL); michael@0: michael@0: nsPoint startPoint, endPoint; michael@0: nsIFrame* startFrame = GetPointFromOffset(frame, aStartIndex, true, startPoint); michael@0: nsIFrame* endFrame = GetPointFromOffset(frame, aEndIndex, false, endPoint); michael@0: if (!startFrame || !endFrame) michael@0: return E_FAIL; michael@0: michael@0: nsRect sum; michael@0: nsIFrame* iter = startFrame; michael@0: nsIFrame* stopLoopFrame = endFrame->GetNextContinuation(); michael@0: for (; iter != stopLoopFrame; iter = iter->GetNextContinuation()) { michael@0: nsRect rect = iter->GetScreenRectInAppUnits(); michael@0: nscoord start = (iter == startFrame) ? startPoint.x : 0; michael@0: nscoord end = (iter == endFrame) ? endPoint.x : rect.width; michael@0: rect.x += start; michael@0: rect.width = end - start; michael@0: sum.UnionRect(sum, rect); michael@0: } michael@0: michael@0: nsPresContext* presContext = mAccessible->Document()->PresContext(); michael@0: *aX = presContext->AppUnitsToDevPixels(sum.x); michael@0: *aY = presContext->AppUnitsToDevPixels(sum.y); michael@0: *aWidth = presContext->AppUnitsToDevPixels(sum.width); michael@0: *aHeight = presContext->AppUnitsToDevPixels(sum.height); michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnTextAccessible::scrollToSubstring(unsigned int aStartIndex, michael@0: unsigned int aEndIndex) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (mAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsRefPtr range = new nsRange(mAccessible->GetContent()); michael@0: if (NS_FAILED(range->SetStart(mAccessible->GetContent(), aStartIndex))) michael@0: return E_FAIL; michael@0: michael@0: if (NS_FAILED(range->SetEnd(mAccessible->GetContent(), aEndIndex))) michael@0: return E_FAIL; michael@0: michael@0: nsresult rv = michael@0: nsCoreUtils::ScrollSubstringTo(mAccessible->GetFrame(), range, michael@0: nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE); michael@0: return GetHRESULT(rv); michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnTextAccessible::get_fontFamily(BSTR __RPC_FAR* aFontFamily) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aFontFamily) michael@0: return E_INVALIDARG; michael@0: *aFontFamily = nullptr; michael@0: michael@0: if (mAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsIFrame* frame = mAccessible->GetFrame(); michael@0: if (!frame) michael@0: return E_FAIL; michael@0: michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(frame, getter_AddRefs(fm)); michael@0: michael@0: const nsString& name = fm->GetThebesFontGroup()->GetFontAt(0)->GetName(); michael@0: if (name.IsEmpty()) michael@0: return S_FALSE; michael@0: michael@0: *aFontFamily = ::SysAllocStringLen(name.get(), name.Length()); michael@0: return *aFontFamily ? S_OK : E_OUTOFMEMORY; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: nsIFrame* michael@0: sdnTextAccessible::GetPointFromOffset(nsIFrame* aContainingFrame, michael@0: int32_t aOffset, michael@0: bool aPreferNext, michael@0: nsPoint& aOutPoint) michael@0: { michael@0: nsIFrame* textFrame = nullptr; michael@0: int32_t outOffset; michael@0: aContainingFrame->GetChildFrameContainingOffset(aOffset, aPreferNext, michael@0: &outOffset, &textFrame); michael@0: if (textFrame) michael@0: textFrame->GetPointFromOffset(aOffset, &aOutPoint); michael@0: michael@0: return textFrame; michael@0: }