Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/accessible/base/SelectionManager.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
5
6
#include "mozilla/a11y/SelectionManager.h"
7
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"
16
17
#include "nsIAccessibleTypes.h"
18
#include "nsIDocument.h"
19
#include "nsIPresShell.h"
20
#include "mozilla/dom/Selection.h"
21
#include "mozilla/dom/Element.h"
22
23
using namespace mozilla;
24
using namespace mozilla::a11y;
25
using mozilla::dom::Selection;
26
27
struct mozilla::a11y::SelData final
28
{
29
  SelData(Selection* aSel, int32_t aReason) :
30
0
    mSel(aSel), mReason(aReason) {}
31
32
  RefPtr<Selection> mSel;
33
  int16_t mReason;
34
35
  NS_INLINE_DECL_REFCOUNTING(SelData)
36
37
private:
38
  // Private destructor, to discourage deletion outside of Release():
39
0
  ~SelData() {}
40
};
41
42
SelectionManager::SelectionManager() :
43
  mCaretOffset(-1), mAccWithCaret(nullptr)
44
0
{
45
0
46
0
}
47
48
void
49
SelectionManager::ClearControlSelectionListener()
50
0
{
51
0
  // Remove 'this' registered as selection listener for the normal selection.
52
0
  if (mCurrCtrlNormalSel) {
53
0
    mCurrCtrlNormalSel->RemoveSelectionListener(this);
54
0
    mCurrCtrlNormalSel = nullptr;
55
0
  }
56
0
57
0
  // Remove 'this' registered as selection listener for the spellcheck
58
0
  // selection.
59
0
  if (mCurrCtrlSpellSel) {
60
0
    mCurrCtrlSpellSel->RemoveSelectionListener(this);
61
0
    mCurrCtrlSpellSel = nullptr;
62
0
  }
63
0
}
64
65
void
66
SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
67
0
{
68
0
  // When focus moves such that the caret is part of a new frame selection
69
0
  // this removes the old selection listener and attaches a new one for
70
0
  // the current focus.
71
0
  ClearControlSelectionListener();
72
0
73
0
  nsIFrame* controlFrame = aFocusedElm->GetPrimaryFrame();
74
0
  if (!controlFrame)
75
0
    return;
76
0
77
0
  const nsFrameSelection* frameSel = controlFrame->GetConstFrameSelection();
78
0
  NS_ASSERTION(frameSel, "No frame selection for focused element!");
79
0
  if (!frameSel)
80
0
    return;
81
0
82
0
  // Register 'this' as selection listener for the normal selection.
83
0
  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
84
0
  normalSel->AddSelectionListener(this);
85
0
  mCurrCtrlNormalSel = normalSel;
86
0
87
0
  // Register 'this' as selection listener for the spell check selection.
88
0
  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
89
0
  spellSel->AddSelectionListener(this);
90
0
  mCurrCtrlSpellSel = spellSel;
91
0
}
92
93
void
94
SelectionManager::AddDocSelectionListener(nsIPresShell* aPresShell)
95
0
{
96
0
  const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
97
0
98
0
  // Register 'this' as selection listener for the normal selection.
99
0
  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
100
0
  normalSel->AddSelectionListener(this);
101
0
102
0
  // Register 'this' as selection listener for the spell check selection.
103
0
  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
104
0
  spellSel->AddSelectionListener(this);
105
0
}
106
107
void
108
SelectionManager::RemoveDocSelectionListener(nsIPresShell* aPresShell)
109
0
{
110
0
  const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
111
0
112
0
  // Remove 'this' registered as selection listener for the normal selection.
113
0
  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
114
0
  normalSel->RemoveSelectionListener(this);
115
0
116
0
  // Remove 'this' registered as selection listener for the spellcheck
117
0
  // selection.
118
0
  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
119
0
  spellSel->RemoveSelectionListener(this);
120
0
}
121
122
void
123
SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent)
124
0
{
125
0
  // Fire selection change event if it's not pure caret-move selection change,
126
0
  // i.e. the accessible has or had not collapsed selection.
127
0
  AccTextSelChangeEvent* event = downcast_accEvent(aEvent);
128
0
  if (!event->IsCaretMoveOnly())
129
0
    nsEventShell::FireEvent(aEvent);
130
0
131
0
  // Fire caret move event if there's a caret in the selection.
132
0
  nsINode* caretCntrNode =
133
0
    nsCoreUtils::GetDOMNodeFromDOMPoint(event->mSel->GetFocusNode(),
134
0
                                        event->mSel->FocusOffset());
135
0
  if (!caretCntrNode)
136
0
    return;
137
0
138
0
  HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode);
139
0
  NS_ASSERTION(caretCntr,
140
0
               "No text container for focus while there's one for common ancestor?!");
141
0
  if (!caretCntr)
142
0
    return;
143
0
144
0
  Selection* selection = caretCntr->DOMSelection();
145
0
146
0
  // XXX Sometimes we can't get a selection for caretCntr, in that case assume
147
0
  // event->mSel is correct.
148
0
  if (!selection)
149
0
    selection = event->mSel;
150
0
151
0
  mCaretOffset = caretCntr->DOMPointToOffset(selection->GetFocusNode(),
152
0
                                             selection->FocusOffset());
153
0
  mAccWithCaret = caretCntr;
154
0
  if (mCaretOffset != -1) {
155
0
    RefPtr<AccCaretMoveEvent> caretMoveEvent =
156
0
      new AccCaretMoveEvent(caretCntr, mCaretOffset, aEvent->FromUserInput());
157
0
    nsEventShell::FireEvent(caretMoveEvent);
158
0
  }
159
0
}
160
161
NS_IMETHODIMP
162
SelectionManager::NotifySelectionChanged(nsIDocument* aDocument,
163
                                         Selection* aSelection,
164
                                         int16_t aReason)
165
0
{
166
0
  if (NS_WARN_IF(!aDocument) || NS_WARN_IF(!aSelection)) {
167
0
    return NS_ERROR_INVALID_ARG;
168
0
  }
169
0
170
0
  DocAccessible* document = GetAccService()->GetDocAccessible(aDocument);
171
0
172
0
#ifdef A11Y_LOG
173
0
  if (logging::IsEnabled(logging::eSelection))
174
0
    logging::SelChange(aSelection, document, aReason);
175
0
#endif
176
0
177
0
  if (document) {
178
0
    // Selection manager has longer lifetime than any document accessible,
179
0
    // so that we are guaranteed that the notification is processed before
180
0
    // the selection manager is destroyed.
181
0
    RefPtr<SelData> selData = new SelData(aSelection, aReason);
182
0
    document->HandleNotification<SelectionManager, SelData>
183
0
      (this, &SelectionManager::ProcessSelectionChanged, selData);
184
0
  }
185
0
186
0
  return NS_OK;
187
0
}
188
189
void
190
SelectionManager::ProcessSelectionChanged(SelData* aSelData)
191
0
{
192
0
  Selection* selection = aSelData->mSel;
193
0
  if (!selection->GetPresShell())
194
0
    return;
195
0
196
0
  const nsRange* range = selection->GetAnchorFocusRange();
197
0
  nsINode* cntrNode = nullptr;
198
0
  if (range)
199
0
    cntrNode = range->GetCommonAncestor();
200
0
201
0
  if (!cntrNode) {
202
0
    cntrNode = selection->GetFrameSelection()->GetAncestorLimiter();
203
0
    if (!cntrNode) {
204
0
      cntrNode = selection->GetPresShell()->GetDocument();
205
0
      NS_ASSERTION(aSelData->mSel->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(),
206
0
                   "Wrong selection container was used!");
207
0
    }
208
0
  }
209
0
210
0
  HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode);
211
0
  if (!text) {
212
0
    // FIXME bug 1126649
213
0
    NS_ERROR("We must reach document accessible implementing text interface!");
214
0
    return;
215
0
  }
216
0
217
0
  if (selection->GetType() == SelectionType::eNormal) {
218
0
    RefPtr<AccEvent> event =
219
0
      new AccTextSelChangeEvent(text, selection, aSelData->mReason);
220
0
    text->Document()->FireDelayedEvent(event);
221
0
222
0
  } else if (selection->GetType() == SelectionType::eSpellCheck) {
223
0
    // XXX: fire an event for container accessible of the focus/anchor range
224
0
    // of the spelcheck selection.
225
0
    text->Document()->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
226
0
                                       text);
227
0
  }
228
0
}