diff options
Diffstat (limited to 'layout/xul/BoxObject.cpp')
-rw-r--r-- | layout/xul/BoxObject.cpp | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/layout/xul/BoxObject.cpp b/layout/xul/BoxObject.cpp new file mode 100644 index 000000000..6636a6d62 --- /dev/null +++ b/layout/xul/BoxObject.cpp @@ -0,0 +1,614 @@ +/* -*- 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/. */ + +#include "mozilla/dom/BoxObject.h" +#include "nsCOMPtr.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "nsPresContext.h" +#include "nsIContent.h" +#include "nsContainerFrame.h" +#include "nsIDocShell.h" +#include "nsReadableUtils.h" +#include "nsDOMClassInfoID.h" +#include "nsView.h" +#ifdef MOZ_XUL +#include "nsIDOMXULElement.h" +#else +#include "nsIDOMElement.h" +#endif +#include "nsLayoutUtils.h" +#include "nsISupportsPrimitives.h" +#include "nsSupportsPrimitives.h" +#include "mozilla/dom/Element.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/dom/BoxObjectBinding.h" + +// Implementation ///////////////////////////////////////////////////////////////// + +namespace mozilla { +namespace dom { + +// Static member variable initialization + +// Implement our nsISupports methods +NS_IMPL_CYCLE_COLLECTION_CLASS(BoxObject) +NS_IMPL_CYCLE_COLLECTING_ADDREF(BoxObject) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BoxObject) + +// QueryInterface implementation for BoxObject +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BoxObject) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsIBoxObject) + NS_INTERFACE_MAP_ENTRY(nsPIBoxObject) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BoxObject) + // XXX jmorton: why aren't we unlinking mPropertyTable? + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BoxObject) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + if (tmp->mPropertyTable) { + for (auto iter = tmp->mPropertyTable->Iter(); !iter.Done(); iter.Next()) { + cb.NoteXPCOMChild(iter.UserData()); + } + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BoxObject) + +// Constructors/Destructors +BoxObject::BoxObject() + : mContent(nullptr) +{ +} + +BoxObject::~BoxObject() +{ +} + +NS_IMETHODIMP +BoxObject::GetElement(nsIDOMElement** aResult) +{ + if (mContent) { + return CallQueryInterface(mContent, aResult); + } + + *aResult = nullptr; + return NS_OK; +} + +// nsPIBoxObject ////////////////////////////////////////////////////////////////////////// + +nsresult +BoxObject::Init(nsIContent* aContent) +{ + mContent = aContent; + return NS_OK; +} + +void +BoxObject::Clear() +{ + mPropertyTable = nullptr; + mContent = nullptr; +} + +void +BoxObject::ClearCachedValues() +{ +} + +nsIFrame* +BoxObject::GetFrame(bool aFlushLayout) +{ + nsIPresShell* shell = GetPresShell(aFlushLayout); + if (!shell) + return nullptr; + + if (!aFlushLayout) { + // If we didn't flush layout when getting the presshell, we should at least + // flush to make sure our frame model is up to date. + // XXXbz should flush on document, no? Except people call this from + // frame code, maybe? + shell->FlushPendingNotifications(Flush_Frames); + } + + // The flush might have killed mContent. + if (!mContent) { + return nullptr; + } + + return mContent->GetPrimaryFrame(); +} + +nsIPresShell* +BoxObject::GetPresShell(bool aFlushLayout) +{ + if (!mContent) { + return nullptr; + } + + nsCOMPtr<nsIDocument> doc = mContent->GetUncomposedDoc(); + if (!doc) { + return nullptr; + } + + if (aFlushLayout) { + doc->FlushPendingNotifications(Flush_Layout); + } + + return doc->GetShell(); +} + +nsresult +BoxObject::GetOffsetRect(nsIntRect& aRect) +{ + aRect.SetRect(0, 0, 0, 0); + + if (!mContent) + return NS_ERROR_NOT_INITIALIZED; + + // Get the Frame for our content + nsIFrame* frame = GetFrame(true); + if (frame) { + // Get its origin + nsPoint origin = frame->GetPositionIgnoringScrolling(); + + // Find the frame parent whose content is the document element. + Element* docElement = mContent->GetComposedDoc()->GetRootElement(); + nsIFrame* parent = frame->GetParent(); + for (;;) { + // If we've hit the document element, break here + if (parent->GetContent() == docElement) { + break; + } + + nsIFrame* next = parent->GetParent(); + if (!next) { + NS_WARNING("We should have hit the document element..."); + origin += parent->GetPosition(); + break; + } + + // Add the parent's origin to our own to get to the + // right coordinate system + origin += next->GetPositionOfChildIgnoringScrolling(parent); + parent = next; + } + + // For the origin, add in the border for the frame + const nsStyleBorder* border = frame->StyleBorder(); + origin.x += border->GetComputedBorderWidth(NS_SIDE_LEFT); + origin.y += border->GetComputedBorderWidth(NS_SIDE_TOP); + + // And subtract out the border for the parent + const nsStyleBorder* parentBorder = parent->StyleBorder(); + origin.x -= parentBorder->GetComputedBorderWidth(NS_SIDE_LEFT); + origin.y -= parentBorder->GetComputedBorderWidth(NS_SIDE_TOP); + + aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); + aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); + + // Get the union of all rectangles in this and continuation frames. + // It doesn't really matter what we use as aRelativeTo here, since + // we only care about the size. Using 'parent' might make things + // a bit faster by speeding up the internal GetOffsetTo operations. + nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, parent); + aRect.width = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.width); + aRect.height = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.height); + } + + return NS_OK; +} + +nsresult +BoxObject::GetScreenPosition(nsIntPoint& aPoint) +{ + aPoint.x = aPoint.y = 0; + + if (!mContent) + return NS_ERROR_NOT_INITIALIZED; + + nsIFrame* frame = GetFrame(true); + if (frame) { + nsIntRect rect = frame->GetScreenRect(); + aPoint.x = rect.x; + aPoint.y = rect.y; + } + + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetX(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.x; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetY(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.y; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetWidth(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.width; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetHeight(int32_t* aResult) +{ + nsIntRect rect; + GetOffsetRect(rect); + *aResult = rect.height; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetScreenX(int32_t *_retval) +{ + nsIntPoint position; + nsresult rv = GetScreenPosition(position); + if (NS_FAILED(rv)) return rv; + *_retval = position.x; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetScreenY(int32_t *_retval) +{ + nsIntPoint position; + nsresult rv = GetScreenPosition(position); + if (NS_FAILED(rv)) return rv; + *_retval = position.y; + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetPropertyAsSupports(const char16_t* aPropertyName, nsISupports** aResult) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + if (!mPropertyTable) { + *aResult = nullptr; + return NS_OK; + } + nsDependentString propertyName(aPropertyName); + mPropertyTable->Get(propertyName, aResult); // Addref here. + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::SetPropertyAsSupports(const char16_t* aPropertyName, nsISupports* aValue) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + + if (!mPropertyTable) { + mPropertyTable = new nsInterfaceHashtable<nsStringHashKey,nsISupports>(4); + } + + nsDependentString propertyName(aPropertyName); + mPropertyTable->Put(propertyName, aValue); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetProperty(const char16_t* aPropertyName, char16_t** aResult) +{ + nsCOMPtr<nsISupports> data; + nsresult rv = GetPropertyAsSupports(aPropertyName,getter_AddRefs(data)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!data) { + *aResult = nullptr; + return NS_OK; + } + + nsCOMPtr<nsISupportsString> supportsStr = do_QueryInterface(data); + if (!supportsStr) { + return NS_ERROR_FAILURE; + } + + return supportsStr->ToString(aResult); +} + +NS_IMETHODIMP +BoxObject::SetProperty(const char16_t* aPropertyName, const char16_t* aPropertyValue) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + + nsDependentString propertyName(aPropertyName); + nsDependentString propertyValue; + if (aPropertyValue) { + propertyValue.Rebind(aPropertyValue); + } else { + propertyValue.SetIsVoid(true); + } + + nsCOMPtr<nsISupportsString> supportsStr(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); + NS_ENSURE_TRUE(supportsStr, NS_ERROR_OUT_OF_MEMORY); + supportsStr->SetData(propertyValue); + + return SetPropertyAsSupports(aPropertyName,supportsStr); +} + +NS_IMETHODIMP +BoxObject::RemoveProperty(const char16_t* aPropertyName) +{ + NS_ENSURE_ARG(aPropertyName && *aPropertyName); + + if (!mPropertyTable) return NS_OK; + + nsDependentString propertyName(aPropertyName); + mPropertyTable->Remove(propertyName); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetParentBox(nsIDOMElement * *aParentBox) +{ + *aParentBox = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* parent = frame->GetParent(); + if (!parent) return NS_OK; + + nsCOMPtr<nsIDOMElement> el = do_QueryInterface(parent->GetContent()); + *aParentBox = el; + NS_IF_ADDREF(*aParentBox); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetFirstChild(nsIDOMElement * *aFirstVisibleChild) +{ + *aFirstVisibleChild = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* firstFrame = frame->PrincipalChildList().FirstChild(); + if (!firstFrame) return NS_OK; + // get the content for the box and query to a dom element + nsCOMPtr<nsIDOMElement> el = do_QueryInterface(firstFrame->GetContent()); + el.swap(*aFirstVisibleChild); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetLastChild(nsIDOMElement * *aLastVisibleChild) +{ + *aLastVisibleChild = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + return GetPreviousSibling(frame, nullptr, aLastVisibleChild); +} + +NS_IMETHODIMP +BoxObject::GetNextSibling(nsIDOMElement **aNextOrdinalSibling) +{ + *aNextOrdinalSibling = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* nextFrame = frame->GetNextSibling(); + if (!nextFrame) return NS_OK; + // get the content for the box and query to a dom element + nsCOMPtr<nsIDOMElement> el = do_QueryInterface(nextFrame->GetContent()); + el.swap(*aNextOrdinalSibling); + return NS_OK; +} + +NS_IMETHODIMP +BoxObject::GetPreviousSibling(nsIDOMElement **aPreviousOrdinalSibling) +{ + *aPreviousOrdinalSibling = nullptr; + nsIFrame* frame = GetFrame(false); + if (!frame) return NS_OK; + nsIFrame* parentFrame = frame->GetParent(); + if (!parentFrame) return NS_OK; + return GetPreviousSibling(parentFrame, frame, aPreviousOrdinalSibling); +} + +nsresult +BoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame, + nsIDOMElement** aResult) +{ + *aResult = nullptr; + nsIFrame* nextFrame = aParentFrame->PrincipalChildList().FirstChild(); + nsIFrame* prevFrame = nullptr; + while (nextFrame) { + if (nextFrame == aFrame) + break; + prevFrame = nextFrame; + nextFrame = nextFrame->GetNextSibling(); + } + + if (!prevFrame) return NS_OK; + // get the content for the box and query to a dom element + nsCOMPtr<nsIDOMElement> el = do_QueryInterface(prevFrame->GetContent()); + el.swap(*aResult); + return NS_OK; +} + +nsIContent* +BoxObject::GetParentObject() const +{ + return mContent; +} + +JSObject* +BoxObject::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return BoxObjectBinding::Wrap(aCx, this, aGivenProto); +} + +Element* +BoxObject::GetElement() const +{ + return mContent && mContent->IsElement() ? mContent->AsElement() : nullptr; +} + +int32_t +BoxObject::X() +{ + int32_t ret = 0; + GetX(&ret); + return ret; +} + +int32_t +BoxObject::Y() +{ + int32_t ret = 0; + GetY(&ret); + return ret; +} + +int32_t +BoxObject::GetScreenX(ErrorResult& aRv) +{ + int32_t ret = 0; + aRv = GetScreenX(&ret); + return ret; +} + +int32_t +BoxObject::GetScreenY(ErrorResult& aRv) +{ + int32_t ret = 0; + aRv = GetScreenY(&ret); + return ret; +} + +int32_t +BoxObject::Width() +{ + int32_t ret = 0; + GetWidth(&ret); + return ret; +} + +int32_t +BoxObject::Height() +{ + int32_t ret = 0; + GetHeight(&ret); + return ret; +} + +already_AddRefed<nsISupports> +BoxObject::GetPropertyAsSupports(const nsAString& propertyName) +{ + nsCOMPtr<nsISupports> ret; + GetPropertyAsSupports(PromiseFlatString(propertyName).get(), getter_AddRefs(ret)); + return ret.forget(); +} + +void +BoxObject::SetPropertyAsSupports(const nsAString& propertyName, nsISupports* value) +{ + SetPropertyAsSupports(PromiseFlatString(propertyName).get(), value); +} + +void +BoxObject::GetProperty(const nsAString& propertyName, nsString& aRetVal, ErrorResult& aRv) +{ + nsCOMPtr<nsISupports> data(GetPropertyAsSupports(propertyName)); + if (!data) { + return; + } + + nsCOMPtr<nsISupportsString> supportsStr(do_QueryInterface(data)); + if (!supportsStr) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + supportsStr->GetData(aRetVal); +} + +void +BoxObject::SetProperty(const nsAString& propertyName, const nsAString& propertyValue) +{ + SetProperty(PromiseFlatString(propertyName).get(), PromiseFlatString(propertyValue).get()); +} + +void +BoxObject::RemoveProperty(const nsAString& propertyName) +{ + RemoveProperty(PromiseFlatString(propertyName).get()); +} + +already_AddRefed<Element> +BoxObject::GetParentBox() +{ + nsCOMPtr<nsIDOMElement> el; + GetParentBox(getter_AddRefs(el)); + nsCOMPtr<Element> ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed<Element> +BoxObject::GetFirstChild() +{ + nsCOMPtr<nsIDOMElement> el; + GetFirstChild(getter_AddRefs(el)); + nsCOMPtr<Element> ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed<Element> +BoxObject::GetLastChild() +{ + nsCOMPtr<nsIDOMElement> el; + GetLastChild(getter_AddRefs(el)); + nsCOMPtr<Element> ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed<Element> +BoxObject::GetNextSibling() +{ + nsCOMPtr<nsIDOMElement> el; + GetNextSibling(getter_AddRefs(el)); + nsCOMPtr<Element> ret(do_QueryInterface(el)); + return ret.forget(); +} + +already_AddRefed<Element> +BoxObject::GetPreviousSibling() +{ + nsCOMPtr<nsIDOMElement> el; + GetPreviousSibling(getter_AddRefs(el)); + nsCOMPtr<Element> ret(do_QueryInterface(el)); + return ret.forget(); +} + +} // namespace dom +} // namespace mozilla + +// Creation Routine /////////////////////////////////////////////////////////////////////// + +using namespace mozilla::dom; + +nsresult +NS_NewBoxObject(nsIBoxObject** aResult) +{ + NS_ADDREF(*aResult = new BoxObject()); + return NS_OK; +} |