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: 4; 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 "HTMLSelectAccessible.h"
8 #include "Accessible-inl.h"
9 #include "nsAccessibilityService.h"
10 #include "nsAccUtils.h"
11 #include "DocAccessible.h"
12 #include "nsEventShell.h"
13 #include "nsIAccessibleEvent.h"
14 #include "nsTextEquivUtils.h"
15 #include "Role.h"
16 #include "States.h"
18 #include "nsCOMPtr.h"
19 #include "mozilla/dom/HTMLOptionElement.h"
20 #include "nsIComboboxControlFrame.h"
21 #include "nsIFrame.h"
22 #include "nsIListControlFrame.h"
24 using namespace mozilla::a11y;
25 using namespace mozilla::dom;
27 ////////////////////////////////////////////////////////////////////////////////
28 // HTMLSelectListAccessible
29 ////////////////////////////////////////////////////////////////////////////////
31 HTMLSelectListAccessible::
32 HTMLSelectListAccessible(nsIContent* aContent, DocAccessible* aDoc) :
33 AccessibleWrap(aContent, aDoc)
34 {
35 mGenericTypes |= eListControl | eSelect;
36 }
38 ////////////////////////////////////////////////////////////////////////////////
39 // HTMLSelectListAccessible: Accessible public
41 uint64_t
42 HTMLSelectListAccessible::NativeState()
43 {
44 uint64_t state = AccessibleWrap::NativeState();
45 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple))
46 state |= states::MULTISELECTABLE | states::EXTSELECTABLE;
48 return state;
49 }
51 role
52 HTMLSelectListAccessible::NativeRole()
53 {
54 return roles::LISTBOX;
55 }
57 ////////////////////////////////////////////////////////////////////////////////
58 // HTMLSelectListAccessible: SelectAccessible
60 bool
61 HTMLSelectListAccessible::SelectAll()
62 {
63 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
64 AccessibleWrap::SelectAll() : false;
65 }
67 bool
68 HTMLSelectListAccessible::UnselectAll()
69 {
70 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
71 AccessibleWrap::UnselectAll() : false;
72 }
74 ////////////////////////////////////////////////////////////////////////////////
75 // HTMLSelectListAccessible: Widgets
77 bool
78 HTMLSelectListAccessible::IsWidget() const
79 {
80 return true;
81 }
83 bool
84 HTMLSelectListAccessible::IsActiveWidget() const
85 {
86 return FocusMgr()->HasDOMFocus(mContent);
87 }
89 bool
90 HTMLSelectListAccessible::AreItemsOperable() const
91 {
92 return true;
93 }
95 Accessible*
96 HTMLSelectListAccessible::CurrentItem()
97 {
98 nsIListControlFrame* listControlFrame = do_QueryFrame(GetFrame());
99 if (listControlFrame) {
100 nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
101 if (activeOptionNode) {
102 DocAccessible* document = Document();
103 if (document)
104 return document->GetAccessible(activeOptionNode);
105 }
106 }
107 return nullptr;
108 }
110 void
111 HTMLSelectListAccessible::SetCurrentItem(Accessible* aItem)
112 {
113 aItem->GetContent()->SetAttr(kNameSpaceID_None,
114 nsGkAtoms::selected, NS_LITERAL_STRING("true"),
115 true);
116 }
118 ////////////////////////////////////////////////////////////////////////////////
119 // HTMLSelectListAccessible: Accessible protected
121 void
122 HTMLSelectListAccessible::CacheChildren()
123 {
124 // Cache accessibles for <optgroup> and <option> DOM decendents as children,
125 // as well as the accessibles for them. Avoid whitespace text nodes. We want
126 // to count all the <optgroup>s and <option>s as children because we want
127 // a flat tree under the Select List.
128 for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
129 childContent = childContent->GetNextSibling()) {
130 if (!childContent->IsHTML()) {
131 continue;
132 }
134 nsIAtom* tag = childContent->Tag();
135 if (tag == nsGkAtoms::option ||
136 tag == nsGkAtoms::optgroup) {
138 // Get an accessible for option or optgroup and cache it.
139 nsRefPtr<Accessible> accessible =
140 GetAccService()->GetOrCreateAccessible(childContent, this);
141 if (accessible)
142 AppendChild(accessible);
143 }
144 }
145 }
148 ////////////////////////////////////////////////////////////////////////////////
149 // HTMLSelectOptionAccessible
150 ////////////////////////////////////////////////////////////////////////////////
152 HTMLSelectOptionAccessible::
153 HTMLSelectOptionAccessible(nsIContent* aContent, DocAccessible* aDoc) :
154 HyperTextAccessibleWrap(aContent, aDoc)
155 {
156 }
158 ////////////////////////////////////////////////////////////////////////////////
159 // HTMLSelectOptionAccessible: Accessible public
161 role
162 HTMLSelectOptionAccessible::NativeRole()
163 {
164 if (GetCombobox())
165 return roles::COMBOBOX_OPTION;
167 return roles::OPTION;
168 }
170 ENameValueFlag
171 HTMLSelectOptionAccessible::NativeName(nsString& aName)
172 {
173 // CASE #1 -- great majority of the cases
174 // find the label attribute - this is what the W3C says we should use
175 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
176 if (!aName.IsEmpty())
177 return eNameOK;
179 // CASE #2 -- no label parameter, get the first child,
180 // use it if it is a text node
181 nsIContent* text = mContent->GetFirstChild();
182 if (text && text->IsNodeOfType(nsINode::eTEXT)) {
183 nsTextEquivUtils::AppendTextEquivFromTextContent(text, &aName);
184 aName.CompressWhitespace();
185 return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
186 }
188 return eNameOK;
189 }
191 uint64_t
192 HTMLSelectOptionAccessible::NativeState()
193 {
194 // As a HTMLSelectOptionAccessible we can have the following states:
195 // SELECTABLE, SELECTED, FOCUSED, FOCUSABLE, OFFSCREEN
196 // Upcall to Accessible, but skip HyperTextAccessible impl
197 // because we don't want EDITABLE or SELECTABLE_TEXT
198 uint64_t state = Accessible::NativeState();
200 Accessible* select = GetSelect();
201 if (!select)
202 return state;
204 uint64_t selectState = select->State();
205 if (selectState & states::INVISIBLE)
206 return state;
208 // Are we selected?
209 HTMLOptionElement* option = HTMLOptionElement::FromContent(mContent);
210 bool selected = option && option->Selected();
211 if (selected)
212 state |= states::SELECTED;
214 if (selectState & states::OFFSCREEN) {
215 state |= states::OFFSCREEN;
216 } else if (selectState & states::COLLAPSED) {
217 // <select> is COLLAPSED: add OFFSCREEN, if not the currently
218 // visible option
219 if (!selected) {
220 state |= states::OFFSCREEN;
221 state ^= states::INVISIBLE;
222 } else {
223 // Clear offscreen and invisible for currently showing option
224 state &= ~(states::OFFSCREEN | states::INVISIBLE);
225 state |= selectState & states::OPAQUE1;
226 }
227 } else {
228 // XXX list frames are weird, don't rely on Accessible's general
229 // visibility implementation unless they get reimplemented in layout
230 state &= ~states::OFFSCREEN;
231 // <select> is not collapsed: compare bounds to calculate OFFSCREEN
232 Accessible* listAcc = Parent();
233 if (listAcc) {
234 int32_t optionX, optionY, optionWidth, optionHeight;
235 int32_t listX, listY, listWidth, listHeight;
236 GetBounds(&optionX, &optionY, &optionWidth, &optionHeight);
237 listAcc->GetBounds(&listX, &listY, &listWidth, &listHeight);
238 if (optionY < listY || optionY + optionHeight > listY + listHeight) {
239 state |= states::OFFSCREEN;
240 }
241 }
242 }
244 return state;
245 }
247 uint64_t
248 HTMLSelectOptionAccessible::NativeInteractiveState() const
249 {
250 return NativelyUnavailable() ?
251 states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE;
252 }
254 int32_t
255 HTMLSelectOptionAccessible::GetLevelInternal()
256 {
257 nsIContent* parentContent = mContent->GetParent();
259 int32_t level =
260 parentContent->NodeInfo()->Equals(nsGkAtoms::optgroup) ? 2 : 1;
262 if (level == 1 && Role() != roles::HEADING)
263 level = 0; // In a single level list, the level is irrelevant
265 return level;
266 }
268 void
269 HTMLSelectOptionAccessible::GetBoundsRect(nsRect& aTotalBounds,
270 nsIFrame** aBoundingFrame)
271 {
272 Accessible* combobox = GetCombobox();
273 if (combobox && (combobox->State() & states::COLLAPSED))
274 combobox->GetBoundsRect(aTotalBounds, aBoundingFrame);
275 else
276 HyperTextAccessibleWrap::GetBoundsRect(aTotalBounds, aBoundingFrame);
277 }
279 NS_IMETHODIMP
280 HTMLSelectOptionAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
281 {
282 if (aIndex == eAction_Select) {
283 aName.AssignLiteral("select");
284 return NS_OK;
285 }
286 return NS_ERROR_INVALID_ARG;
287 }
289 uint8_t
290 HTMLSelectOptionAccessible::ActionCount()
291 {
292 return 1;
293 }
295 NS_IMETHODIMP
296 HTMLSelectOptionAccessible::DoAction(uint8_t aIndex)
297 {
298 if (aIndex != eAction_Select)
299 return NS_ERROR_INVALID_ARG;
301 if (IsDefunct())
302 return NS_ERROR_FAILURE;
304 DoCommand();
305 return NS_OK;
306 }
308 NS_IMETHODIMP
309 HTMLSelectOptionAccessible::SetSelected(bool aSelect)
310 {
311 if (IsDefunct())
312 return NS_ERROR_FAILURE;
314 HTMLOptionElement* option = HTMLOptionElement::FromContent(mContent);
315 return option ? option->SetSelected(aSelect) : NS_ERROR_FAILURE;
316 }
318 ////////////////////////////////////////////////////////////////////////////////
319 // HTMLSelectOptionAccessible: Widgets
321 Accessible*
322 HTMLSelectOptionAccessible::ContainerWidget() const
323 {
324 Accessible* parent = Parent();
325 if (parent && parent->IsHTMLOptGroup())
326 parent = parent->Parent();
328 return parent && parent->IsListControl() ? parent : nullptr;
329 }
331 ////////////////////////////////////////////////////////////////////////////////
332 // HTMLSelectOptGroupAccessible
333 ////////////////////////////////////////////////////////////////////////////////
335 role
336 HTMLSelectOptGroupAccessible::NativeRole()
337 {
338 return roles::GROUPING;
339 }
341 uint64_t
342 HTMLSelectOptGroupAccessible::NativeInteractiveState() const
343 {
344 return NativelyUnavailable() ? states::UNAVAILABLE : 0;
345 }
347 NS_IMETHODIMP
348 HTMLSelectOptGroupAccessible::DoAction(uint8_t index)
349 {
350 return NS_ERROR_NOT_IMPLEMENTED;
351 }
353 NS_IMETHODIMP
354 HTMLSelectOptGroupAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
355 {
356 return NS_ERROR_NOT_IMPLEMENTED;
357 }
359 uint8_t
360 HTMLSelectOptGroupAccessible::ActionCount()
361 {
362 return 0;
363 }
365 ////////////////////////////////////////////////////////////////////////////////
366 // HTMLComboboxAccessible
367 ////////////////////////////////////////////////////////////////////////////////
369 HTMLComboboxAccessible::
370 HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
371 AccessibleWrap(aContent, aDoc)
372 {
373 mGenericTypes |= eCombobox;
374 }
376 ////////////////////////////////////////////////////////////////////////////////
377 // HTMLComboboxAccessible: Accessible
379 role
380 HTMLComboboxAccessible::NativeRole()
381 {
382 return roles::COMBOBOX;
383 }
385 void
386 HTMLComboboxAccessible::InvalidateChildren()
387 {
388 AccessibleWrap::InvalidateChildren();
390 if (mListAccessible)
391 mListAccessible->InvalidateChildren();
392 }
394 void
395 HTMLComboboxAccessible::CacheChildren()
396 {
397 nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
398 if (!comboFrame)
399 return;
401 nsIFrame* listFrame = comboFrame->GetDropDown();
402 if (!listFrame)
403 return;
405 if (!mListAccessible) {
406 mListAccessible =
407 new HTMLComboboxListAccessible(mParent, mContent, mDoc);
409 // Initialize and put into cache.
410 Document()->BindToDocument(mListAccessible, nullptr);
411 }
413 if (AppendChild(mListAccessible)) {
414 // Cache combobox option accessibles so that we build complete accessible
415 // tree for combobox.
416 mListAccessible->EnsureChildren();
417 }
418 }
420 void
421 HTMLComboboxAccessible::Shutdown()
422 {
423 AccessibleWrap::Shutdown();
425 if (mListAccessible) {
426 mListAccessible->Shutdown();
427 mListAccessible = nullptr;
428 }
429 }
431 uint64_t
432 HTMLComboboxAccessible::NativeState()
433 {
434 // As a HTMLComboboxAccessible we can have the following states:
435 // FOCUSED, FOCUSABLE, HASPOPUP, EXPANDED, COLLAPSED
436 // Get focus status from base class
437 uint64_t state = Accessible::NativeState();
439 nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
440 if (comboFrame && comboFrame->IsDroppedDown())
441 state |= states::EXPANDED;
442 else
443 state |= states::COLLAPSED;
445 state |= states::HASPOPUP;
446 return state;
447 }
449 void
450 HTMLComboboxAccessible::Description(nsString& aDescription)
451 {
452 aDescription.Truncate();
453 // First check to see if combo box itself has a description, perhaps through
454 // tooltip (title attribute) or via aria-describedby
455 Accessible::Description(aDescription);
456 if (!aDescription.IsEmpty())
457 return;
459 // Otherwise use description of selected option.
460 Accessible* option = SelectedOption();
461 if (option)
462 option->Description(aDescription);
463 }
465 void
466 HTMLComboboxAccessible::Value(nsString& aValue)
467 {
468 // Use accessible name of selected option.
469 Accessible* option = SelectedOption();
470 if (option)
471 option->Name(aValue);
472 }
474 uint8_t
475 HTMLComboboxAccessible::ActionCount()
476 {
477 return 1;
478 }
480 NS_IMETHODIMP
481 HTMLComboboxAccessible::DoAction(uint8_t aIndex)
482 {
483 if (aIndex != eAction_Click)
484 return NS_ERROR_INVALID_ARG;
486 if (IsDefunct())
487 return NS_ERROR_FAILURE;
489 DoCommand();
490 return NS_OK;
491 }
493 /**
494 * Our action name is the reverse of our state:
495 * if we are closed -> open is our name.
496 * if we are open -> closed is our name.
497 * Uses the frame to get the state, updated on every click
498 */
499 NS_IMETHODIMP
500 HTMLComboboxAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
501 {
502 if (aIndex != HTMLComboboxAccessible::eAction_Click) {
503 return NS_ERROR_INVALID_ARG;
504 }
505 nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
506 if (!comboFrame) {
507 return NS_ERROR_FAILURE;
508 }
509 if (comboFrame->IsDroppedDown())
510 aName.AssignLiteral("close");
511 else
512 aName.AssignLiteral("open");
514 return NS_OK;
515 }
517 ////////////////////////////////////////////////////////////////////////////////
518 // HTMLComboboxAccessible: Widgets
520 bool
521 HTMLComboboxAccessible::IsWidget() const
522 {
523 return true;
524 }
526 bool
527 HTMLComboboxAccessible::IsActiveWidget() const
528 {
529 return FocusMgr()->HasDOMFocus(mContent);
530 }
532 bool
533 HTMLComboboxAccessible::AreItemsOperable() const
534 {
535 nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(GetFrame());
536 return comboboxFrame && comboboxFrame->IsDroppedDown();
537 }
539 Accessible*
540 HTMLComboboxAccessible::CurrentItem()
541 {
542 return AreItemsOperable() ? mListAccessible->CurrentItem() : nullptr;
543 }
545 void
546 HTMLComboboxAccessible::SetCurrentItem(Accessible* aItem)
547 {
548 if (AreItemsOperable())
549 mListAccessible->SetCurrentItem(aItem);
550 }
552 ////////////////////////////////////////////////////////////////////////////////
553 // HTMLComboboxAccessible: protected
555 Accessible*
556 HTMLComboboxAccessible::SelectedOption() const
557 {
558 nsIFrame* frame = GetFrame();
559 nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
560 if (!comboboxFrame)
561 return nullptr;
563 nsIListControlFrame* listControlFrame =
564 do_QueryFrame(comboboxFrame->GetDropDown());
565 if (listControlFrame) {
566 nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
567 if (activeOptionNode) {
568 DocAccessible* document = Document();
569 if (document)
570 return document->GetAccessible(activeOptionNode);
571 }
572 }
574 return nullptr;
575 }
578 ////////////////////////////////////////////////////////////////////////////////
579 // HTMLComboboxListAccessible
580 ////////////////////////////////////////////////////////////////////////////////
582 HTMLComboboxListAccessible::
583 HTMLComboboxListAccessible(nsIAccessible* aParent, nsIContent* aContent,
584 DocAccessible* aDoc) :
585 HTMLSelectListAccessible(aContent, aDoc)
586 {
587 mStateFlags |= eSharedNode;
588 }
590 ////////////////////////////////////////////////////////////////////////////////
591 // HTMLComboboxAccessible: Accessible
593 nsIFrame*
594 HTMLComboboxListAccessible::GetFrame() const
595 {
596 nsIFrame* frame = HTMLSelectListAccessible::GetFrame();
597 nsIComboboxControlFrame* comboBox = do_QueryFrame(frame);
598 if (comboBox) {
599 return comboBox->GetDropDown();
600 }
602 return nullptr;
603 }
605 role
606 HTMLComboboxListAccessible::NativeRole()
607 {
608 return roles::COMBOBOX_LIST;
609 }
611 uint64_t
612 HTMLComboboxListAccessible::NativeState()
613 {
614 // As a HTMLComboboxListAccessible we can have the following states:
615 // FOCUSED, FOCUSABLE, FLOATING, INVISIBLE
616 // Get focus status from base class
617 uint64_t state = Accessible::NativeState();
619 nsIComboboxControlFrame* comboFrame = do_QueryFrame(mParent->GetFrame());
620 if (comboFrame && comboFrame->IsDroppedDown())
621 state |= states::FLOATING;
622 else
623 state |= states::INVISIBLE;
625 return state;
626 }
628 /**
629 * Gets the bounds for the areaFrame.
630 * Walks the Frame tree and checks for proper frames.
631 */
632 void
633 HTMLComboboxListAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame)
634 {
635 *aBoundingFrame = nullptr;
637 Accessible* comboAcc = Parent();
638 if (!comboAcc)
639 return;
641 if (0 == (comboAcc->State() & states::COLLAPSED)) {
642 HTMLSelectListAccessible::GetBoundsRect(aBounds, aBoundingFrame);
643 return;
644 }
646 // Get the first option.
647 nsIContent* content = mContent->GetFirstChild();
648 if (!content) {
649 return;
650 }
651 nsIFrame* frame = content->GetPrimaryFrame();
652 if (!frame) {
653 *aBoundingFrame = nullptr;
654 return;
655 }
657 *aBoundingFrame = frame->GetParent();
658 aBounds = (*aBoundingFrame)->GetRect();
659 }
661 ////////////////////////////////////////////////////////////////////////////////
662 // HTMLComboboxListAccessible: Widgets
664 bool
665 HTMLComboboxListAccessible::IsActiveWidget() const
666 {
667 return mParent && mParent->IsActiveWidget();
668 }
670 bool
671 HTMLComboboxListAccessible::AreItemsOperable() const
672 {
673 return mParent && mParent->AreItemsOperable();
674 }