Tue, 06 Jan 2015 21:39:09 +0100
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 }