Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/SelectionChangeEventDispatcher.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * Implementation of mozilla::SelectionChangeEventDispatcher
9
 */
10
11
#include "SelectionChangeEventDispatcher.h"
12
13
#include "mozilla/AsyncEventDispatcher.h"
14
#include "nsCOMPtr.h"
15
#include "nsContentUtils.h"
16
#include "nsIDocument.h"
17
#include "nsFrameSelection.h"
18
#include "nsRange.h"
19
#include "mozilla/dom/Selection.h"
20
21
namespace mozilla {
22
23
using namespace dom;
24
25
SelectionChangeEventDispatcher::RawRangeData::RawRangeData(
26
                                                const nsRange* aRange)
27
0
{
28
0
  mozilla::ErrorResult rv;
29
0
  mStartContainer = aRange->GetStartContainer(rv);
30
0
  rv.SuppressException();
31
0
  mEndContainer = aRange->GetEndContainer(rv);
32
0
  rv.SuppressException();
33
0
  mStartOffset = aRange->GetStartOffset(rv);
34
0
  rv.SuppressException();
35
0
  mEndOffset = aRange->GetEndOffset(rv);
36
0
  rv.SuppressException();
37
0
}
38
39
bool
40
SelectionChangeEventDispatcher::RawRangeData::Equals(const nsRange* aRange)
41
0
{
42
0
  mozilla::ErrorResult rv;
43
0
  bool eq = mStartContainer == aRange->GetStartContainer(rv);
44
0
  rv.SuppressException();
45
0
  eq = eq && mEndContainer == aRange->GetEndContainer(rv);
46
0
  rv.SuppressException();
47
0
  eq = eq && mStartOffset == aRange->GetStartOffset(rv);
48
0
  rv.SuppressException();
49
0
  eq = eq && mEndOffset == aRange->GetEndOffset(rv);
50
0
  rv.SuppressException();
51
0
  return eq;
52
0
}
53
54
inline void
55
ImplCycleCollectionTraverse(
56
  nsCycleCollectionTraversalCallback& aCallback,
57
  SelectionChangeEventDispatcher::RawRangeData& aField,
58
  const char* aName,
59
  uint32_t aFlags = 0)
60
0
{
61
0
  ImplCycleCollectionTraverse(aCallback, aField.mStartContainer,
62
0
                              "mStartContainer", aFlags);
63
0
  ImplCycleCollectionTraverse(aCallback, aField.mEndContainer,
64
0
                              "mEndContainer", aFlags);
65
0
}
66
67
NS_IMPL_CYCLE_COLLECTION_CLASS(SelectionChangeEventDispatcher)
68
69
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SelectionChangeEventDispatcher)
70
0
  tmp->mOldRanges.Clear();
71
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
72
73
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SelectionChangeEventDispatcher)
74
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOldRanges);
75
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
76
77
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SelectionChangeEventDispatcher, AddRef)
78
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SelectionChangeEventDispatcher, Release)
79
80
void
81
SelectionChangeEventDispatcher::OnSelectionChange(nsIDocument* aDoc,
82
                                                  Selection* aSel,
83
                                                  int16_t aReason)
84
0
{
85
0
  nsIDocument* doc = aSel->GetParentObject();
86
0
  if (!(doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal())) &&
87
0
      !nsFrameSelection::sSelectionEventsEnabled) {
88
0
    return;
89
0
  }
90
0
91
0
  // Check if the ranges have actually changed
92
0
  // Don't bother checking this if we are hiding changes.
93
0
  if (mOldRanges.Length() == aSel->RangeCount() &&
94
0
      !aSel->IsBlockingSelectionChangeEvents()) {
95
0
    bool changed = false;
96
0
97
0
    for (size_t i = 0; i < mOldRanges.Length(); i++) {
98
0
      if (!mOldRanges[i].Equals(aSel->GetRangeAt(i))) {
99
0
        changed = true;
100
0
        break;
101
0
      }
102
0
    }
103
0
104
0
    if (!changed) {
105
0
      return;
106
0
    }
107
0
  }
108
0
109
0
  // The ranges have actually changed, update the mOldRanges array
110
0
  mOldRanges.ClearAndRetainStorage();
111
0
  for (size_t i = 0; i < aSel->RangeCount(); i++) {
112
0
    mOldRanges.AppendElement(RawRangeData(aSel->GetRangeAt(i)));
113
0
  }
114
0
115
0
  if (doc) {
116
0
    nsPIDOMWindowInner* inner = doc->GetInnerWindow();
117
0
    if (inner && !inner->HasSelectionChangeEventListeners()) {
118
0
      return;
119
0
    }
120
0
  }
121
0
122
0
  // If we are hiding changes, then don't do anything else. We do this after we
123
0
  // update mOldRanges so that changes after the changes stop being hidden don't
124
0
  // incorrectly trigger a change, even though they didn't change anything
125
0
  if (aSel->IsBlockingSelectionChangeEvents()) {
126
0
    return;
127
0
  }
128
0
129
0
  // The spec currently doesn't say that we should dispatch this event on text
130
0
  // controls, so for now we only support doing that under a pref, disabled by
131
0
  // default.
132
0
  // See https://github.com/w3c/selection-api/issues/53.
133
0
  if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
134
0
    nsCOMPtr<nsINode> target;
135
0
136
0
    // Check if we should be firing this event to a different node than the
137
0
    // document. The limiter of the nsFrameSelection will be within the native
138
0
    // anonymous subtree of the node we want to fire the event on. We need to
139
0
    // climb up the parent chain to escape the native anonymous subtree, and then
140
0
    // fire the event.
141
0
    if (const nsFrameSelection* fs = aSel->GetFrameSelection()) {
142
0
      if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
143
0
        while (root && root->IsInNativeAnonymousSubtree()) {
144
0
          root = root->GetParent();
145
0
        }
146
0
147
0
        target = root.forget();
148
0
      }
149
0
    }
150
0
151
0
    // If we didn't get a target before, we can instead fire the event at the document.
152
0
    if (!target) {
153
0
      target = aDoc;
154
0
    }
155
0
156
0
    if (target) {
157
0
      RefPtr<AsyncEventDispatcher> asyncDispatcher =
158
0
        new AsyncEventDispatcher(target, eSelectionChange, CanBubble::eNo);
159
0
      asyncDispatcher->PostDOMEvent();
160
0
    }
161
0
  } else {
162
0
    if (const nsFrameSelection* fs = aSel->GetFrameSelection()) {
163
0
      if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
164
0
        if (root->IsInNativeAnonymousSubtree()) {
165
0
          return;
166
0
        }
167
0
      }
168
0
    }
169
0
170
0
    if (aDoc) {
171
0
      RefPtr<AsyncEventDispatcher> asyncDispatcher =
172
0
        new AsyncEventDispatcher(aDoc, eSelectionChange, CanBubble::eNo);
173
0
      asyncDispatcher->PostDOMEvent();
174
0
    }
175
0
  }
176
0
}
177
178
} // namespace mozilla