Bug 881092 - Allow decoding files we know we can't play, in the context of WebAudio...
[gecko.git] / accessible / src / base / SelectionManager.cpp
blob96678d522e9593ce97a0b2198fc04709e9802da2
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 "mozilla/a11y/SelectionManager.h"
8 #include "DocAccessible-inl.h"
9 #include "nsAccessibilityService.h"
10 #include "nsAccUtils.h"
11 #include "nsCoreUtils.h"
12 #include "nsIAccessibleEvent.h"
13 #include "RootAccessible.h"
15 #include "nsCaret.h"
16 #include "nsIDOMDocument.h"
17 #include "nsIDOMHTMLAnchorElement.h"
18 #include "nsIDOMHTMLTextAreaElement.h"
19 #include "nsIFrame.h"
20 #include "nsIPresShell.h"
21 #include "nsISelectionPrivate.h"
22 #include "nsServiceManagerUtils.h"
23 #include "mozilla/Selection.h"
25 using namespace mozilla;
26 using namespace mozilla::a11y;
28 void
29 SelectionManager::Shutdown()
31 ClearControlSelectionListener();
32 mLastTextAccessible = nullptr;
33 mLastUsedSelection = nullptr;
36 void
37 SelectionManager::ClearControlSelectionListener()
39 if (!mCurrCtrlFrame)
40 return;
42 const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
43 NS_ASSERTION(frameSel, "No frame selection for the element!");
45 mCurrCtrlFrame = nullptr;
46 if (!frameSel)
47 return;
49 // Remove 'this' registered as selection listener for the normal selection.
50 Selection* normalSel =
51 frameSel->GetSelection(nsISelectionController::SELECTION_NORMAL);
52 normalSel->RemoveSelectionListener(this);
54 // Remove 'this' registered as selection listener for the spellcheck
55 // selection.
56 Selection* spellSel =
57 frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
58 spellSel->RemoveSelectionListener(this);
61 void
62 SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
64 // When focus moves such that the caret is part of a new frame selection
65 // this removes the old selection listener and attaches a new one for
66 // the current focus.
67 ClearControlSelectionListener();
69 mLastTextAccessible = nullptr;
71 mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame();
72 if (!mCurrCtrlFrame)
73 return;
75 const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
76 NS_ASSERTION(frameSel, "No frame selection for focused element!");
77 if (!frameSel)
78 return;
80 // Register 'this' as selection listener for the normal selection.
81 Selection* normalSel =
82 frameSel->GetSelection(nsISelectionController::SELECTION_NORMAL);
83 normalSel->AddSelectionListener(this);
85 // Register 'this' as selection listener for the spell check selection.
86 Selection* spellSel =
87 frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
88 spellSel->AddSelectionListener(this);
91 void
92 SelectionManager::AddDocSelectionListener(nsIPresShell* aPresShell)
94 const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
96 // Register 'this' as selection listener for the normal selection.
97 Selection* normalSel =
98 frameSel->GetSelection(nsISelectionController::SELECTION_NORMAL);
99 normalSel->AddSelectionListener(this);
101 // Register 'this' as selection listener for the spell check selection.
102 Selection* spellSel =
103 frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
104 spellSel->AddSelectionListener(this);
107 void
108 SelectionManager::RemoveDocSelectionListener(nsIPresShell* aPresShell)
110 const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
112 // Remove 'this' registered as selection listener for the normal selection.
113 Selection* normalSel =
114 frameSel->GetSelection(nsISelectionController::SELECTION_NORMAL);
115 normalSel->RemoveSelectionListener(this);
117 // Remove 'this' registered as selection listener for the spellcheck
118 // selection.
119 Selection* spellSel =
120 frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
121 spellSel->RemoveSelectionListener(this);
124 NS_IMETHODIMP
125 SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
126 nsISelection* aSelection,
127 int16_t aReason)
129 NS_ENSURE_ARG(aDOMDocument);
131 nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
132 DocAccessible* document = GetAccService()->GetDocAccessible(documentNode);
134 #ifdef A11Y_LOG
135 if (logging::IsEnabled(logging::eSelection))
136 logging::SelChange(aSelection, document);
137 #endif
139 // Don't fire events until document is loaded.
140 if (document && document->IsContentLoaded()) {
141 // Selection manager has longer lifetime than any document accessible,
142 // so that we are guaranteed that the notification is processed before
143 // the selection manager is destroyed.
144 document->HandleNotification<SelectionManager, nsISelection>
145 (this, &SelectionManager::ProcessSelectionChanged, aSelection);
148 return NS_OK;
151 void
152 SelectionManager::ProcessSelectionChanged(nsISelection* aSelection)
154 nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
156 int16_t type = 0;
157 privSel->GetType(&type);
159 if (type == nsISelectionController::SELECTION_NORMAL)
160 NormalSelectionChanged(aSelection);
162 else if (type == nsISelectionController::SELECTION_SPELLCHECK)
163 SpellcheckSelectionChanged(aSelection);
166 void
167 SelectionManager::NormalSelectionChanged(nsISelection* aSelection)
169 mLastUsedSelection = do_GetWeakReference(aSelection);
171 int32_t rangeCount = 0;
172 aSelection->GetRangeCount(&rangeCount);
173 if (rangeCount == 0) {
174 mLastTextAccessible = nullptr;
175 return; // No selection
178 HyperTextAccessible* textAcc =
179 nsAccUtils::GetTextAccessibleFromSelection(aSelection);
180 if (!textAcc)
181 return;
183 int32_t caretOffset = -1;
184 nsresult rv = textAcc->GetCaretOffset(&caretOffset);
185 if (NS_FAILED(rv))
186 return;
188 if (textAcc == mLastTextAccessible && caretOffset == mLastCaretOffset) {
189 int32_t selectionCount = 0;
190 textAcc->GetSelectionCount(&selectionCount); // Don't swallow similar events when selecting text
191 if (!selectionCount)
192 return; // Swallow duplicate caret event
195 mLastCaretOffset = caretOffset;
196 mLastTextAccessible = textAcc;
198 nsRefPtr<AccEvent> event = new AccCaretMoveEvent(mLastTextAccessible);
199 mLastTextAccessible->Document()->FireDelayedEvent(event);
202 void
203 SelectionManager::SpellcheckSelectionChanged(nsISelection* aSelection)
205 // XXX: fire an event for accessible of focus node of the selection. If
206 // spellchecking is enabled then we will fire the number of events for
207 // the same accessible for newly appended range of the selection (for every
208 // misspelled word). If spellchecking is disabled (for example,
209 // @spellcheck="false" on html:body) then we won't fire any event.
211 HyperTextAccessible* hyperText =
212 nsAccUtils::GetTextAccessibleFromSelection(aSelection);
213 if (hyperText) {
214 hyperText->Document()->
215 FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
216 hyperText);
220 nsIntRect
221 SelectionManager::GetCaretRect(nsIWidget** aWidget)
223 nsIntRect caretRect;
224 NS_ENSURE_TRUE(aWidget, caretRect);
225 *aWidget = nullptr;
227 if (!mLastTextAccessible) {
228 return caretRect; // Return empty rect
231 nsINode *lastNodeWithCaret = mLastTextAccessible->GetNode();
232 NS_ENSURE_TRUE(lastNodeWithCaret, caretRect);
234 nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(lastNodeWithCaret);
235 NS_ENSURE_TRUE(presShell, caretRect);
237 nsRefPtr<nsCaret> caret = presShell->GetCaret();
238 NS_ENSURE_TRUE(caret, caretRect);
240 nsCOMPtr<nsISelection> caretSelection(do_QueryReferent(mLastUsedSelection));
241 NS_ENSURE_TRUE(caretSelection, caretRect);
243 bool isVisible;
244 caret->GetCaretVisible(&isVisible);
245 if (!isVisible) {
246 return nsIntRect(); // Return empty rect
249 nsRect rect;
250 nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
251 if (!frame || rect.IsEmpty()) {
252 return nsIntRect(); // Return empty rect
255 nsPoint offset;
256 // Offset from widget origin to the frame origin, which includes chrome
257 // on the widget.
258 *aWidget = frame->GetNearestWidget(offset);
259 NS_ENSURE_TRUE(*aWidget, nsIntRect());
260 rect.MoveBy(offset);
262 caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
263 // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
264 caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
266 // Correct for character size, so that caret always matches the size of the character
267 // This is important for font size transitions, and is necessary because the Gecko caret uses the
268 // previous character's size as the user moves forward in the text by character.
269 int32_t charX, charY, charWidth, charHeight;
270 if (NS_SUCCEEDED(mLastTextAccessible->GetCharacterExtents(mLastCaretOffset, &charX, &charY,
271 &charWidth, &charHeight,
272 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) {
273 caretRect.height -= charY - caretRect.y;
274 caretRect.y = charY;
277 return caretRect;