tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XULPopupElement.cpp (11231B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/XULPopupElement.h"
      8 
      9 #include "XULMenuParentElement.h"
     10 #include "mozilla/AppUnits.h"
     11 #include "mozilla/AsyncEventDispatcher.h"
     12 #include "mozilla/dom/DOMRect.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "mozilla/dom/Element.h"
     15 #include "mozilla/dom/Event.h"
     16 #include "mozilla/dom/XULButtonElement.h"
     17 #include "mozilla/dom/XULMenuElement.h"
     18 #include "mozilla/dom/XULPopupElementBinding.h"
     19 #include "nsCOMPtr.h"
     20 #include "nsDOMCSSDeclaration.h"
     21 #include "nsGkAtoms.h"
     22 #include "nsIContent.h"
     23 #include "nsMenuPopupFrame.h"
     24 #include "nsNameSpaceManager.h"
     25 #include "nsStringFwd.h"
     26 #ifdef MOZ_WAYLAND
     27 #  include "mozilla/WidgetUtilsGtk.h"
     28 #endif
     29 
     30 namespace mozilla::dom {
     31 
     32 nsXULElement* NS_NewXULPopupElement(
     33    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
     34  RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
     35  auto* nim = nodeInfo->NodeInfoManager();
     36  return new (nim) XULPopupElement(nodeInfo.forget());
     37 }
     38 
     39 JSObject* XULPopupElement::WrapNode(JSContext* aCx,
     40                                    JS::Handle<JSObject*> aGivenProto) {
     41  return XULPopupElement_Binding::Wrap(aCx, this, aGivenProto);
     42 }
     43 
     44 nsMenuPopupFrame* XULPopupElement::GetFrame(FlushType aFlushType) {
     45  nsIFrame* f = GetPrimaryFrame(aFlushType);
     46  MOZ_ASSERT(!f || f->IsMenuPopupFrame());
     47  return static_cast<nsMenuPopupFrame*>(f);
     48 }
     49 
     50 void XULPopupElement::OpenPopup(Element* aAnchorElement,
     51                                const StringOrOpenPopupOptions& aOptions,
     52                                int32_t aXPos, int32_t aYPos,
     53                                bool aIsContextMenu, bool aAttributesOverride,
     54                                Event* aTriggerEvent) {
     55  nsAutoString position;
     56  if (aOptions.IsOpenPopupOptions()) {
     57    const OpenPopupOptions& options = aOptions.GetAsOpenPopupOptions();
     58    position = options.mPosition;
     59    aXPos = options.mX;
     60    aYPos = options.mY;
     61    aIsContextMenu = options.mIsContextMenu;
     62    aAttributesOverride = options.mAttributesOverride;
     63    aTriggerEvent = options.mTriggerEvent;
     64  } else {
     65    position = aOptions.GetAsString();
     66  }
     67 
     68  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     69  if (!pm) {
     70    return;
     71  }
     72  // As a special case for popups that are menus when no anchor or position
     73  // are specified, open the popup with ShowMenu instead of ShowPopup so that
     74  // the popup is aligned with the menu.
     75  if (!aAnchorElement && position.IsEmpty() && GetPrimaryFrame()) {
     76    if (auto* menu = GetContainingMenu()) {
     77      pm->ShowMenu(menu, false);
     78      return;
     79    }
     80  }
     81  pm->ShowPopup(this, aAnchorElement, position, aXPos, aYPos, aIsContextMenu,
     82                aAttributesOverride, false, aTriggerEvent);
     83 }
     84 
     85 void XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
     86                                        bool aIsContextMenu,
     87                                        Event* aTriggerEvent) {
     88  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     89  if (pm) {
     90    pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
     91  }
     92 }
     93 
     94 void XULPopupElement::OpenPopupAtScreenRect(const nsAString& aPosition,
     95                                            int32_t aXPos, int32_t aYPos,
     96                                            int32_t aWidth, int32_t aHeight,
     97                                            bool aIsContextMenu,
     98                                            bool aAttributesOverride,
     99                                            Event* aTriggerEvent) {
    100  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    101  if (pm) {
    102    pm->ShowPopupAtScreenRect(
    103        this, aPosition, nsIntRect(aXPos, aYPos, aWidth, aHeight),
    104        aIsContextMenu, aAttributesOverride, aTriggerEvent);
    105  }
    106 }
    107 
    108 void XULPopupElement::HidePopup(bool aCancel) {
    109  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    110  if (!pm) {
    111    return;
    112  }
    113  HidePopupOptions options{HidePopupOption::DeselectMenu};
    114  if (aCancel) {
    115    options += HidePopupOption::IsRollup;
    116  }
    117  pm->HidePopup(this, options);
    118 }
    119 
    120 static Modifiers ConvertModifiers(const ActivateMenuItemOptions& aModifiers) {
    121  Modifiers modifiers = 0;
    122  if (aModifiers.mCtrlKey) {
    123    modifiers |= MODIFIER_CONTROL;
    124  }
    125  if (aModifiers.mAltKey) {
    126    modifiers |= MODIFIER_ALT;
    127  }
    128  if (aModifiers.mShiftKey) {
    129    modifiers |= MODIFIER_SHIFT;
    130  }
    131  if (aModifiers.mMetaKey) {
    132    modifiers |= MODIFIER_META;
    133  }
    134  return modifiers;
    135 }
    136 
    137 void XULPopupElement::PopupOpened(bool aSelectFirstItem) {
    138  if (aSelectFirstItem) {
    139    SelectFirstItem();
    140  }
    141  if (RefPtr button = GetContainingMenu()) {
    142    if (RefPtr parent = button->GetMenuParent()) {
    143      parent->SetActiveMenuChild(button);
    144    }
    145  }
    146 }
    147 
    148 void XULPopupElement::PopupClosed(bool aDeselectMenu) {
    149  LockMenuUntilClosed(false);
    150  SetActiveMenuChild(nullptr);
    151  auto dispatcher = MakeRefPtr<AsyncEventDispatcher>(
    152      this, u"DOMMenuInactive"_ns, CanBubble::eYes, ChromeOnlyDispatch::eNo);
    153  dispatcher->PostDOMEvent();
    154  if (RefPtr button = GetContainingMenu()) {
    155    button->PopupClosed(aDeselectMenu);
    156  }
    157 }
    158 
    159 void XULPopupElement::ActivateItem(Element& aItemElement,
    160                                   const ActivateMenuItemOptions& aOptions,
    161                                   ErrorResult& aRv) {
    162  if (!Contains(&aItemElement)) {
    163    return aRv.ThrowInvalidStateError("Menu item is not inside this menu.");
    164  }
    165 
    166  Modifiers modifiers = ConvertModifiers(aOptions);
    167 
    168  // First, check if a native menu is open, and activate the item in it.
    169  if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
    170    if (pm->ActivateNativeMenuItem(&aItemElement, modifiers, aOptions.mButton,
    171                                   aRv)) {
    172      return;
    173    }
    174  }
    175 
    176  auto* item = XULButtonElement::FromNode(aItemElement);
    177  if (!item || !item->IsMenu()) {
    178    return aRv.ThrowInvalidStateError("Not a menu item");
    179  }
    180 
    181  if (!item->GetPrimaryFrame(FlushType::Frames)) {
    182    return aRv.ThrowInvalidStateError("Menu item is hidden");
    183  }
    184 
    185  auto* popup = item->GetContainingPopupElement();
    186  if (!popup) {
    187    return aRv.ThrowInvalidStateError("No popup");
    188  }
    189 
    190  nsMenuPopupFrame* frame = popup->GetFrame(FlushType::None);
    191  if (!frame || !frame->IsOpen()) {
    192    return aRv.ThrowInvalidStateError("Popup is not open");
    193  }
    194 
    195  // This is a chrome-only API, so we're trusted.
    196  const bool trusted = true;
    197  // KnownLive because item is aItemElement.
    198  MOZ_KnownLive(item)->ExecuteMenu(modifiers, aOptions.mButton, trusted);
    199 }
    200 
    201 void XULPopupElement::MoveTo(int32_t aLeft, int32_t aTop) {
    202  if (nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetPrimaryFrame())) {
    203    menuPopupFrame->MoveTo(CSSIntPoint(aLeft, aTop), true);
    204  }
    205 }
    206 
    207 void XULPopupElement::MoveToAnchor(Element* aAnchorElement,
    208                                   const nsAString& aPosition, int32_t aXPos,
    209                                   int32_t aYPos, bool aAttributesOverride) {
    210  nsMenuPopupFrame* menuPopupFrame = GetFrame(FlushType::None);
    211  if (menuPopupFrame && menuPopupFrame->IsVisibleOrShowing()) {
    212    menuPopupFrame->MoveToAnchor(aAnchorElement, aPosition, aXPos, aYPos,
    213                                 aAttributesOverride);
    214  }
    215 }
    216 
    217 void XULPopupElement::SizeTo(int32_t aWidth, int32_t aHeight) {
    218  nsAutoCString width;
    219  nsAutoCString height;
    220  width.AppendInt(aWidth);
    221  width.AppendLiteral("px");
    222  height.AppendInt(aHeight);
    223  height.AppendLiteral("px");
    224 
    225  nsCOMPtr<nsDOMCSSDeclaration> style = Style();
    226  style->SetProperty("width"_ns, width, ""_ns, IgnoreErrors());
    227  style->SetProperty("height"_ns, height, ""_ns, IgnoreErrors());
    228 
    229  // If the popup is open, force a reposition of the popup after resizing it
    230  // with notifications set to true so that the popuppositioned event is fired.
    231  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
    232  if (menuPopupFrame && menuPopupFrame->PopupState() == ePopupShown) {
    233    menuPopupFrame->SetPopupPosition(false);
    234  }
    235 }
    236 
    237 void XULPopupElement::GetState(nsString& aState) {
    238  // set this here in case there's no frame for the popup
    239  aState.AssignLiteral("closed");
    240 
    241  if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
    242    switch (pm->GetPopupState(this)) {
    243      case ePopupShown:
    244        aState.AssignLiteral("open");
    245        break;
    246      case ePopupShowing:
    247      case ePopupPositioning:
    248      case ePopupOpening:
    249      case ePopupVisible:
    250        aState.AssignLiteral("showing");
    251        break;
    252      case ePopupHiding:
    253      case ePopupInvisible:
    254        aState.AssignLiteral("hiding");
    255        break;
    256      case ePopupClosed:
    257        break;
    258      default:
    259        MOZ_ASSERT_UNREACHABLE("Bad popup state");
    260        break;
    261    }
    262  }
    263 }
    264 
    265 nsINode* XULPopupElement::GetTriggerNode() const {
    266  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
    267  return nsMenuPopupFrame::GetTriggerContent(menuPopupFrame);
    268 }
    269 
    270 // FIXME(emilio): should probably be renamed to GetAnchorElement?
    271 Element* XULPopupElement::GetAnchorNode() const {
    272  nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetPrimaryFrame());
    273  if (!menuPopupFrame) {
    274    return nullptr;
    275  }
    276  return Element::FromNodeOrNull(menuPopupFrame->GetAnchor());
    277 }
    278 
    279 already_AddRefed<DOMRect> XULPopupElement::GetOuterScreenRect() {
    280  RefPtr<DOMRect> rect = new DOMRect(ToSupports(OwnerDoc()));
    281 
    282  // Return an empty rectangle if the popup is not open.
    283  nsMenuPopupFrame* menuPopupFrame =
    284      do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
    285  if (!menuPopupFrame || !menuPopupFrame->IsOpen()) {
    286    return rect.forget();
    287  }
    288 
    289  Maybe<CSSRect> screenRect;
    290 
    291  if (menuPopupFrame->IsNativeMenu()) {
    292    // For native menus we can't query the true size. Use the anchor rect
    293    // instead, which at least has the position at which we were intending to
    294    // open the menu.
    295    screenRect = Some(CSSRect(menuPopupFrame->GetScreenAnchorRect()));
    296  } else if (nsIWidget* widget = menuPopupFrame->GetWidget()) {
    297    // For non-native menus, query the bounds from the widget.
    298    screenRect = Some(widget->GetScreenBounds() /
    299                      menuPopupFrame->PresContext()->CSSToDevPixelScale());
    300  }
    301 
    302  if (screenRect) {
    303    rect->SetRect(screenRect->X(), screenRect->Y(), screenRect->Width(),
    304                  screenRect->Height());
    305  }
    306  return rect.forget();
    307 }
    308 
    309 void XULPopupElement::SetConstraintRect(dom::DOMRectReadOnly& aRect) {
    310  nsMenuPopupFrame* menuPopupFrame =
    311      do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
    312  if (menuPopupFrame) {
    313    menuPopupFrame->SetOverrideConstraintRect(CSSIntRect::Truncate(
    314        aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height()));
    315  }
    316 }
    317 
    318 bool XULPopupElement::IsWaylandDragSource() const {
    319 #ifdef MOZ_WAYLAND
    320  nsMenuPopupFrame* f = do_QueryFrame(GetPrimaryFrame());
    321  return f && f->IsDragSource();
    322 #else
    323  return false;
    324 #endif
    325 }
    326 
    327 bool XULPopupElement::IsWaylandPopup() const {
    328 #ifdef MOZ_WAYLAND
    329  return widget::GdkIsWaylandDisplay() || widget::IsXWaylandProtocol();
    330 #else
    331  return false;
    332 #endif
    333 }
    334 
    335 }  // namespace mozilla::dom