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: #include "nsCOMPtr.h" michael@0: #include "nsIPopupBoxObject.h" michael@0: #include "nsIRootBox.h" michael@0: #include "nsBoxObject.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsMenuPopupFrame.h" michael@0: #include "nsView.h" michael@0: #include "mozilla/AppUnits.h" michael@0: #include "mozilla/dom/DOMRect.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: class nsPopupBoxObject : public nsBoxObject, michael@0: public nsIPopupBoxObject michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_NSIPOPUPBOXOBJECT michael@0: michael@0: nsPopupBoxObject() {} michael@0: protected: michael@0: virtual ~nsPopupBoxObject() {} michael@0: michael@0: nsPopupSetFrame* GetPopupSetFrame(); michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsPopupBoxObject, nsBoxObject, nsIPopupBoxObject) michael@0: michael@0: nsPopupSetFrame* michael@0: nsPopupBoxObject::GetPopupSetFrame() michael@0: { michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(GetPresShell(false)); michael@0: if (!rootBox) michael@0: return nullptr; michael@0: michael@0: return rootBox->GetPopupSetFrame(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::HidePopup() michael@0: { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm && mContent) michael@0: pm->HidePopup(mContent, false, true, false, false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::ShowPopup(nsIDOMElement* aAnchorElement, michael@0: nsIDOMElement* aPopupElement, michael@0: int32_t aXPos, int32_t aYPos, michael@0: const char16_t *aPopupType, michael@0: const char16_t *aAnchorAlignment, michael@0: const char16_t *aPopupAlignment) michael@0: { michael@0: NS_ENSURE_TRUE(aPopupElement, NS_ERROR_INVALID_ARG); michael@0: // srcContent can be null. michael@0: michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm && mContent) { michael@0: nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); michael@0: nsAutoString popupType(aPopupType); michael@0: nsAutoString anchor(aAnchorAlignment); michael@0: nsAutoString align(aPopupAlignment); michael@0: pm->ShowPopupWithAnchorAlign(mContent, anchorContent, anchor, align, michael@0: aXPos, aYPos, popupType.EqualsLiteral("context")); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::OpenPopup(nsIDOMElement* aAnchorElement, michael@0: const nsAString& aPosition, michael@0: int32_t aXPos, int32_t aYPos, michael@0: bool aIsContextMenu, michael@0: bool aAttributesOverride, michael@0: nsIDOMEvent* aTriggerEvent) michael@0: { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm && mContent) { michael@0: nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); michael@0: pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos, michael@0: aIsContextMenu, aAttributesOverride, false, aTriggerEvent); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos, michael@0: bool aIsContextMenu, michael@0: nsIDOMEvent* aTriggerEvent) michael@0: { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm && mContent) michael@0: pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu, aTriggerEvent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::MoveTo(int32_t aLeft, int32_t aTop) michael@0: { michael@0: nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; michael@0: if (menuPopupFrame) { michael@0: menuPopupFrame->MoveTo(aLeft, aTop, true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::MoveToAnchor(nsIDOMElement* aAnchorElement, michael@0: const nsAString& aPosition, michael@0: int32_t aXPos, int32_t aYPos, michael@0: bool aAttributesOverride) michael@0: { michael@0: if (mContent) { michael@0: nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); michael@0: michael@0: nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(mContent->GetPrimaryFrame()); michael@0: if (menuPopupFrame && menuPopupFrame->PopupState() == ePopupOpenAndVisible) { michael@0: menuPopupFrame->MoveToAnchor(anchorContent, aPosition, aXPos, aYPos, aAttributesOverride); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::SizeTo(int32_t aWidth, int32_t aHeight) michael@0: { michael@0: if (!mContent) michael@0: return NS_OK; michael@0: michael@0: nsAutoString width, height; michael@0: width.AppendInt(aWidth); michael@0: height.AppendInt(aHeight); michael@0: michael@0: nsCOMPtr content = mContent; michael@0: michael@0: // We only want to pass aNotify=true to SetAttr once, but must make sure michael@0: // we pass it when a value is being changed. Thus, we check if the height michael@0: // is the same and if so, pass true when setting the width. michael@0: bool heightSame = content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters); michael@0: michael@0: content->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame); michael@0: content->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetAutoPosition(bool* aShouldAutoPosition) michael@0: { michael@0: *aShouldAutoPosition = true; michael@0: michael@0: nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; michael@0: if (menuPopupFrame) { michael@0: *aShouldAutoPosition = menuPopupFrame->GetAutoPosition(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::SetAutoPosition(bool aShouldAutoPosition) michael@0: { michael@0: nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; michael@0: if (menuPopupFrame) { michael@0: menuPopupFrame->SetAutoPosition(aShouldAutoPosition); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::EnableRollup(bool aShouldRollup) michael@0: { michael@0: // this does nothing now michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::SetConsumeRollupEvent(uint32_t aConsume) michael@0: { michael@0: nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); michael@0: if (menuPopupFrame) { michael@0: menuPopupFrame->SetConsumeRollupEvent(aConsume); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::EnableKeyboardNavigator(bool aEnableKeyboardNavigator) michael@0: { michael@0: if (!mContent) michael@0: return NS_OK; michael@0: michael@0: // Use ignorekeys="true" on the popup instead of using this function. michael@0: if (aEnableKeyboardNavigator) michael@0: mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, true); michael@0: else michael@0: mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, michael@0: NS_LITERAL_STRING("true"), true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetPopupState(nsAString& aState) michael@0: { michael@0: // set this here in case there's no frame for the popup michael@0: aState.AssignLiteral("closed"); michael@0: michael@0: nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; michael@0: if (menuPopupFrame) { michael@0: switch (menuPopupFrame->PopupState()) { michael@0: case ePopupShowing: michael@0: case ePopupOpen: michael@0: aState.AssignLiteral("showing"); michael@0: break; michael@0: case ePopupOpenAndVisible: michael@0: aState.AssignLiteral("open"); michael@0: break; michael@0: case ePopupHiding: michael@0: case ePopupInvisible: michael@0: aState.AssignLiteral("hiding"); michael@0: break; michael@0: case ePopupClosed: michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Bad popup state"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetTriggerNode(nsIDOMNode** aTriggerNode) michael@0: { michael@0: *aTriggerNode = nullptr; michael@0: michael@0: nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; michael@0: nsIContent* triggerContent = nsMenuPopupFrame::GetTriggerContent(menuPopupFrame); michael@0: if (triggerContent) michael@0: CallQueryInterface(triggerContent, aTriggerNode); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetAnchorNode(nsIDOMElement** aAnchor) michael@0: { michael@0: *aAnchor = nullptr; michael@0: michael@0: nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; michael@0: if (!menuPopupFrame) michael@0: return NS_OK; michael@0: michael@0: nsIContent* anchor = menuPopupFrame->GetAnchor(); michael@0: if (anchor) michael@0: CallQueryInterface(anchor, aAnchor); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetOuterScreenRect(nsIDOMClientRect** aRect) michael@0: { michael@0: DOMRect* rect = new DOMRect(mContent); michael@0: michael@0: NS_ADDREF(*aRect = rect); michael@0: michael@0: nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); michael@0: if (!menuPopupFrame) michael@0: return NS_OK; michael@0: michael@0: // Return an empty rectangle if the popup is not open. michael@0: nsPopupState state = menuPopupFrame->PopupState(); michael@0: if (state != ePopupOpen && state != ePopupOpenAndVisible) michael@0: return NS_OK; michael@0: michael@0: nsView* view = menuPopupFrame->GetView(); michael@0: if (view) { michael@0: nsIWidget* widget = view->GetWidget(); michael@0: if (widget) { michael@0: nsIntRect screenRect; michael@0: widget->GetScreenBounds(screenRect); michael@0: michael@0: int32_t pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: rect->SetLayoutRect(screenRect.ToAppUnits(pp)); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetAlignmentPosition(nsAString& positionStr) michael@0: { michael@0: positionStr.Truncate(); michael@0: michael@0: // This needs to flush layout. michael@0: nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true)); michael@0: if (!menuPopupFrame) michael@0: return NS_OK; michael@0: michael@0: int8_t position = menuPopupFrame->GetAlignmentPosition(); michael@0: switch (position) { michael@0: case POPUPPOSITION_AFTERSTART: michael@0: positionStr.AssignLiteral("after_start"); michael@0: break; michael@0: case POPUPPOSITION_AFTEREND: michael@0: positionStr.AssignLiteral("after_end"); michael@0: break; michael@0: case POPUPPOSITION_BEFORESTART: michael@0: positionStr.AssignLiteral("before_start"); michael@0: break; michael@0: case POPUPPOSITION_BEFOREEND: michael@0: positionStr.AssignLiteral("before_end"); michael@0: break; michael@0: case POPUPPOSITION_STARTBEFORE: michael@0: positionStr.AssignLiteral("start_before"); michael@0: break; michael@0: case POPUPPOSITION_ENDBEFORE: michael@0: positionStr.AssignLiteral("end_before"); michael@0: break; michael@0: case POPUPPOSITION_STARTAFTER: michael@0: positionStr.AssignLiteral("start_after"); michael@0: break; michael@0: case POPUPPOSITION_ENDAFTER: michael@0: positionStr.AssignLiteral("end_after"); michael@0: break; michael@0: case POPUPPOSITION_OVERLAP: michael@0: positionStr.AssignLiteral("overlap"); michael@0: break; michael@0: case POPUPPOSITION_AFTERPOINTER: michael@0: positionStr.AssignLiteral("after_pointer"); michael@0: break; michael@0: default: michael@0: // Leave as an empty string. michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPopupBoxObject::GetAlignmentOffset(int32_t *aAlignmentOffset) michael@0: { michael@0: nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); michael@0: if (!menuPopupFrame) michael@0: return NS_OK; michael@0: michael@0: int32_t pp = mozilla::AppUnitsPerCSSPixel(); michael@0: // Note that the offset might be along either the X or Y axis, but for the michael@0: // sake of simplicity we use a point with only the X axis set so we can michael@0: // use ToNearestPixels(). michael@0: nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0); michael@0: nsIntPoint popupOffset = appOffset.ToNearestPixels(pp); michael@0: *aAlignmentOffset = popupOffset.x; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Creation Routine /////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsresult michael@0: NS_NewPopupBoxObject(nsIBoxObject** aResult) michael@0: { michael@0: *aResult = new nsPopupBoxObject; michael@0: if (!*aResult) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: }