Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / accessible / base / SelectionManager.cpp
blob1fc8290bfe20b04eaf290b6fee59c2aa91f0c9e3
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 "HyperTextAccessible.h"
10 #include "HyperTextAccessible-inl.h"
11 #include "nsAccessibilityService.h"
12 #include "nsAccUtils.h"
13 #include "nsCoreUtils.h"
14 #include "nsEventShell.h"
15 #include "nsFrameSelection.h"
17 #include "mozilla/PresShell.h"
18 #include "mozilla/dom/Document.h"
19 #include "mozilla/dom/Selection.h"
20 #include "mozilla/dom/Element.h"
22 using namespace mozilla;
23 using namespace mozilla::a11y;
24 using mozilla::dom::Selection;
26 struct mozilla::a11y::SelData final {
27 SelData(Selection* aSel, int32_t aReason, int32_t aGranularity)
28 : mSel(aSel), mReason(aReason), mGranularity(aGranularity) {}
30 RefPtr<Selection> mSel;
31 int16_t mReason;
32 int32_t mGranularity;
34 NS_INLINE_DECL_REFCOUNTING(SelData)
36 private:
37 // Private destructor, to discourage deletion outside of Release():
38 ~SelData() {}
41 SelectionManager::SelectionManager()
42 : mCaretOffset(-1), mAccWithCaret(nullptr) {}
44 void SelectionManager::ClearControlSelectionListener() {
45 // Remove 'this' registered as selection listener for the normal selection.
46 if (mCurrCtrlNormalSel) {
47 mCurrCtrlNormalSel->RemoveSelectionListener(this);
48 mCurrCtrlNormalSel = nullptr;
51 // Remove 'this' registered as selection listener for the spellcheck
52 // selection.
53 if (mCurrCtrlSpellSel) {
54 mCurrCtrlSpellSel->RemoveSelectionListener(this);
55 mCurrCtrlSpellSel = nullptr;
59 void SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm) {
60 // When focus moves such that the caret is part of a new frame selection
61 // this removes the old selection listener and attaches a new one for
62 // the current focus.
63 ClearControlSelectionListener();
65 nsIFrame* controlFrame = aFocusedElm->GetPrimaryFrame();
66 if (!controlFrame) return;
68 const nsFrameSelection* frameSel = controlFrame->GetConstFrameSelection();
69 NS_ASSERTION(frameSel, "No frame selection for focused element!");
70 if (!frameSel) return;
72 // Register 'this' as selection listener for the normal selection.
73 Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
74 normalSel->AddSelectionListener(this);
75 mCurrCtrlNormalSel = normalSel;
77 // Register 'this' as selection listener for the spell check selection.
78 Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
79 spellSel->AddSelectionListener(this);
80 mCurrCtrlSpellSel = spellSel;
83 void SelectionManager::AddDocSelectionListener(PresShell* aPresShell) {
84 const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
86 // Register 'this' as selection listener for the normal selection.
87 Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
88 normalSel->AddSelectionListener(this);
90 // Register 'this' as selection listener for the spell check selection.
91 Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
92 spellSel->AddSelectionListener(this);
95 void SelectionManager::RemoveDocSelectionListener(PresShell* aPresShell) {
96 const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
98 // Remove 'this' registered as selection listener for the normal selection.
99 Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
100 normalSel->RemoveSelectionListener(this);
102 // Remove 'this' registered as selection listener for the spellcheck
103 // selection.
104 Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
105 spellSel->RemoveSelectionListener(this);
107 if (mCurrCtrlNormalSel) {
108 if (mCurrCtrlNormalSel->GetPresShell() == aPresShell) {
109 // Remove 'this' registered as selection listener for the normal selection
110 // if we are removing listeners for its PresShell.
111 mCurrCtrlNormalSel->RemoveSelectionListener(this);
112 mCurrCtrlNormalSel = nullptr;
116 if (mCurrCtrlSpellSel) {
117 if (mCurrCtrlSpellSel->GetPresShell() == aPresShell) {
118 // Remove 'this' registered as selection listener for the spellcheck
119 // selection if we are removing listeners for its PresShell.
120 mCurrCtrlSpellSel->RemoveSelectionListener(this);
121 mCurrCtrlSpellSel = nullptr;
126 void SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent) {
127 // Fire selection change event if it's not pure caret-move selection change,
128 // i.e. the accessible has or had not collapsed selection.
129 AccTextSelChangeEvent* event = downcast_accEvent(aEvent);
130 if (!event->IsCaretMoveOnly()) nsEventShell::FireEvent(aEvent);
132 // Fire caret move event if there's a caret in the selection.
133 nsINode* caretCntrNode = nsCoreUtils::GetDOMNodeFromDOMPoint(
134 event->mSel->GetFocusNode(), event->mSel->FocusOffset());
135 if (!caretCntrNode) return;
137 HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode);
138 NS_ASSERTION(
139 caretCntr,
140 "No text container for focus while there's one for common ancestor?!");
141 if (!caretCntr) return;
143 Selection* selection = caretCntr->DOMSelection();
145 // XXX Sometimes we can't get a selection for caretCntr, in that case assume
146 // event->mSel is correct.
147 if (!selection) selection = event->mSel;
149 mCaretOffset = caretCntr->DOMPointToOffset(selection->GetFocusNode(),
150 selection->FocusOffset());
151 mAccWithCaret = caretCntr;
152 if (mCaretOffset != -1) {
153 RefPtr<AccCaretMoveEvent> caretMoveEvent =
154 new AccCaretMoveEvent(caretCntr, mCaretOffset, selection->IsCollapsed(),
155 caretCntr->IsCaretAtEndOfLine(),
156 event->GetGranularity(), aEvent->FromUserInput());
157 nsEventShell::FireEvent(caretMoveEvent);
161 NS_IMETHODIMP
162 SelectionManager::NotifySelectionChanged(dom::Document* aDocument,
163 Selection* aSelection, int16_t aReason,
164 int32_t aAmount) {
165 if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
166 return NS_ERROR_INVALID_ARG;
169 DocAccessible* document = GetAccService()->GetDocAccessible(aDocument);
171 #ifdef A11Y_LOG
172 if (logging::IsEnabled(logging::eSelection)) {
173 logging::SelChange(aSelection, document, aReason);
175 #endif
177 if (document) {
178 // Selection manager has longer lifetime than any document accessible,
179 // so that we are guaranteed that the notification is processed before
180 // the selection manager is destroyed.
181 RefPtr<SelData> selData = new SelData(aSelection, aReason, aAmount);
182 document->HandleNotification<SelectionManager, SelData>(
183 this, &SelectionManager::ProcessSelectionChanged, selData);
186 return NS_OK;
189 void SelectionManager::ProcessSelectionChanged(SelData* aSelData) {
190 Selection* selection = aSelData->mSel;
191 if (!selection->GetPresShell()) return;
193 const nsRange* range = selection->GetAnchorFocusRange();
194 nsINode* cntrNode = nullptr;
195 if (range) {
196 cntrNode = range->GetClosestCommonInclusiveAncestor();
199 if (!cntrNode) {
200 cntrNode = selection->GetFrameSelection()->GetAncestorLimiter();
201 if (!cntrNode) {
202 cntrNode = selection->GetPresShell()->GetDocument();
203 NS_ASSERTION(aSelData->mSel->GetPresShell()->ConstFrameSelection() ==
204 selection->GetFrameSelection(),
205 "Wrong selection container was used!");
209 HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode);
210 if (!text) {
211 // FIXME bug 1126649
212 NS_ERROR("We must reach document accessible implementing text interface!");
213 return;
216 if (selection->GetType() == SelectionType::eNormal) {
217 RefPtr<AccEvent> event = new AccTextSelChangeEvent(
218 text, selection, aSelData->mReason, aSelData->mGranularity);
219 text->Document()->FireDelayedEvent(event);
221 } else if (selection->GetType() == SelectionType::eSpellCheck) {
222 // XXX: fire an event for container accessible of the focus/anchor range
223 // of the spelcheck selection.
224 text->Document()->FireDelayedEvent(
225 nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, text);
229 SelectionManager::~SelectionManager() = default;