michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 "nsBoxObject.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsView.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsIDOMXULElement.h" michael@0: #else michael@0: #include "nsIDOMElement.h" michael@0: #endif michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsSupportsPrimitives.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: // Implementation ///////////////////////////////////////////////////////////////// michael@0: michael@0: // Static member variable initialization michael@0: michael@0: // Implement our nsISupports methods michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBoxObject) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBoxObject) michael@0: michael@0: DOMCI_DATA(BoxObject, nsBoxObject) michael@0: michael@0: // QueryInterface implementation for nsBoxObject michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBoxObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsIBoxObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsPIBoxObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BoxObject) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: static PLDHashOperator michael@0: PropertyTraverser(const nsAString& aKey, nsISupports* aProperty, void* userArg) michael@0: { michael@0: nsCycleCollectionTraversalCallback *cb = michael@0: static_cast(userArg); michael@0: michael@0: cb->NoteXPCOMChild(aProperty); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsBoxObject) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsBoxObject) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBoxObject) michael@0: if (tmp->mPropertyTable) { michael@0: tmp->mPropertyTable->EnumerateRead(PropertyTraverser, &cb); michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: // Constructors/Destructors michael@0: nsBoxObject::nsBoxObject(void) michael@0: :mContent(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsBoxObject::~nsBoxObject(void) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetElement(nsIDOMElement** aResult) michael@0: { michael@0: if (mContent) { michael@0: return CallQueryInterface(mContent, aResult); michael@0: } michael@0: michael@0: *aResult = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsPIBoxObject ////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: nsBoxObject::Init(nsIContent* aContent) michael@0: { michael@0: mContent = aContent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsBoxObject::Clear() michael@0: { michael@0: mPropertyTable = nullptr; michael@0: mContent = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsBoxObject::ClearCachedValues() michael@0: { michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsBoxObject::GetFrame(bool aFlushLayout) michael@0: { michael@0: nsIPresShell* shell = GetPresShell(aFlushLayout); michael@0: if (!shell) michael@0: return nullptr; michael@0: michael@0: if (!aFlushLayout) { michael@0: // If we didn't flush layout when getting the presshell, we should at least michael@0: // flush to make sure our frame model is up to date. michael@0: // XXXbz should flush on document, no? Except people call this from michael@0: // frame code, maybe? michael@0: shell->FlushPendingNotifications(Flush_Frames); michael@0: } michael@0: michael@0: // The flush might have killed mContent. michael@0: if (!mContent) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mContent->GetPrimaryFrame(); michael@0: } michael@0: michael@0: nsIPresShell* michael@0: nsBoxObject::GetPresShell(bool aFlushLayout) michael@0: { michael@0: if (!mContent) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr doc = mContent->GetCurrentDoc(); michael@0: if (!doc) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aFlushLayout) { michael@0: doc->FlushPendingNotifications(Flush_Layout); michael@0: } michael@0: michael@0: return doc->GetShell(); michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxObject::GetOffsetRect(nsIntRect& aRect) michael@0: { michael@0: aRect.SetRect(0, 0, 0, 0); michael@0: michael@0: if (!mContent) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: // Get the Frame for our content michael@0: nsIFrame* frame = GetFrame(true); michael@0: if (frame) { michael@0: // Get its origin michael@0: nsPoint origin = frame->GetPositionIgnoringScrolling(); michael@0: michael@0: // Find the frame parent whose content is the document element. michael@0: Element *docElement = mContent->GetCurrentDoc()->GetRootElement(); michael@0: nsIFrame* parent = frame->GetParent(); michael@0: for (;;) { michael@0: // If we've hit the document element, break here michael@0: if (parent->GetContent() == docElement) { michael@0: break; michael@0: } michael@0: michael@0: nsIFrame* next = parent->GetParent(); michael@0: if (!next) { michael@0: NS_WARNING("We should have hit the document element..."); michael@0: origin += parent->GetPosition(); michael@0: break; michael@0: } michael@0: michael@0: // Add the parent's origin to our own to get to the michael@0: // right coordinate system michael@0: origin += next->GetPositionOfChildIgnoringScrolling(parent); michael@0: parent = next; michael@0: } michael@0: michael@0: // For the origin, add in the border for the frame michael@0: const nsStyleBorder* border = frame->StyleBorder(); michael@0: origin.x += border->GetComputedBorderWidth(NS_SIDE_LEFT); michael@0: origin.y += border->GetComputedBorderWidth(NS_SIDE_TOP); michael@0: michael@0: // And subtract out the border for the parent michael@0: const nsStyleBorder* parentBorder = parent->StyleBorder(); michael@0: origin.x -= parentBorder->GetComputedBorderWidth(NS_SIDE_LEFT); michael@0: origin.y -= parentBorder->GetComputedBorderWidth(NS_SIDE_TOP); michael@0: michael@0: aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); michael@0: aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); michael@0: michael@0: // Get the union of all rectangles in this and continuation frames. michael@0: // It doesn't really matter what we use as aRelativeTo here, since michael@0: // we only care about the size. Using 'parent' might make things michael@0: // a bit faster by speeding up the internal GetOffsetTo operations. michael@0: nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, parent); michael@0: aRect.width = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.width); michael@0: aRect.height = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.height); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxObject::GetScreenPosition(nsIntPoint& aPoint) michael@0: { michael@0: aPoint.x = aPoint.y = 0; michael@0: michael@0: if (!mContent) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsIFrame* frame = GetFrame(true); michael@0: if (frame) { michael@0: nsIntRect rect = frame->GetScreenRect(); michael@0: aPoint.x = rect.x; michael@0: aPoint.y = rect.y; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetX(int32_t* aResult) michael@0: { michael@0: nsIntRect rect; michael@0: GetOffsetRect(rect); michael@0: *aResult = rect.x; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetY(int32_t* aResult) michael@0: { michael@0: nsIntRect rect; michael@0: GetOffsetRect(rect); michael@0: *aResult = rect.y; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetWidth(int32_t* aResult) michael@0: { michael@0: nsIntRect rect; michael@0: GetOffsetRect(rect); michael@0: *aResult = rect.width; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetHeight(int32_t* aResult) michael@0: { michael@0: nsIntRect rect; michael@0: GetOffsetRect(rect); michael@0: *aResult = rect.height; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetScreenX(int32_t *_retval) michael@0: { michael@0: nsIntPoint position; michael@0: nsresult rv = GetScreenPosition(position); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *_retval = position.x; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetScreenY(int32_t *_retval) michael@0: { michael@0: nsIntPoint position; michael@0: nsresult rv = GetScreenPosition(position); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *_retval = position.y; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetPropertyAsSupports(const char16_t* aPropertyName, nsISupports** aResult) michael@0: { michael@0: NS_ENSURE_ARG(aPropertyName && *aPropertyName); michael@0: if (!mPropertyTable) { michael@0: *aResult = nullptr; michael@0: return NS_OK; michael@0: } michael@0: nsDependentString propertyName(aPropertyName); michael@0: mPropertyTable->Get(propertyName, aResult); // Addref here. michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::SetPropertyAsSupports(const char16_t* aPropertyName, nsISupports* aValue) michael@0: { michael@0: NS_ENSURE_ARG(aPropertyName && *aPropertyName); michael@0: michael@0: if (!mPropertyTable) { michael@0: mPropertyTable = new nsInterfaceHashtable(8); michael@0: } michael@0: michael@0: nsDependentString propertyName(aPropertyName); michael@0: mPropertyTable->Put(propertyName, aValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetProperty(const char16_t* aPropertyName, char16_t** aResult) michael@0: { michael@0: nsCOMPtr data; michael@0: nsresult rv = GetPropertyAsSupports(aPropertyName,getter_AddRefs(data)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!data) { michael@0: *aResult = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr supportsStr = do_QueryInterface(data); michael@0: if (!supportsStr) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return supportsStr->ToString(aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::SetProperty(const char16_t* aPropertyName, const char16_t* aPropertyValue) michael@0: { michael@0: NS_ENSURE_ARG(aPropertyName && *aPropertyName); michael@0: michael@0: nsDependentString propertyName(aPropertyName); michael@0: nsDependentString propertyValue; michael@0: if (aPropertyValue) { michael@0: propertyValue.Rebind(aPropertyValue); michael@0: } else { michael@0: propertyValue.SetIsVoid(true); michael@0: } michael@0: michael@0: nsCOMPtr supportsStr(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); michael@0: NS_ENSURE_TRUE(supportsStr, NS_ERROR_OUT_OF_MEMORY); michael@0: supportsStr->SetData(propertyValue); michael@0: michael@0: return SetPropertyAsSupports(aPropertyName,supportsStr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::RemoveProperty(const char16_t* aPropertyName) michael@0: { michael@0: NS_ENSURE_ARG(aPropertyName && *aPropertyName); michael@0: michael@0: if (!mPropertyTable) return NS_OK; michael@0: michael@0: nsDependentString propertyName(aPropertyName); michael@0: mPropertyTable->Remove(propertyName); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetParentBox(nsIDOMElement * *aParentBox) michael@0: { michael@0: *aParentBox = nullptr; michael@0: nsIFrame* frame = GetFrame(false); michael@0: if (!frame) return NS_OK; michael@0: nsIFrame* parent = frame->GetParent(); michael@0: if (!parent) return NS_OK; michael@0: michael@0: nsCOMPtr el = do_QueryInterface(parent->GetContent()); michael@0: *aParentBox = el; michael@0: NS_IF_ADDREF(*aParentBox); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetFirstChild(nsIDOMElement * *aFirstVisibleChild) michael@0: { michael@0: *aFirstVisibleChild = nullptr; michael@0: nsIFrame* frame = GetFrame(false); michael@0: if (!frame) return NS_OK; michael@0: nsIFrame* firstFrame = frame->GetFirstPrincipalChild(); michael@0: if (!firstFrame) return NS_OK; michael@0: // get the content for the box and query to a dom element michael@0: nsCOMPtr el = do_QueryInterface(firstFrame->GetContent()); michael@0: el.swap(*aFirstVisibleChild); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetLastChild(nsIDOMElement * *aLastVisibleChild) michael@0: { michael@0: *aLastVisibleChild = nullptr; michael@0: nsIFrame* frame = GetFrame(false); michael@0: if (!frame) return NS_OK; michael@0: return GetPreviousSibling(frame, nullptr, aLastVisibleChild); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetNextSibling(nsIDOMElement **aNextOrdinalSibling) michael@0: { michael@0: *aNextOrdinalSibling = nullptr; michael@0: nsIFrame* frame = GetFrame(false); michael@0: if (!frame) return NS_OK; michael@0: nsIFrame* nextFrame = frame->GetNextSibling(); michael@0: if (!nextFrame) return NS_OK; michael@0: // get the content for the box and query to a dom element michael@0: nsCOMPtr el = do_QueryInterface(nextFrame->GetContent()); michael@0: el.swap(*aNextOrdinalSibling); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBoxObject::GetPreviousSibling(nsIDOMElement **aPreviousOrdinalSibling) michael@0: { michael@0: *aPreviousOrdinalSibling = nullptr; michael@0: nsIFrame* frame = GetFrame(false); michael@0: if (!frame) return NS_OK; michael@0: nsIFrame* parentFrame = frame->GetParent(); michael@0: if (!parentFrame) return NS_OK; michael@0: return GetPreviousSibling(parentFrame, frame, aPreviousOrdinalSibling); michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame, michael@0: nsIDOMElement** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: nsIFrame* nextFrame = aParentFrame->GetFirstPrincipalChild(); michael@0: nsIFrame* prevFrame = nullptr; michael@0: while (nextFrame) { michael@0: if (nextFrame == aFrame) michael@0: break; michael@0: prevFrame = nextFrame; michael@0: nextFrame = nextFrame->GetNextSibling(); michael@0: } michael@0: michael@0: if (!prevFrame) return NS_OK; michael@0: // get the content for the box and query to a dom element michael@0: nsCOMPtr el = do_QueryInterface(prevFrame->GetContent()); michael@0: el.swap(*aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Creation Routine /////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: NS_NewBoxObject(nsIBoxObject** aResult) michael@0: { michael@0: *aResult = new nsBoxObject; michael@0: if (!*aResult) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: