/* -*- 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 "nsView.h" #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) 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(Element** aResult) { RefPtr element = mContent; element.forget(aResult); return NS_OK; } // nsPIBoxObject ////////////////////////////////////////////////////////////////////////// nsresult BoxObject::Init(Element* aElement) { mContent = aElement; 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(FlushType::Frames); } // The flush might have killed mContent. if (!mContent) { return nullptr; } return mContent->GetPrimaryFrame(); } nsIPresShell* BoxObject::GetPresShell(bool aFlushLayout) { if (!mContent) { return nullptr; } nsCOMPtr doc = mContent->GetComposedDoc(); if (!doc) { return nullptr; } if (aFlushLayout) { doc->FlushPendingNotifications(FlushType::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(eSideLeft); origin.y += border->GetComputedBorderWidth(eSideTop); // And subtract out the border for the parent const nsStyleBorder* parentBorder = parent->StyleBorder(); origin.x -= parentBorder->GetComputedBorderWidth(eSideLeft); origin.y -= parentBorder->GetComputedBorderWidth(eSideTop); 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) { CSSIntRect 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(4); } nsDependentString propertyName(aPropertyName); mPropertyTable->Put(propertyName, aValue); return NS_OK; } NS_IMETHODIMP BoxObject::GetProperty(const char16_t* aPropertyName, char16_t** aResult) { nsCOMPtr data; nsresult rv = GetPropertyAsSupports(aPropertyName,getter_AddRefs(data)); NS_ENSURE_SUCCESS(rv, rv); if (!data) { *aResult = nullptr; return NS_OK; } nsCOMPtr 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 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; } Element* BoxObject::GetParentBox() { nsIFrame* frame = GetFrame(false); if (!frame) { return nullptr; } nsIFrame* parent = frame->GetParent(); if (!parent) { return nullptr; } nsIContent* parentContent = parent->GetContent(); // In theory parent could be viewport, and then parentContent is null. if (parentContent && parentContent->IsElement()) { return parentContent->AsElement(); } return nullptr; } Element* BoxObject::GetFirstChild() { nsIFrame* frame = GetFrame(false); if (!frame) { return nullptr; } nsIFrame* firstFrame = frame->PrincipalChildList().FirstChild(); if (!firstFrame) { return nullptr; } nsIContent* content = firstFrame->GetContent(); if (content->IsElement()) { return content->AsElement(); } return nullptr; } Element* BoxObject::GetLastChild() { nsIFrame* frame = GetFrame(false); if (!frame) { return nullptr; } return GetPreviousSibling(frame, nullptr); } Element* BoxObject::GetNextSibling() { nsIFrame* frame = GetFrame(false); if (!frame) { return nullptr; } nsIFrame* nextFrame = frame->GetNextSibling(); if (!nextFrame) { return nullptr; } nsIContent* content = nextFrame->GetContent(); if (content->IsElement()) { return content->AsElement(); } return nullptr; } Element* BoxObject::GetPreviousSibling() { nsIFrame* frame = GetFrame(false); if (!frame) { return nullptr; } nsIFrame* parentFrame = frame->GetParent(); if (!parentFrame) { return nullptr; } return GetPreviousSibling(parentFrame, frame); } Element* BoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame) { nsIFrame* nextFrame = aParentFrame->PrincipalChildList().FirstChild(); nsIFrame* prevFrame = nullptr; while (nextFrame) { if (nextFrame == aFrame) break; prevFrame = nextFrame; nextFrame = nextFrame->GetNextSibling(); } if (!prevFrame) { return nullptr; } nsIContent* content = prevFrame->GetContent(); if (!content->IsElement()) { return nullptr; } return content->AsElement(); } Element* BoxObject::GetParentObject() const { return mContent; } JSObject* BoxObject::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return BoxObject_Binding::Wrap(aCx, this, aGivenProto); } Element* BoxObject::GetElement() const { return mContent; } 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 BoxObject::GetPropertyAsSupports(const nsAString& propertyName) { nsCOMPtr 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 data(GetPropertyAsSupports(propertyName)); if (!data) { return; } nsCOMPtr 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()); } } // namespace dom } // namespace mozilla // Creation Routine /////////////////////////////////////////////////////////////////////// using namespace mozilla::dom; nsresult NS_NewBoxObject(nsIBoxObject** aResult) { NS_ADDREF(*aResult = new BoxObject()); return NS_OK; }