accessible/src/xul/XULMenuAccessible.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "XULMenuAccessible.h"
     8 #include "Accessible-inl.h"
     9 #include "nsAccessibilityService.h"
    10 #include "nsAccUtils.h"
    11 #include "DocAccessible.h"
    12 #include "Role.h"
    13 #include "States.h"
    14 #include "XULFormControlAccessible.h"
    16 #include "nsIDOMElement.h"
    17 #include "nsIDOMXULElement.h"
    18 #include "nsIMutableArray.h"
    19 #include "nsIDOMXULContainerElement.h"
    20 #include "nsIDOMXULSelectCntrlItemEl.h"
    21 #include "nsIDOMXULMultSelectCntrlEl.h"
    22 #include "nsIDOMKeyEvent.h"
    23 #include "nsIServiceManager.h"
    24 #include "nsIPresShell.h"
    25 #include "nsIContent.h"
    26 #include "nsMenuBarFrame.h"
    27 #include "nsMenuPopupFrame.h"
    29 #include "mozilla/Preferences.h"
    30 #include "mozilla/LookAndFeel.h"
    31 #include "mozilla/dom/Element.h"
    33 using namespace mozilla;
    34 using namespace mozilla::a11y;
    36 ////////////////////////////////////////////////////////////////////////////////
    37 // XULMenuitemAccessible
    38 ////////////////////////////////////////////////////////////////////////////////
    40 XULMenuitemAccessible::
    41   XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
    42   AccessibleWrap(aContent, aDoc)
    43 {
    44 }
    46 uint64_t
    47 XULMenuitemAccessible::NativeState()
    48 {
    49   uint64_t state = Accessible::NativeState();
    51   // Has Popup?
    52   if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) {
    53     state |= states::HASPOPUP;
    54     if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open))
    55       state |= states::EXPANDED;
    56     else
    57       state |= states::COLLAPSED;
    58   }
    60   // Checkable/checked?
    61   static nsIContent::AttrValuesArray strings[] =
    62     { &nsGkAtoms::radio, &nsGkAtoms::checkbox, nullptr };
    64   if (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, strings,
    65                                 eCaseMatters) >= 0) {
    67     // Checkable?
    68     state |= states::CHECKABLE;
    70     // Checked?
    71     if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
    72                               nsGkAtoms::_true, eCaseMatters))
    73       state |= states::CHECKED;
    74   }
    76   // Combo box listitem
    77   bool isComboboxOption = (Role() == roles::COMBOBOX_OPTION);
    78   if (isComboboxOption) {
    79     // Is selected?
    80     bool isSelected = false;
    81     nsCOMPtr<nsIDOMXULSelectControlItemElement>
    82       item(do_QueryInterface(mContent));
    83     NS_ENSURE_TRUE(item, state);
    84     item->GetSelected(&isSelected);
    86     // Is collapsed?
    87     bool isCollapsed = false;
    88     Accessible* parent = Parent();
    89     if (parent && parent->State() & states::INVISIBLE)
    90       isCollapsed = true;
    92     if (isSelected) {
    93       state |= states::SELECTED;
    95       // Selected and collapsed?
    96       if (isCollapsed) {
    97         // Set selected option offscreen/invisible according to combobox state
    98         Accessible* grandParent = parent->Parent();
    99         if (!grandParent)
   100           return state;
   101         NS_ASSERTION(grandParent->Role() == roles::COMBOBOX,
   102                      "grandparent of combobox listitem is not combobox");
   103         uint64_t grandParentState = grandParent->State();
   104         state &= ~(states::OFFSCREEN | states::INVISIBLE);
   105         state |= (grandParentState & states::OFFSCREEN) |
   106                  (grandParentState & states::INVISIBLE) |
   107                  (grandParentState & states::OPAQUE1);
   108       } // isCollapsed
   109     } // isSelected
   110   } // ROLE_COMBOBOX_OPTION
   112   return state;
   113 }
   115 uint64_t
   116 XULMenuitemAccessible::NativeInteractiveState() const
   117 {
   118   if (NativelyUnavailable()) {
   119     // Note: keep in sinc with nsXULPopupManager::IsValidMenuItem() logic.
   120     bool skipNavigatingDisabledMenuItem = true;
   121     nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
   122     if (!menuFrame || !menuFrame->IsOnMenuBar()) {
   123       skipNavigatingDisabledMenuItem = LookAndFeel::
   124         GetInt(LookAndFeel::eIntID_SkipNavigatingDisabledMenuItem, 0) != 0;
   125     }
   127     if (skipNavigatingDisabledMenuItem)
   128       return states::UNAVAILABLE;
   130     return states::UNAVAILABLE | states::FOCUSABLE | states::SELECTABLE;
   131   }
   133   return states::FOCUSABLE | states::SELECTABLE;
   134 }
   136 ENameValueFlag
   137 XULMenuitemAccessible::NativeName(nsString& aName)
   138 {
   139   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
   140   return eNameOK;
   141 }
   143 void
   144 XULMenuitemAccessible::Description(nsString& aDescription)
   145 {
   146   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::description,
   147                     aDescription);
   148 }
   150 KeyBinding
   151 XULMenuitemAccessible::AccessKey() const
   152 {
   153   // Return menu accesskey: N or Alt+F.
   154   static int32_t gMenuAccesskeyModifier = -1;  // magic value of -1 indicates unitialized state
   156   // We do not use nsCoreUtils::GetAccesskeyFor() because accesskeys for
   157   // menu are't registered by EventStateManager.
   158   nsAutoString accesskey;
   159   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey,
   160                     accesskey);
   161   if (accesskey.IsEmpty())
   162     return KeyBinding();
   164   uint32_t modifierKey = 0;
   166   Accessible* parentAcc = Parent();
   167   if (parentAcc) {
   168     if (parentAcc->NativeRole() == roles::MENUBAR) {
   169       // If top level menu item, add Alt+ or whatever modifier text to string
   170       // No need to cache pref service, this happens rarely
   171       if (gMenuAccesskeyModifier == -1) {
   172         // Need to initialize cached global accesskey pref
   173         gMenuAccesskeyModifier = Preferences::GetInt("ui.key.menuAccessKey", 0);
   174       }
   176       switch (gMenuAccesskeyModifier) {
   177         case nsIDOMKeyEvent::DOM_VK_CONTROL:
   178           modifierKey = KeyBinding::kControl;
   179           break;
   180         case nsIDOMKeyEvent::DOM_VK_ALT:
   181           modifierKey = KeyBinding::kAlt;
   182           break;
   183         case nsIDOMKeyEvent::DOM_VK_META:
   184           modifierKey = KeyBinding::kMeta;
   185           break;
   186         case nsIDOMKeyEvent::DOM_VK_WIN:
   187           modifierKey = KeyBinding::kOS;
   188           break;
   189       }
   190     }
   191   }
   193   return KeyBinding(accesskey[0], modifierKey);
   194 }
   196 KeyBinding
   197 XULMenuitemAccessible::KeyboardShortcut() const
   198 {
   199   nsAutoString keyElmId;
   200   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyElmId);
   201   if (keyElmId.IsEmpty())
   202     return KeyBinding();
   204   nsIContent* keyElm = mContent->OwnerDoc()->GetElementById(keyElmId);
   205   if (!keyElm)
   206     return KeyBinding();
   208   uint32_t key = 0;
   210   nsAutoString keyStr;
   211   keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyStr);
   212   if (keyStr.IsEmpty()) {
   213     nsAutoString keyCodeStr;
   214     keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeStr);
   215     nsresult errorCode;
   216     key = keyStr.ToInteger(&errorCode, kAutoDetect);
   217   } else {
   218     key = keyStr[0];
   219   }
   221   nsAutoString modifiersStr;
   222   keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr);
   224   uint32_t modifierMask = 0;
   225   if (modifiersStr.Find("shift") != -1)
   226     modifierMask |= KeyBinding::kShift;
   227   if (modifiersStr.Find("alt") != -1)
   228     modifierMask |= KeyBinding::kAlt;
   229   if (modifiersStr.Find("meta") != -1)
   230     modifierMask |= KeyBinding::kMeta;
   231   if (modifiersStr.Find("os") != -1)
   232     modifierMask |= KeyBinding::kOS;
   233   if (modifiersStr.Find("control") != -1)
   234     modifierMask |= KeyBinding::kControl;
   235   if (modifiersStr.Find("accel") != -1) {
   236     // Get the accelerator key value from prefs, overriding the default.
   237     switch (Preferences::GetInt("ui.key.accelKey", 0)) {
   238       case nsIDOMKeyEvent::DOM_VK_META:
   239         modifierMask |= KeyBinding::kMeta;
   240         break;
   242       case nsIDOMKeyEvent::DOM_VK_WIN:
   243         modifierMask |= KeyBinding::kOS;
   244         break;
   246       case nsIDOMKeyEvent::DOM_VK_ALT:
   247         modifierMask |= KeyBinding::kAlt;
   248         break;
   250       case nsIDOMKeyEvent::DOM_VK_CONTROL:
   251         modifierMask |= KeyBinding::kControl;
   252         break;
   254       default:
   255 #ifdef XP_MACOSX
   256         modifierMask |= KeyBinding::kMeta;
   257 #else
   258         modifierMask |= KeyBinding::kControl;
   259 #endif
   260     }
   261   }
   263   return KeyBinding(key, modifierMask);
   264 }
   266 role
   267 XULMenuitemAccessible::NativeRole()
   268 {
   269   nsCOMPtr<nsIDOMXULContainerElement> xulContainer(do_QueryInterface(mContent));
   270   if (xulContainer)
   271     return roles::PARENT_MENUITEM;
   273   if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
   274     return roles::COMBOBOX_OPTION;
   276   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
   277                             nsGkAtoms::radio, eCaseMatters)) 
   278     return roles::RADIO_MENU_ITEM;
   280   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
   281                             nsGkAtoms::checkbox,
   282                             eCaseMatters)) 
   283     return roles::CHECK_MENU_ITEM;
   285   return roles::MENUITEM;
   286 }
   288 int32_t
   289 XULMenuitemAccessible::GetLevelInternal()
   290 {
   291   return nsAccUtils::GetLevelForXULContainerItem(mContent);
   292 }
   294 bool
   295 XULMenuitemAccessible::CanHaveAnonChildren()
   296 {
   297   // That indicates we don't walk anonymous children for menuitems
   298   return false;
   299 }
   301 NS_IMETHODIMP
   302 XULMenuitemAccessible::DoAction(uint8_t index)
   303 {
   304   if (index == eAction_Click) {   // default action
   305     DoCommand();
   306     return NS_OK;
   307   }
   309   return NS_ERROR_INVALID_ARG;
   310 }
   312 /** select us! close combo box if necessary*/
   313 NS_IMETHODIMP
   314 XULMenuitemAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
   315 {
   316   if (aIndex == eAction_Click) {
   317     aName.AssignLiteral("click"); 
   318     return NS_OK;
   319   }
   320   return NS_ERROR_INVALID_ARG;
   321 }
   323 uint8_t
   324 XULMenuitemAccessible::ActionCount()
   325 {
   326   return 1;
   327 }
   329 ////////////////////////////////////////////////////////////////////////////////
   330 // XULMenuitemAccessible: Widgets
   332 bool
   333 XULMenuitemAccessible::IsActiveWidget() const
   334 {
   335   // Parent menu item is a widget, it's active when its popup is open.
   336   nsIContent* menuPopupContent = mContent->GetFirstChild();
   337   if (menuPopupContent) {
   338     nsMenuPopupFrame* menuPopupFrame =
   339       do_QueryFrame(menuPopupContent->GetPrimaryFrame());
   340     return menuPopupFrame && menuPopupFrame->IsOpen();
   341   }
   342   return false;
   343 }
   345 bool
   346 XULMenuitemAccessible::AreItemsOperable() const
   347 {
   348   // Parent menu item is a widget, its items are operable when its popup is open.
   349   nsIContent* menuPopupContent = mContent->GetFirstChild();
   350   if (menuPopupContent) {
   351     nsMenuPopupFrame* menuPopupFrame =
   352       do_QueryFrame(menuPopupContent->GetPrimaryFrame());
   353     return menuPopupFrame && menuPopupFrame->IsOpen();
   354   }
   355   return false;
   356 }
   358 Accessible*
   359 XULMenuitemAccessible::ContainerWidget() const
   360 {
   361   nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
   362   if (menuFrame) {
   363     nsMenuParent* menuParent = menuFrame->GetMenuParent();
   364     if (menuParent) {
   365       if (menuParent->IsMenuBar()) // menubar menu
   366         return mParent;
   368       // a menupoup or parent menu item
   369       if (menuParent->IsMenu())
   370         return mParent;
   372       // otherwise it's different kind of popups (like panel or tooltip), it
   373       // shouldn't be a real case.
   374     }
   375   }
   376   return nullptr;
   377 }
   380 ////////////////////////////////////////////////////////////////////////////////
   381 // XULMenuSeparatorAccessible
   382 ////////////////////////////////////////////////////////////////////////////////
   384 XULMenuSeparatorAccessible::
   385   XULMenuSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   386   XULMenuitemAccessible(aContent, aDoc)
   387 {
   388 }
   390 uint64_t
   391 XULMenuSeparatorAccessible::NativeState()
   392 {
   393   // Isn't focusable, but can be offscreen/invisible -- only copy those states
   394   return XULMenuitemAccessible::NativeState() &
   395     (states::OFFSCREEN | states::INVISIBLE);
   396 }
   398 ENameValueFlag
   399 XULMenuSeparatorAccessible::NativeName(nsString& aName)
   400 {
   401   return eNameOK;
   402 }
   404 role
   405 XULMenuSeparatorAccessible::NativeRole()
   406 {
   407   return roles::SEPARATOR;
   408 }
   410 NS_IMETHODIMP
   411 XULMenuSeparatorAccessible::DoAction(uint8_t index)
   412 {
   413   return NS_ERROR_NOT_IMPLEMENTED;
   414 }
   416 NS_IMETHODIMP
   417 XULMenuSeparatorAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
   418 {
   419   return NS_ERROR_NOT_IMPLEMENTED;
   420 }
   422 uint8_t
   423 XULMenuSeparatorAccessible::ActionCount()
   424 {
   425   return 0;
   426 }
   428 ////////////////////////////////////////////////////////////////////////////////
   429 // XULMenupopupAccessible
   430 ////////////////////////////////////////////////////////////////////////////////
   432 XULMenupopupAccessible::
   433   XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   434   XULSelectControlAccessible(aContent, aDoc)
   435 {
   436   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   437   if (menuPopupFrame && menuPopupFrame->IsMenu())
   438     mType = eMenuPopupType;
   440   // May be the anonymous <menupopup> inside <menulist> (a combobox)
   441   mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent());
   442   if (!mSelectControl)
   443     mGenericTypes &= ~eSelect;
   444 }
   446 uint64_t
   447 XULMenupopupAccessible::NativeState()
   448 {
   449   uint64_t state = Accessible::NativeState();
   451 #ifdef DEBUG
   452   // We are onscreen if our parent is active
   453   bool isActive = mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive);
   454   if (!isActive) {
   455     Accessible* parent = Parent();
   456     if (parent) {
   457       nsIContent* parentContent = parent->GetContent();
   458       if (parentContent)
   459         isActive = parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open);
   460     }
   461   }
   463   NS_ASSERTION(isActive || (state & states::INVISIBLE),
   464                "XULMenupopup doesn't have INVISIBLE when it's inactive");
   465 #endif
   467   if (state & states::INVISIBLE)
   468     state |= states::OFFSCREEN | states::COLLAPSED;
   470   return state;
   471 }
   473 ENameValueFlag
   474 XULMenupopupAccessible::NativeName(nsString& aName)
   475 {
   476   nsIContent* content = mContent;
   477   while (content && aName.IsEmpty()) {
   478     content->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
   479     content = content->GetFlattenedTreeParent();
   480   }
   482   return eNameOK;
   483 }
   485 role
   486 XULMenupopupAccessible::NativeRole()
   487 {
   488   // If accessible is not bound to the tree (this happens while children are
   489   // cached) return general role.
   490   if (mParent) {
   491     roles::Role role = mParent->Role();
   492     if (role == roles::COMBOBOX || role == roles::AUTOCOMPLETE)
   493       return roles::COMBOBOX_LIST;
   495     if (role == roles::PUSHBUTTON) {
   496       // Some widgets like the search bar have several popups, owned by buttons.
   497       Accessible* grandParent = mParent->Parent();
   498       if (grandParent && grandParent->Role() == roles::AUTOCOMPLETE)
   499         return roles::COMBOBOX_LIST;
   500     }
   501   }
   503   return roles::MENUPOPUP;
   504 }
   506 ////////////////////////////////////////////////////////////////////////////////
   507 // XULMenupopupAccessible: Widgets
   509 bool
   510 XULMenupopupAccessible::IsWidget() const
   511 {
   512   return true;
   513 }
   515 bool
   516 XULMenupopupAccessible::IsActiveWidget() const
   517 {
   518   // If menupopup is a widget (the case of context menus) then active when open.
   519   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   520   return menuPopupFrame && menuPopupFrame->IsOpen();
   521 }
   523 bool
   524 XULMenupopupAccessible::AreItemsOperable() const
   525 {
   526   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   527   return menuPopupFrame && menuPopupFrame->IsOpen();
   528 }
   530 Accessible*
   531 XULMenupopupAccessible::ContainerWidget() const
   532 {
   533   DocAccessible* document = Document();
   535   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   536   while (menuPopupFrame) {
   537     Accessible* menuPopup =
   538       document->GetAccessible(menuPopupFrame->GetContent());
   539     if (!menuPopup) // shouldn't be a real case
   540       return nullptr;
   542     nsMenuFrame* menuFrame = do_QueryFrame(menuPopupFrame->GetParent());
   543     if (!menuFrame) // context menu or popups
   544       return nullptr;
   546     nsMenuParent* menuParent = menuFrame->GetMenuParent();
   547     if (!menuParent) // menulist or menubutton
   548       return menuPopup->Parent();
   550     if (menuParent->IsMenuBar()) { // menubar menu
   551       nsMenuBarFrame* menuBarFrame = static_cast<nsMenuBarFrame*>(menuParent);
   552       return document->GetAccessible(menuBarFrame->GetContent());
   553     }
   555     // different kind of popups like panel or tooltip
   556     if (!menuParent->IsMenu())
   557       return nullptr;
   559     menuPopupFrame = static_cast<nsMenuPopupFrame*>(menuParent);
   560   }
   562   NS_NOTREACHED("Shouldn't be a real case.");
   563   return nullptr;
   564 }
   566 ////////////////////////////////////////////////////////////////////////////////
   567 // XULMenubarAccessible
   568 ////////////////////////////////////////////////////////////////////////////////
   570 XULMenubarAccessible::
   571   XULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   572   AccessibleWrap(aContent, aDoc)
   573 {
   574 }
   576 ENameValueFlag
   577 XULMenubarAccessible::NativeName(nsString& aName)
   578 {
   579   aName.AssignLiteral("Application");
   580   return eNameOK;
   581 }
   583 role
   584 XULMenubarAccessible::NativeRole()
   585 {
   586   return roles::MENUBAR;
   587 }
   589 ////////////////////////////////////////////////////////////////////////////////
   590 // XULMenubarAccessible: Widgets
   592 bool
   593 XULMenubarAccessible::IsActiveWidget() const
   594 {
   595   nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
   596   return menuBarFrame && menuBarFrame->IsActive();
   597 }
   599 bool
   600 XULMenubarAccessible::AreItemsOperable() const
   601 {
   602   return true;
   603 }
   605 Accessible*
   606 XULMenubarAccessible::CurrentItem()
   607 {
   608   nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
   609   if (menuBarFrame) {
   610     nsMenuFrame* menuFrame = menuBarFrame->GetCurrentMenuItem();
   611     if (menuFrame) {
   612       nsIContent* menuItemNode = menuFrame->GetContent();
   613       return mDoc->GetAccessible(menuItemNode);
   614     }
   615   }
   616   return nullptr;
   617 }
   619 void
   620 XULMenubarAccessible::SetCurrentItem(Accessible* aItem)
   621 {
   622   NS_ERROR("XULMenubarAccessible::SetCurrentItem not implemented");
   623 }

mercurial