Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp
Warning:line 805, column 24
Value stored to 'tHint' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_layout_generic2.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/layout/generic -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/layout/generic -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/layout/generic -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/layout/generic -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/layout/base -I /var/lib/jenkins/workspace/firefox-scan-build/layout/forms -I /var/lib/jenkins/workspace/firefox-scan-build/layout/painting -I /var/lib/jenkins/workspace/firefox-scan-build/layout/style -I /var/lib/jenkins/workspace/firefox-scan-build/layout/tables -I /var/lib/jenkins/workspace/firefox-scan-build/layout/xul -I /var/lib/jenkins/workspace/firefox-scan-build/docshell/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/html -I /var/lib/jenkins/workspace/firefox-scan-build/dom/xul -I /var/lib/jenkins/workspace/firefox-scan-build/gfx/cairo/cairo/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/cloudproviders -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gtk-3.0/unix-print -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-07-27-022226-2793976-1 -x c++ Unified_cpp_layout_generic2.cpp
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 nsFrameSelection
9 */
10
11#include "nsFrameSelection.h"
12
13#include "ErrorList.h"
14#include "mozilla/intl/BidiEmbeddingLevel.h"
15#include "mozilla/Attributes.h"
16#include "mozilla/AutoRestore.h"
17#include "mozilla/BasePrincipal.h"
18#include "mozilla/HTMLEditor.h"
19#include "mozilla/IntegerRange.h"
20#include "mozilla/Logging.h"
21#include "mozilla/PresShell.h"
22#include "mozilla/ScrollContainerFrame.h"
23#include "mozilla/ScrollTypes.h"
24#include "mozilla/StaticAnalysisFunctions.h"
25#include "mozilla/StaticPrefs_bidi.h"
26#include "mozilla/StaticPrefs_dom.h"
27#include "mozilla/StaticPrefs_layout.h"
28#include "mozilla/Unused.h"
29
30#include "nsCOMPtr.h"
31#include "nsDebug.h"
32#include "nsFrameTraversal.h"
33#include "nsString.h"
34#include "nsISelectionListener.h"
35#include "nsDeviceContext.h"
36#include "nsIContent.h"
37#include "nsRange.h"
38#include "nsITableCellLayout.h"
39#include "nsTArray.h"
40#include "nsTableWrapperFrame.h"
41#include "nsTableCellFrame.h"
42#include "nsCCUncollectableMarker.h"
43#include "nsTextFragment.h"
44#include <algorithm>
45#include "nsContentUtils.h"
46#include "nsCSSFrameConstructor.h"
47
48#include "nsGkAtoms.h"
49#include "nsLayoutUtils.h"
50#include "nsBidiPresUtils.h"
51#include "nsTextFrame.h"
52
53#include "nsThreadUtils.h"
54#include "mozilla/Preferences.h"
55
56#include "mozilla/PresShell.h"
57#include "nsPresContext.h"
58#include "nsCaret.h"
59
60#include "mozilla/MouseEvents.h"
61#include "mozilla/TextEvents.h"
62
63// notifications
64#include "mozilla/dom/Document.h"
65
66#include "nsISelectionController.h" //for the enums
67#include "nsCopySupport.h"
68#include "nsIClipboard.h"
69#include "nsIFrameInlines.h"
70
71#include "nsError.h"
72#include "mozilla/AutoCopyListener.h"
73#include "mozilla/dom/AncestorIterator.h"
74#include "mozilla/dom/Element.h"
75#include "mozilla/dom/Highlight.h"
76#include "mozilla/dom/Selection.h"
77#include "mozilla/dom/ShadowRoot.h"
78#include "mozilla/dom/StaticRange.h"
79#include "mozilla/dom/Text.h"
80#include "mozilla/ErrorResult.h"
81#include "mozilla/dom/SelectionBinding.h"
82#include "mozilla/AsyncEventDispatcher.h"
83#include "mozilla/Telemetry.h"
84
85#include "nsFocusManager.h"
86#include "nsPIDOMWindow.h"
87
88#include "SelectionMovementUtils.h"
89
90using namespace mozilla;
91using namespace mozilla::dom;
92
93static LazyLogModule sFrameSelectionLog("FrameSelection");
94
95// #define DEBUG_TABLE 1
96
97/**
98 * Add cells to the selection inside of the given cells range.
99 *
100 * @param aTable [in] HTML table element
101 * @param aStartRowIndex [in] row index where the cells range starts
102 * @param aStartColumnIndex [in] column index where the cells range starts
103 * @param aEndRowIndex [in] row index where the cells range ends
104 * @param aEndColumnIndex [in] column index where the cells range ends
105 */
106static nsresult AddCellsToSelection(const nsIContent* aTableContent,
107 int32_t aStartRowIndex,
108 int32_t aStartColumnIndex,
109 int32_t aEndRowIndex,
110 int32_t aEndColumnIndex,
111 Selection& aNormalSelection);
112
113static nsAtom* GetTag(nsINode* aNode);
114
115static nsINode* GetClosestInclusiveTableCellAncestor(nsINode* aDomNode);
116MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult CreateAndAddRange(
117 nsINode* aContainer, int32_t aOffset, Selection& aNormalSelection);
118static nsresult SelectCellElement(nsIContent* aCellElement,
119 Selection& aNormalSelection);
120
121#ifdef XP_MACOSX
122static nsresult UpdateSelectionCacheOnRepaintSelection(Selection* aSel);
123#endif // XP_MACOSX
124
125#ifdef PRINT_RANGE
126static void printRange(nsRange* aDomRange);
127# define DEBUG_OUT_RANGE(x) printRange(x)
128#else
129# define DEBUG_OUT_RANGE(x)
130#endif // PRINT_RANGE
131
132/******************************************************************************
133 * mozilla::PeekOffsetStruct
134 ******************************************************************************/
135
136// #define DEBUG_SELECTION // uncomment for printf describing every collapse and
137// extend. #define DEBUG_NAVIGATION
138
139// #define DEBUG_TABLE_SELECTION 1
140
141namespace mozilla {
142
143PeekOffsetStruct::PeekOffsetStruct(
144 nsSelectionAmount aAmount, nsDirection aDirection, int32_t aStartOffset,
145 nsPoint aDesiredCaretPos, const PeekOffsetOptions aOptions,
146 EWordMovementType aWordMovementType /* = eDefaultBehavior */,
147 const Element* aAncestorLimiter /* = nullptr */)
148 : mAmount(aAmount),
149 mDirection(aDirection),
150 mStartOffset(aStartOffset),
151 mDesiredCaretPos(aDesiredCaretPos),
152 mWordMovementType(aWordMovementType),
153 mOptions(aOptions),
154 mAncestorLimiter(aAncestorLimiter),
155 mResultFrame(nullptr),
156 mContentOffset(0),
157 mAttach(CaretAssociationHint::Before) {}
158
159} // namespace mozilla
160
161// Array which contains index of each SelectionType in
162// Selection::mDOMSelections. For avoiding using if nor switch to retrieve the
163// index, this needs to have -1 for SelectionTypes which won't be created its
164// Selection instance.
165static const int8_t kIndexOfSelections[] = {
166 -1, // SelectionType::eInvalid
167 -1, // SelectionType::eNone
168 0, // SelectionType::eNormal
169 1, // SelectionType::eSpellCheck
170 2, // SelectionType::eIMERawClause
171 3, // SelectionType::eIMESelectedRawClause
172 4, // SelectionType::eIMEConvertedClause
173 5, // SelectionType::eIMESelectedClause
174 6, // SelectionType::eAccessibility
175 7, // SelectionType::eFind
176 8, // SelectionType::eURLSecondary
177 9, // SelectionType::eURLStrikeout
178 10, // SelectionType::eTargetText
179 -1, // SelectionType::eHighlight
180};
181
182inline int8_t GetIndexFromSelectionType(SelectionType aSelectionType) {
183 // The enum value of eInvalid is -1 and the others are sequential value
184 // starting from 0. Therefore, |SelectionType + 1| is the index of
185 // kIndexOfSelections.
186 return kIndexOfSelections[static_cast<int8_t>(aSelectionType) + 1];
187}
188
189/*
190The limiter is used specifically for the text areas and textfields
191In that case it is the DIV tag that is anonymously created for the text
192areas/fields. Text nodes and BR nodes fall beneath it. In the case of a
193BR node the limiter will be the parent and the offset will point before or
194after the BR node. In the case of the text node the parent content is
195the text node itself and the offset will be the exact character position.
196The offset is not important to check for validity. Simply look at the
197passed in content. If it equals the limiter then the selection point is valid.
198If its parent it the limiter then the point is also valid. In the case of
199NO limiter all points are valid since you are in a topmost iframe. (browser
200or composer)
201*/
202bool nsFrameSelection::IsValidSelectionPoint(nsINode* aNode) const {
203 if (!aNode) {
204 return false;
205 }
206
207 nsIContent* limiter = GetLimiter();
208 if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
209 // if newfocus == the limiter. that's ok. but if not there and not parent
210 // bad
211 return false; // not in the right content. tLimiter said so
212 }
213
214 limiter = GetAncestorLimiter();
215 return !limiter || aNode->IsInclusiveDescendantOf(limiter);
216}
217
218namespace mozilla {
219struct MOZ_RAII AutoPrepareFocusRange {
220 AutoPrepareFocusRange(Selection* aSelection,
221 const bool aMultiRangeSelection) {
222 MOZ_ASSERT(aSelection)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSelection)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSelection))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aSelection", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 222); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSelection" ")"
); do { *((volatile int*)__null) = 222; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
223 MOZ_ASSERT(aSelection->GetType() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSelection->GetType() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aSelection->GetType() == SelectionType::eNormal))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aSelection->GetType() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 223); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSelection->GetType() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 223; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
224
225 if (aSelection->mStyledRanges.mRanges.Length() <= 1) {
226 return;
227 }
228
229 if (aSelection->mFrameSelection->IsUserSelectionReason()) {
230 mUserSelect.emplace(aSelection);
231 }
232
233 nsTArray<StyledRange>& ranges = aSelection->mStyledRanges.mRanges;
234 if (!aSelection->mUserInitiated || aMultiRangeSelection) {
235 // Scripted command or the user is starting a new explicit multi-range
236 // selection.
237 for (StyledRange& entry : ranges) {
238 MOZ_ASSERT(entry.mRange->IsDynamicRange())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(entry.mRange->IsDynamicRange())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(entry.mRange->IsDynamicRange
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("entry.mRange->IsDynamicRange()", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 238); AnnotateMozCrashReason("MOZ_ASSERT" "(" "entry.mRange->IsDynamicRange()"
")"); do { *((volatile int*)__null) = 238; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
239 entry.mRange->AsDynamicRange()->SetIsGenerated(false);
240 }
241 return;
242 }
243
244 if (!IsAnchorRelativeOperation(
245 aSelection->mFrameSelection->mSelectionChangeReasons)) {
246 return;
247 }
248
249 // This operation is against the anchor but our current mAnchorFocusRange
250 // represents the focus in a multi-range selection. The anchor from a user
251 // perspective is the most distant generated range on the opposite side.
252 // Find that range and make it the mAnchorFocusRange.
253 nsRange* const newAnchorFocusRange =
254 FindGeneratedRangeMostDistantFromAnchor(*aSelection);
255
256 if (!newAnchorFocusRange) {
257 // There are no generated ranges - that's fine.
258 return;
259 }
260
261 // Setup the new mAnchorFocusRange and mark the old one as generated.
262 if (aSelection->mAnchorFocusRange) {
263 aSelection->mAnchorFocusRange->SetIsGenerated(true);
264 }
265
266 newAnchorFocusRange->SetIsGenerated(false);
267 aSelection->mAnchorFocusRange = newAnchorFocusRange;
268
269 RemoveGeneratedRanges(*aSelection);
270
271 if (aSelection->mFrameSelection) {
272 aSelection->mFrameSelection->InvalidateDesiredCaretPos();
273 }
274 }
275
276 private:
277 static nsRange* FindGeneratedRangeMostDistantFromAnchor(
278 const Selection& aSelection) {
279 const nsTArray<StyledRange>& ranges = aSelection.mStyledRanges.mRanges;
280 const size_t len = ranges.Length();
281 nsRange* result{nullptr};
282 if (aSelection.GetDirection() == eDirNext) {
283 for (size_t i = 0; i < len; ++i) {
284 // This function is only called for selections with type == eNormal.
285 // (see MOZ_ASSERT in constructor).
286 // Therefore, all ranges must be dynamic.
287 if (ranges[i].mRange->AsDynamicRange()->IsGenerated()) {
288 result = ranges[i].mRange->AsDynamicRange();
289 break;
290 }
291 }
292 } else {
293 size_t i = len;
294 while (i--) {
295 if (ranges[i].mRange->AsDynamicRange()->IsGenerated()) {
296 result = ranges[i].mRange->AsDynamicRange();
297 break;
298 }
299 }
300 }
301
302 return result;
303 }
304
305 static void RemoveGeneratedRanges(Selection& aSelection) {
306 RefPtr<nsPresContext> presContext = aSelection.GetPresContext();
307 nsTArray<StyledRange>& ranges = aSelection.mStyledRanges.mRanges;
308 size_t i = ranges.Length();
309 while (i--) {
310 // This function is only called for selections with type == eNormal.
311 // (see MOZ_ASSERT in constructor).
312 // Therefore, all ranges must be dynamic.
313 if (!ranges[i].mRange->IsDynamicRange()) {
314 continue;
315 }
316 nsRange* range = ranges[i].mRange->AsDynamicRange();
317 if (range->IsGenerated()) {
318 range->UnregisterSelection(aSelection);
319 aSelection.SelectFrames(presContext, *range, false);
320 ranges.RemoveElementAt(i);
321 }
322 }
323 }
324
325 /**
326 * @aParam aSelectionChangeReasons can be multiple of the reasons defined in
327 nsISelectionListener.idl.
328 */
329 static bool IsAnchorRelativeOperation(const int16_t aSelectionChangeReasons) {
330 return aSelectionChangeReasons &
331 (nsISelectionListener::DRAG_REASON |
332 nsISelectionListener::MOUSEDOWN_REASON |
333 nsISelectionListener::MOUSEUP_REASON |
334 nsISelectionListener::COLLAPSETOSTART_REASON);
335 }
336
337 Maybe<Selection::AutoUserInitiated> mUserSelect;
338};
339
340} // namespace mozilla
341
342////////////BEGIN nsFrameSelection methods
343
344template Result<RefPtr<nsRange>, nsresult>
345nsFrameSelection::CreateRangeExtendedToSomewhere(
346 nsDirection aDirection, const nsSelectionAmount aAmount,
347 CaretMovementStyle aMovementStyle);
348template Result<RefPtr<StaticRange>, nsresult>
349nsFrameSelection::CreateRangeExtendedToSomewhere(
350 nsDirection aDirection, const nsSelectionAmount aAmount,
351 CaretMovementStyle aMovementStyle);
352
353nsFrameSelection::nsFrameSelection(PresShell* aPresShell, nsIContent* aLimiter,
354 const bool aAccessibleCaretEnabled) {
355 for (size_t i = 0; i < ArrayLength(mDomSelections); i++) {
356 mDomSelections[i] = new Selection(kPresentSelectionTypes[i], this);
357 }
358
359 if (AutoCopyListener::IsEnabled()) {
360 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
361 if (mDomSelections[index]) {
362 mDomSelections[index]->NotifyAutoCopy();
363 }
364 }
365
366 mPresShell = aPresShell;
367 mDragState = false;
368 mLimiters.mLimiter = aLimiter;
369
370 // This should only ever be initialized on the main thread, so we are OK here.
371 MOZ_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 371); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 371; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
372
373 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
374
375 mAccessibleCaretEnabled = aAccessibleCaretEnabled;
376 if (mAccessibleCaretEnabled) {
377 mDomSelections[index]->MaybeNotifyAccessibleCaretEventHub(aPresShell);
378 }
379
380 if (mDomSelections[index]) {
381 mDomSelections[index]->EnableSelectionChangeEvent();
382 }
383}
384
385nsFrameSelection::~nsFrameSelection() = default;
386
387NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)nsFrameSelection::cycleCollection nsFrameSelection::_cycleCollectorGlobal
;
388
389NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)void nsFrameSelection::cycleCollection::Unlink(void* p) { nsFrameSelection
* tmp = DowncastCCParticipant<nsFrameSelection>(p);
390 for (size_t i = 0; i < ArrayLength(tmp->mDomSelections); ++i) {
391 tmp->mDomSelections[i] = nullptr;
392 }
393 tmp->mHighlightSelections.Clear();
394
395 NS_IMPL_CYCLE_COLLECTION_UNLINK(ImplCycleCollectionUnlink(tmp->mTableSelection.mClosestInclusiveTableCellAncestor
);
396 mTableSelection.mClosestInclusiveTableCellAncestor)ImplCycleCollectionUnlink(tmp->mTableSelection.mClosestInclusiveTableCellAncestor
);
397 tmp->mTableSelection.mMode = TableSelectionMode::None;
398 tmp->mTableSelection.mDragSelectingCells = false;
399 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTableSelection.mStartSelectedCell)ImplCycleCollectionUnlink(tmp->mTableSelection.mStartSelectedCell
);
400 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTableSelection.mEndSelectedCell)ImplCycleCollectionUnlink(tmp->mTableSelection.mEndSelectedCell
);
401 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTableSelection.mAppendStartSelectedCell)ImplCycleCollectionUnlink(tmp->mTableSelection.mAppendStartSelectedCell
);
402 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTableSelection.mUnselectCellOnMouseUp)ImplCycleCollectionUnlink(tmp->mTableSelection.mUnselectCellOnMouseUp
);
403 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaintainedRange.mRange)ImplCycleCollectionUnlink(tmp->mMaintainedRange.mRange);
404 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiters.mLimiter)ImplCycleCollectionUnlink(tmp->mLimiters.mLimiter);
405 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiters.mAncestorLimiter)ImplCycleCollectionUnlink(tmp->mLimiters.mAncestorLimiter)
;
406NS_IMPL_CYCLE_COLLECTION_UNLINK_END(void)tmp; }
407NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)nsresult nsFrameSelection::cycleCollection::TraverseNative( void
* p, nsCycleCollectionTraversalCallback& cb) { nsFrameSelection
* tmp = DowncastCCParticipant<nsFrameSelection>(p); cb.
DescribeRefCountedNode(tmp->mRefCnt.get(), "nsFrameSelection"
);
408 if (tmp->mPresShell && tmp->mPresShell->GetDocument() &&
409 nsCCUncollectableMarker::InGeneration(
410 cb, tmp->mPresShell->GetDocument()->GetMarkedCCGeneration())) {
411 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
412 }
413 for (size_t i = 0; i < ArrayLength(tmp->mDomSelections); ++i) {
414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDomSelections[i])ImplCycleCollectionTraverse(cb, tmp->mDomSelections[i], "mDomSelections[i]"
, 0);
415 }
416
417 for (const auto& value : tmp->mHighlightSelections) {
418 CycleCollectionNoteChild(cb, value.second().get(),
419 "mHighlightSelections[]");
420 }
421
422 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(ImplCycleCollectionTraverse(cb, tmp->mTableSelection.mClosestInclusiveTableCellAncestor
, "mTableSelection.mClosestInclusiveTableCellAncestor", 0);
423 mTableSelection.mClosestInclusiveTableCellAncestor)ImplCycleCollectionTraverse(cb, tmp->mTableSelection.mClosestInclusiveTableCellAncestor
, "mTableSelection.mClosestInclusiveTableCellAncestor", 0);
424 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTableSelection.mStartSelectedCell)ImplCycleCollectionTraverse(cb, tmp->mTableSelection.mStartSelectedCell
, "mTableSelection.mStartSelectedCell", 0);
425 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTableSelection.mEndSelectedCell)ImplCycleCollectionTraverse(cb, tmp->mTableSelection.mEndSelectedCell
, "mTableSelection.mEndSelectedCell", 0);
426 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTableSelection.mAppendStartSelectedCell)ImplCycleCollectionTraverse(cb, tmp->mTableSelection.mAppendStartSelectedCell
, "mTableSelection.mAppendStartSelectedCell", 0);
427 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTableSelection.mUnselectCellOnMouseUp)ImplCycleCollectionTraverse(cb, tmp->mTableSelection.mUnselectCellOnMouseUp
, "mTableSelection.mUnselectCellOnMouseUp", 0);
428 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMaintainedRange.mRange)ImplCycleCollectionTraverse(cb, tmp->mMaintainedRange.mRange
, "mMaintainedRange.mRange", 0);
429 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiters.mLimiter)ImplCycleCollectionTraverse(cb, tmp->mLimiters.mLimiter, "mLimiters.mLimiter"
, 0);
430 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiters.mAncestorLimiter)ImplCycleCollectionTraverse(cb, tmp->mLimiters.mAncestorLimiter
, "mLimiters.mAncestorLimiter", 0);
431NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END(void)tmp; return NS_OK; }
432
433bool nsFrameSelection::Caret::IsVisualMovement(
434 bool aContinueSelection, CaretMovementStyle aMovementStyle) const {
435 int32_t movementFlag = StaticPrefs::bidi_edit_caret_movement_style();
436 return aMovementStyle == eVisual ||
437 (aMovementStyle == eUsePrefStyle &&
438 (movementFlag == 1 || (movementFlag == 2 && !aContinueSelection)));
439}
440
441// Get the x (or y, in vertical writing mode) position requested
442// by the Key Handling for line-up/down
443nsresult nsFrameSelection::DesiredCaretPos::FetchPos(
444 nsPoint& aDesiredCaretPos, const PresShell& aPresShell,
445 Selection& aNormalSelection) const {
446 MOZ_ASSERT(aNormalSelection.GetType() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.GetType() == SelectionType::eNormal
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.GetType() == SelectionType::eNormal
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aNormalSelection.GetType() == SelectionType::eNormal", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.GetType() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 446; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
447
448 if (mIsSet) {
449 aDesiredCaretPos = mValue;
450 return NS_OK;
451 }
452
453 RefPtr<nsCaret> caret = aPresShell.GetCaret();
454 if (!caret) {
455 return NS_ERROR_NULL_POINTER;
456 }
457
458 caret->SetSelection(&aNormalSelection);
459
460 nsRect coord;
461 nsIFrame* caretFrame = caret->GetGeometry(&coord);
462 if (!caretFrame) {
463 return NS_ERROR_FAILURE;
464 }
465 nsPoint viewOffset(0, 0);
466 nsView* view = nullptr;
467 caretFrame->GetOffsetFromView(viewOffset, &view);
468 if (view) {
469 coord += viewOffset;
470 }
471 aDesiredCaretPos = coord.TopLeft();
472 return NS_OK;
473}
474
475void nsFrameSelection::InvalidateDesiredCaretPos() // do not listen to
476 // mDesiredCaretPos.mValue;
477 // you must get another.
478{
479 mDesiredCaretPos.Invalidate();
480}
481
482void nsFrameSelection::DesiredCaretPos::Invalidate() { mIsSet = false; }
483
484void nsFrameSelection::DesiredCaretPos::Set(const nsPoint& aPos) {
485 mValue = aPos;
486 mIsSet = true;
487}
488
489nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(
490 nsIFrame* aFrame, const nsPoint& aPoint, nsIFrame** aRetFrame,
491 nsPoint& aRetPoint) const {
492 //
493 // The whole point of this method is to return a frame and point that
494 // that lie within the same valid subtree as the anchor node's frame,
495 // for use with the method GetContentAndOffsetsFromPoint().
496 //
497 // A valid subtree is defined to be one where all the content nodes in
498 // the tree have a valid parent-child relationship.
499 //
500 // If the anchor frame and aFrame are in the same subtree, aFrame will
501 // be returned in aRetFrame. If they are in different subtrees, we
502 // return the frame for the root of the subtree.
503 //
504
505 if (!aFrame || !aRetFrame) {
506 return NS_ERROR_NULL_POINTER;
507 }
508
509 *aRetFrame = aFrame;
510 aRetPoint = aPoint;
511
512 //
513 // Get the frame and content for the selection's anchor point!
514 //
515
516 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
517 if (!mDomSelections[index]) {
518 return NS_ERROR_NULL_POINTER;
519 }
520
521 nsCOMPtr<nsIContent> anchorContent =
522 do_QueryInterface(mDomSelections[index]->GetAnchorNode());
523 if (!anchorContent) {
524 return NS_ERROR_FAILURE;
525 }
526
527 //
528 // Now find the root of the subtree containing the anchor's content.
529 //
530
531 NS_ENSURE_STATE(mPresShell)do { if ((__builtin_expect(!!(!(mPresShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mPresShell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 531); return NS_ERROR_UNEXPECTED; } } while (false)
;
532 RefPtr<PresShell> presShell = mPresShell;
533 nsIContent* anchorRoot =
534 anchorContent
535 ->GetSelectionRootContent(
536 presShell,
537 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */);
538 NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED)do { if ((__builtin_expect(!!(!(anchorRoot)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "anchorRoot" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 538); return NS_ERROR_UNEXPECTED; } } while (false)
;
539
540 //
541 // Now find the root of the subtree containing aFrame's content.
542 //
543
544 nsCOMPtr<nsIContent> content = aFrame->GetContent();
545
546 if (content) {
547 nsIContent* contentRoot =
548 content->GetSelectionRootContent(
549 presShell, StaticPrefs::
550 dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */);
551 NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED)do { if ((__builtin_expect(!!(!(contentRoot)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "contentRoot" ") failed"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 551); return NS_ERROR_UNEXPECTED; } } while (false)
;
552
553 if (anchorRoot == contentRoot) {
554 // If the aFrame's content isn't the capturing content, it should be
555 // a descendant. At this time, we can return simply.
556 nsIContent* capturedContent = PresShell::GetCapturingContent();
557 if (capturedContent != content) {
558 return NS_OK;
559 }
560
561 // Find the frame under the mouse cursor with the root frame.
562 // At this time, don't use the anchor's frame because it may not have
563 // fixed positioned frames.
564 nsIFrame* rootFrame = presShell->GetRootFrame();
565 nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
566 nsIFrame* cursorFrame =
567 nsLayoutUtils::GetFrameForPoint(RelativeTo{rootFrame}, ptInRoot);
568
569 // If the mouse cursor in on a frame which is descendant of same
570 // selection root, we can expand the selection to the frame.
571 if (cursorFrame && cursorFrame->PresShell() == presShell) {
572 nsCOMPtr<nsIContent> cursorContent = cursorFrame->GetContent();
573 NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(cursorContent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "cursorContent" ") failed"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 573); return NS_ERROR_FAILURE; } } while (false)
;
574 nsIContent* cursorContentRoot = cursorContent->GetSelectionRootContent(
575 presShell, StaticPrefs::
576 dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */);
577 NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED)do { if ((__builtin_expect(!!(!(cursorContentRoot)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "cursorContentRoot" ") failed"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 577); return NS_ERROR_UNEXPECTED; } } while (false)
;
578 if (cursorContentRoot == anchorRoot) {
579 *aRetFrame = cursorFrame;
580 aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
581 return NS_OK;
582 }
583 }
584 // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
585 // cursor is out of the window), we should use the frame of the anchor
586 // root.
587 }
588 }
589
590 //
591 // When we can't find a frame which is under the mouse cursor and has a same
592 // selection root as the anchor node's, we should return the selection root
593 // frame.
594 //
595
596 *aRetFrame = anchorRoot->GetPrimaryFrame();
597
598 if (!*aRetFrame) {
599 return NS_ERROR_FAILURE;
600 }
601
602 //
603 // Now make sure that aRetPoint is converted to the same coordinate
604 // system used by aRetFrame.
605 //
606
607 aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
608
609 return NS_OK;
610}
611
612void nsFrameSelection::SetCaretBidiLevelAndMaybeSchedulePaint(
613 mozilla::intl::BidiEmbeddingLevel aLevel) {
614 // If the current level is undefined, we have just inserted new text.
615 // In this case, we don't want to reset the keyboard language
616 mCaret.mBidiLevel = aLevel;
617
618 RefPtr<nsCaret> caret;
619 if (mPresShell && (caret = mPresShell->GetCaret())) {
620 caret->SchedulePaint();
621 }
622}
623
624mozilla::intl::BidiEmbeddingLevel nsFrameSelection::GetCaretBidiLevel() const {
625 return mCaret.mBidiLevel;
626}
627
628void nsFrameSelection::UndefineCaretBidiLevel() {
629 mCaret.mBidiLevel = mozilla::intl::BidiEmbeddingLevel(mCaret.mBidiLevel |
630 BIDI_LEVEL_UNDEFINEDmozilla::intl::BidiEmbeddingLevel(0x80));
631}
632
633#ifdef PRINT_RANGE
634void printRange(nsRange* aDomRange) {
635 if (!aDomRange) {
636 printf("NULL Range\n");
637 }
638 nsINode* startNode = aDomRange->GetStartContainer();
639 nsINode* endNode = aDomRange->GetEndContainer();
640 int32_t startOffset = aDomRange->StartOffset();
641 int32_t endOffset = aDomRange->EndOffset();
642
643 printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
644 (unsigned long)aDomRange, (unsigned long)startNode, (long)startOffset,
645 (unsigned long)endNode, (long)endOffset);
646}
647#endif /* PRINT_RANGE */
648
649static nsAtom* GetTag(nsINode* aNode) {
650 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
651 if (!content) {
652 MOZ_ASSERT_UNREACHABLE("bad node passed to GetTag()")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"bad node passed to GetTag()" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 652); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "bad node passed to GetTag()" ")"
); do { *((volatile int*)__null) = 652; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
653 return nullptr;
654 }
655
656 return content->NodeInfo()->NameAtom();
657}
658
659/**
660 * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor.
661 */
662static nsINode* GetClosestInclusiveTableCellAncestor(nsINode* aDomNode) {
663 if (!aDomNode) return nullptr;
664 nsINode* current = aDomNode;
665 // Start with current node and look for a table cell
666 while (current) {
667 nsAtom* tag = GetTag(current);
668 if (tag == nsGkAtoms::td || tag == nsGkAtoms::th) return current;
669 current = current->GetParent();
670 }
671 return nullptr;
672}
673
674static nsDirection GetCaretDirection(const nsIFrame& aFrame,
675 nsDirection aDirection,
676 bool aVisualMovement) {
677 const mozilla::intl::BidiDirection paragraphDirection =
678 nsBidiPresUtils::ParagraphDirection(&aFrame);
679 return (aVisualMovement &&
680 paragraphDirection == mozilla::intl::BidiDirection::RTL)
681 ? nsDirection(1 - aDirection)
682 : aDirection;
683}
684
685nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
686 bool aContinueSelection,
687 const nsSelectionAmount aAmount,
688 CaretMovementStyle aMovementStyle) {
689 NS_ENSURE_STATE(mPresShell)do { if ((__builtin_expect(!!(!(mPresShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mPresShell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 689); return NS_ERROR_UNEXPECTED; } } while (false)
;
690 // Flush out layout, since we need it to be up to date to do caret
691 // positioning.
692 OwningNonNull<PresShell> presShell(*mPresShell);
693 presShell->FlushPendingNotifications(FlushType::Layout);
694
695 if (!mPresShell) {
696 return NS_OK;
697 }
698
699 nsPresContext* context = mPresShell->GetPresContext();
700 if (!context) {
701 return NS_ERROR_FAILURE;
702 }
703
704 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
705 const RefPtr<Selection> sel = mDomSelections[index];
706 if (!sel) {
707 return NS_ERROR_NULL_POINTER;
708 }
709
710 int32_t scrollFlags = Selection::SCROLL_FOR_CARET_MOVE;
711 if (sel->IsEditorSelection()) {
712 // If caret moves in editor, it should cause scrolling even if it's in
713 // overflow: hidden;.
714 scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
715 }
716
717 const bool doCollapse = [&] {
718 if (sel->IsCollapsed() || aContinueSelection) {
719 return false;
720 }
721 if (aAmount > eSelectLine) {
722 return false;
723 }
724 int32_t caretStyle = StaticPrefs::layout_selection_caret_style();
725 return caretStyle == 2 || (caretStyle == 0 && aAmount != eSelectLine);
726 }();
727
728 if (doCollapse) {
729 if (aDirection == eDirPrevious) {
730 SetChangeReasons(nsISelectionListener::COLLAPSETOSTART_REASON);
731 mCaret.mHint = CaretAssociationHint::After;
732 } else {
733 SetChangeReasons(nsISelectionListener::COLLAPSETOEND_REASON);
734 mCaret.mHint = CaretAssociationHint::Before;
735 }
736 } else {
737 SetChangeReasons(nsISelectionListener::KEYPRESS_REASON);
738 }
739
740 mCaretMoveAmount = aAmount;
741
742 AutoPrepareFocusRange prep(sel, false);
743
744 // we must keep this around and revalidate it when its just UP/DOWN
745 nsPoint desiredPos(0, 0);
746
747 if (aAmount == eSelectLine) {
748 nsresult result = mDesiredCaretPos.FetchPos(desiredPos, *mPresShell, *sel);
749 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
750 return result;
751 }
752 mDesiredCaretPos.Set(desiredPos);
753 }
754
755 bool visualMovement =
756 mCaret.IsVisualMovement(aContinueSelection, aMovementStyle);
757 const PrimaryFrameData frameForFocus =
758 sel->GetPrimaryFrameForCaretAtFocusNode(visualMovement);
759 if (!frameForFocus.mFrame) {
760 return NS_ERROR_FAILURE;
761 }
762 if (visualMovement) {
763 // FYI: This was done during a call of GetPrimaryFrameForCaretAtFocusNode.
764 // Therefore, this may not be intended by the original author.
765 SetHint(frameForFocus.mHint);
766 }
767
768 Result<bool, nsresult> isIntraLineCaretMove =
769 SelectionMovementUtils::IsIntraLineCaretMove(aAmount);
770 nsDirection direction{aDirection};
771 if (isIntraLineCaretMove.isErr()) {
772 return isIntraLineCaretMove.unwrapErr();
773 }
774 if (isIntraLineCaretMove.inspect()) {
775 // Forget old caret position for moving caret to different line since
776 // caret position may be changed.
777 mDesiredCaretPos.Invalidate();
778 direction =
779 GetCaretDirection(*frameForFocus.mFrame, aDirection, visualMovement);
780 }
781
782 if (doCollapse) {
783 const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
784 if (anchorFocusRange) {
785 RefPtr<nsINode> node;
786 uint32_t offset;
787 if (visualMovement &&
788 nsBidiPresUtils::IsReversedDirectionFrame(frameForFocus.mFrame)) {
789 direction = nsDirection(1 - direction);
790 }
791 if (direction == eDirPrevious) {
792 node = anchorFocusRange->GetStartContainer();
793 offset = anchorFocusRange->StartOffset();
794 } else {
795 node = anchorFocusRange->GetEndContainer();
796 offset = anchorFocusRange->EndOffset();
797 }
798 sel->CollapseInLimiter(node, offset);
799 }
800 sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
801 ScrollAxis(), ScrollAxis(), scrollFlags);
802 return NS_OK;
803 }
804
805 CaretAssociationHint tHint(
Value stored to 'tHint' during its initialization is never read
806 mCaret.mHint); // temporary variable so we dont set
807 // mCaret.mHint until it is necessary
808
809 Result<PeekOffsetStruct, nsresult> result = PeekOffsetForCaretMove(
810 direction, aContinueSelection, aAmount, aMovementStyle, desiredPos);
811 nsresult rv;
812 if (result.isOk() && result.inspect().mResultContent) {
813 const PeekOffsetStruct& pos = result.inspect();
814 nsIFrame* theFrame;
815 int32_t frameStart, frameEnd;
816
817 if (aAmount <= eSelectWordNoSpace) {
818 // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does
819 // not set pos.mAttachForward, so determine the hint here based on the
820 // result frame and offset: If we're at the end of a text frame, set the
821 // hint to ASSOCIATE_BEFORE to indicate that we want the caret displayed
822 // at the end of this frame, not at the beginning of the next one.
823 theFrame = pos.mResultFrame;
824 std::tie(frameStart, frameEnd) = theFrame->GetOffsets();
825 if (frameEnd == pos.mContentOffset && !(frameStart == 0 && frameEnd == 0))
826 tHint = CaretAssociationHint::Before;
827 else
828 tHint = CaretAssociationHint::After;
829 } else {
830 // For up/down and home/end, pos.mResultFrame might not be set correctly,
831 // or not at all. In these cases, get the frame based on the content and
832 // hint returned by PeekOffset().
833 tHint = pos.mAttach;
834 theFrame = SelectionMovementUtils::GetFrameForNodeOffset(
835 pos.mResultContent, pos.mContentOffset, tHint);
836 if (!theFrame) return NS_ERROR_FAILURE;
837
838 std::tie(frameStart, frameEnd) = theFrame->GetOffsets();
839 }
840
841 if (context->BidiEnabled()) {
842 switch (aAmount) {
843 case eSelectBeginLine:
844 case eSelectEndLine: {
845 // In Bidi contexts, PeekOffset calculates pos.mContentOffset
846 // differently depending on whether the movement is visual or logical.
847 // For visual movement, pos.mContentOffset depends on the direction-
848 // ality of the first/last frame on the line (theFrame), and the caret
849 // directionality must correspond.
850 FrameBidiData bidiData = theFrame->GetBidiData();
851 SetCaretBidiLevelAndMaybeSchedulePaint(
852 visualMovement ? bidiData.embeddingLevel : bidiData.baseLevel);
853 break;
854 }
855 default:
856 // If the current position is not a frame boundary, it's enough just
857 // to take the Bidi level of the current frame
858 if ((pos.mContentOffset != frameStart &&
859 pos.mContentOffset != frameEnd) ||
860 eSelectLine == aAmount) {
861 SetCaretBidiLevelAndMaybeSchedulePaint(
862 theFrame->GetEmbeddingLevel());
863 } else {
864 BidiLevelFromMove(mPresShell, pos.mResultContent,
865 pos.mContentOffset, aAmount, tHint);
866 }
867 }
868 }
869 // "pos" is on the stack, so pos.mResultContent has stack lifetime, so using
870 // MOZ_KnownLive is ok.
871 const FocusMode focusMode = aContinueSelection
872 ? FocusMode::kExtendSelection
873 : FocusMode::kCollapseToNewPoint;
874 rv = TakeFocus(MOZ_KnownLive(*pos.mResultContent)(*pos.mResultContent), pos.mContentOffset,
875 pos.mContentOffset, tHint, focusMode);
876 } else if (aAmount <= eSelectWordNoSpace && direction == eDirNext &&
877 !aContinueSelection) {
878 // Collapse selection if PeekOffset failed, we either
879 // 1. bumped into the BRFrame, bug 207623
880 // 2. had select-all in a text input (DIV range), bug 352759.
881 bool isBRFrame = frameForFocus.mFrame->IsBrFrame();
882 RefPtr<nsINode> node = sel->GetFocusNode();
883 sel->CollapseInLimiter(node, sel->FocusOffset());
884 // Note: 'frameForFocus.mFrame' might be dead here.
885 if (!isBRFrame) {
886 mCaret.mHint = CaretAssociationHint::Before; // We're now at the end of
887 // the frame to the left.
888 }
889 rv = NS_OK;
890 } else {
891 rv = result.isErr() ? result.unwrapErr() : NS_OK;
892 }
893 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
894 rv = sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
895 ScrollAxis(), ScrollAxis(), scrollFlags);
896 }
897
898 return rv;
899}
900
901Result<PeekOffsetStruct, nsresult> nsFrameSelection::PeekOffsetForCaretMove(
902 nsDirection aDirection, bool aContinueSelection,
903 const nsSelectionAmount aAmount, CaretMovementStyle aMovementStyle,
904 const nsPoint& aDesiredCaretPos) const {
905 if (!mPresShell) {
906 return Err(NS_ERROR_NULL_POINTER);
907 }
908
909 Selection* selection =
910 mDomSelections[GetIndexFromSelectionType(SelectionType::eNormal)];
911 if (!selection) {
912 return Err(NS_ERROR_NULL_POINTER);
913 }
914
915 nsIContent* content = nsIContent::FromNodeOrNull(selection->GetFocusNode());
916 if (!content) {
917 return Err(NS_ERROR_FAILURE);
918 }
919 MOZ_ASSERT(mPresShell->GetDocument() == content->GetComposedDoc())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mPresShell->GetDocument() == content->GetComposedDoc
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mPresShell->GetDocument() == content->GetComposedDoc
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mPresShell->GetDocument() == content->GetComposedDoc()"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 919); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mPresShell->GetDocument() == content->GetComposedDoc()"
")"); do { *((volatile int*)__null) = 919; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
920
921 const bool visualMovement =
922 mCaret.IsVisualMovement(aContinueSelection, aMovementStyle);
923
924 PeekOffsetOptions options;
925 // set data using mLimiters.mLimiter to stop on scroll views. If we have a
926 // limiter then we stop peeking when we hit scrollable views. If no limiter
927 // then just let it go ahead
928 if (mLimiters.mLimiter) {
929 options += PeekOffsetOption::StopAtScroller;
930 }
931 if (visualMovement) {
932 options += PeekOffsetOption::Visual;
933 }
934 if (aContinueSelection) {
935 options += PeekOffsetOption::Extend;
936 }
937 const Element* ancestorLimiter =
938 Element::FromNodeOrNull(GetAncestorLimiter());
939 if (selection->IsEditorSelection()) {
940 options += PeekOffsetOption::ForceEditableRegion;
941 // If the editor has not receive `focus` event, it may have not set ancestor
942 // limiter. Then, we need to compute it here for the caret move.
943 if (!ancestorLimiter) {
944 // Editing hosts can be nested. Therefore, computing selection root from
945 // selection range may be different from the focused editing host.
946 // Therefore, we may need to use a non-closest inclusive ancestor editing
947 // host of selection range container. On the other hand, selection ranges
948 // may be outside of focused editing host. In such case, we should use
949 // the closest editing host as the ancestor limiter instead.
950 PresShell* const presShell = selection->GetPresShell();
951 const Document* const doc =
952 presShell ? presShell->GetDocument() : nullptr;
953 if (const nsPIDOMWindowInner* const win =
954 doc ? doc->GetInnerWindow() : nullptr) {
955 const Element* const focusedElement = win->GetFocusedElement();
956 const Element* closestEditingHost = nullptr;
957 for (const Element* element :
958 content->InclusiveAncestorsOfType<Element>()) {
959 if (element->IsEditingHost()) {
960 if (!closestEditingHost) {
961 closestEditingHost = element;
962 }
963 if (focusedElement == element) {
964 ancestorLimiter = focusedElement;
965 break;
966 }
967 }
968 }
969 if (!ancestorLimiter) {
970 ancestorLimiter = closestEditingHost;
971 }
972 }
973 // If it's the root element, we don't need to limit the new caret
974 // position.
975 if (ancestorLimiter && !ancestorLimiter->GetParent()) {
976 ancestorLimiter = nullptr;
977 }
978 }
979 }
980
981 return SelectionMovementUtils::PeekOffsetForCaretMove(
982 content, selection->FocusOffset(), aDirection, GetHint(),
983 GetCaretBidiLevel(), aAmount, aDesiredCaretPos, options, ancestorLimiter);
984}
985
986nsPrevNextBidiLevels nsFrameSelection::GetPrevNextBidiLevels(
987 nsIContent* aNode, uint32_t aContentOffset, bool aJumpLines) const {
988 return SelectionMovementUtils::GetPrevNextBidiLevels(
989 aNode, aContentOffset, mCaret.mHint, aJumpLines);
990}
991
992nsresult nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount) {
993 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
994 if (!mDomSelections[index]) {
995 return NS_ERROR_NULL_POINTER;
996 }
997
998 mMaintainedRange.MaintainAnchorFocusRange(*mDomSelections[index], aAmount);
999
1000 return NS_OK;
1001}
1002
1003void nsFrameSelection::BidiLevelFromMove(PresShell* aPresShell,
1004 nsIContent* aNode,
1005 uint32_t aContentOffset,
1006 nsSelectionAmount aAmount,
1007 CaretAssociationHint aHint) {
1008 switch (aAmount) {
1009 // Movement within the line: the new cursor Bidi level is the level of the
1010 // last character moved over
1011 case eSelectCharacter:
1012 case eSelectCluster:
1013 case eSelectWord:
1014 case eSelectWordNoSpace:
1015 case eSelectBeginLine:
1016 case eSelectEndLine:
1017 case eSelectNoAmount: {
1018 nsPrevNextBidiLevels levels =
1019 SelectionMovementUtils::GetPrevNextBidiLevels(aNode, aContentOffset,
1020 aHint, false);
1021
1022 SetCaretBidiLevelAndMaybeSchedulePaint(
1023 aHint == CaretAssociationHint::Before ? levels.mLevelBefore
1024 : levels.mLevelAfter);
1025 break;
1026 }
1027 /*
1028 // Up and Down: the new cursor Bidi level is the smaller of the two
1029 surrounding characters case eSelectLine: case eSelectParagraph:
1030 GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame,
1031 &secondFrame, &firstLevel, &secondLevel);
1032 aPresShell->SetCaretBidiLevelAndMaybeSchedulePaint(std::min(firstLevel,
1033 secondLevel)); break;
1034 */
1035
1036 default:
1037 UndefineCaretBidiLevel();
1038 }
1039}
1040
1041void nsFrameSelection::BidiLevelFromClick(nsIContent* aNode,
1042 uint32_t aContentOffset) {
1043 nsIFrame* clickInFrame = nullptr;
1044 clickInFrame = SelectionMovementUtils::GetFrameForNodeOffset(
1045 aNode, aContentOffset, mCaret.mHint);
1046 if (!clickInFrame) return;
1047
1048 SetCaretBidiLevelAndMaybeSchedulePaint(clickInFrame->GetEmbeddingLevel());
1049}
1050
1051void nsFrameSelection::MaintainedRange::AdjustNormalSelection(
1052 const nsIContent* aContent, const int32_t aOffset,
1053 Selection& aNormalSelection) const {
1054 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 1054; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1055
1056 if (!mRange || !aContent) {
1057 return;
1058 }
1059
1060 nsINode* rangeStartNode = mRange->GetStartContainer();
1061 nsINode* rangeEndNode = mRange->GetEndContainer();
1062 const uint32_t rangeStartOffset = mRange->StartOffset();
1063 const uint32_t rangeEndOffset = mRange->EndOffset();
1064
1065 NS_ASSERTION(aOffset >= 0, "aOffset should not be negative")do { if (!(aOffset >= 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "aOffset should not be negative", "aOffset >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1065); MOZ_PretendNoReturn(); } } while (0)
;
1066 const Maybe<int32_t> relToStart =
1067 nsContentUtils::ComparePoints_AllowNegativeOffsets(
1068 rangeStartNode, rangeStartOffset, aContent, aOffset);
1069 if (NS_WARN_IF(!relToStart)NS_warn_if_impl(!relToStart, "!relToStart", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1069)
) {
1070 // Potentially handle this properly when Selection across Shadow DOM
1071 // boundary is implemented
1072 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1607497).
1073 return;
1074 }
1075
1076 const Maybe<int32_t> relToEnd =
1077 nsContentUtils::ComparePoints_AllowNegativeOffsets(
1078 rangeEndNode, rangeEndOffset, aContent, aOffset);
1079 if (NS_WARN_IF(!relToEnd)NS_warn_if_impl(!relToEnd, "!relToEnd", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1079)
) {
1080 // Potentially handle this properly when Selection across Shadow DOM
1081 // boundary is implemented
1082 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1607497).
1083 return;
1084 }
1085
1086 // If aContent/aOffset is inside (or at the edge of) the maintained
1087 // selection, or if it is on the "anchor" side of the maintained selection,
1088 // we need to do something.
1089 if ((*relToStart <= 0 && *relToEnd >= 0) ||
1090 (*relToStart > 0 && aNormalSelection.GetDirection() == eDirNext) ||
1091 (*relToEnd < 0 && aNormalSelection.GetDirection() == eDirPrevious)) {
1092 // Set the current range to the maintained range.
1093 aNormalSelection.ReplaceAnchorFocusRange(mRange);
1094 // Set the direction of the selection so that the anchor will be on the
1095 // far side of the maintained selection, relative to aContent/aOffset.
1096 aNormalSelection.SetDirection(*relToStart > 0 ? eDirPrevious : eDirNext);
1097 }
1098}
1099
1100void nsFrameSelection::MaintainedRange::AdjustContentOffsets(
1101 nsIFrame::ContentOffsets& aOffsets, StopAtScroller aStopAtScroller) const {
1102 // Adjust offsets according to maintained amount
1103 if (mRange && mAmount != eSelectNoAmount) {
1104 nsINode* rangenode = mRange->GetStartContainer();
1105 int32_t rangeOffset = mRange->StartOffset();
1106 const Maybe<int32_t> relativePosition = nsContentUtils::ComparePoints(
1107 rangenode, rangeOffset, aOffsets.content, aOffsets.offset);
1108 if (NS_WARN_IF(!relativePosition)NS_warn_if_impl(!relativePosition, "!relativePosition", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1108)
) {
1109 // Potentially handle this properly when Selection across Shadow DOM
1110 // boundary is implemented
1111 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1607497).
1112 return;
1113 }
1114
1115 nsDirection direction = *relativePosition > 0 ? eDirPrevious : eDirNext;
1116 nsSelectionAmount amount = mAmount;
1117 if (amount == eSelectBeginLine && direction == eDirNext) {
1118 amount = eSelectEndLine;
1119 }
1120
1121 uint32_t offset;
1122 nsIFrame* frame = SelectionMovementUtils::GetFrameForNodeOffset(
1123 aOffsets.content, aOffsets.offset, CaretAssociationHint::After,
1124 &offset);
1125
1126 PeekOffsetOptions peekOffsetOptions{};
1127 if (aStopAtScroller == StopAtScroller::Yes) {
1128 peekOffsetOptions += PeekOffsetOption::StopAtScroller;
1129 }
1130 if (frame && amount == eSelectWord && direction == eDirPrevious) {
1131 // To avoid selecting the previous word when at start of word,
1132 // first move one character forward.
1133 PeekOffsetStruct charPos(eSelectCharacter, eDirNext,
1134 static_cast<int32_t>(offset), nsPoint(0, 0),
1135 peekOffsetOptions);
1136 if (NS_SUCCEEDED(frame->PeekOffset(&charPos))((bool)(__builtin_expect(!!(!NS_FAILED_impl(frame->PeekOffset
(&charPos))), 1)))
) {
1137 frame = charPos.mResultFrame;
1138 offset = charPos.mContentOffset;
1139 }
1140 }
1141
1142 PeekOffsetStruct pos(amount, direction, static_cast<int32_t>(offset),
1143 nsPoint(0, 0), peekOffsetOptions);
1144 if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos))((bool)(__builtin_expect(!!(!NS_FAILED_impl(frame->PeekOffset
(&pos))), 1)))
&& pos.mResultContent) {
1145 aOffsets.content = pos.mResultContent;
1146 aOffsets.offset = pos.mContentOffset;
1147 }
1148 }
1149}
1150
1151void nsFrameSelection::MaintainedRange::MaintainAnchorFocusRange(
1152 const Selection& aNormalSelection, const nsSelectionAmount aAmount) {
1153 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1153); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 1153; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1154
1155 mAmount = aAmount;
1156
1157 const nsRange* anchorFocusRange = aNormalSelection.GetAnchorFocusRange();
1158 if (anchorFocusRange && aAmount != eSelectNoAmount) {
1159 mRange = anchorFocusRange->CloneRange();
1160 return;
1161 }
1162
1163 mRange = nullptr;
1164}
1165
1166nsresult nsFrameSelection::HandleClick(nsIContent* aNewFocus,
1167 uint32_t aContentOffset,
1168 uint32_t aContentEndOffset,
1169 const FocusMode aFocusMode,
1170 CaretAssociationHint aHint) {
1171 if (!aNewFocus) return NS_ERROR_INVALID_ARG;
1172
1173 if (MOZ_LOG_TEST(sFrameSelectionLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(sFrameSelectionLog
, LogLevel::Debug)), 0))
) {
1174 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1175 MOZ_LOG(sFrameSelectionLog, LogLevel::Debug,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i"
, __FUNCTION__, mDomSelections[index] ? mDomSelections[index]
.get() : nullptr, aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aFocusMode)); } } while (0)
1176 ("%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i",do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i"
, __FUNCTION__, mDomSelections[index] ? mDomSelections[index]
.get() : nullptr, aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aFocusMode)); } } while (0)
1177 __FUNCTION__,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i"
, __FUNCTION__, mDomSelections[index] ? mDomSelections[index]
.get() : nullptr, aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aFocusMode)); } } while (0)
1178 mDomSelections[index] ? mDomSelections[index].get() : nullptr,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i"
, __FUNCTION__, mDomSelections[index] ? mDomSelections[index]
.get() : nullptr, aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aFocusMode)); } } while (0)
1179 aNewFocus, aContentOffset, aContentEndOffset,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i"
, __FUNCTION__, mDomSelections[index] ? mDomSelections[index]
.get() : nullptr, aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aFocusMode)); } } while (0)
1180 static_cast<int>(aFocusMode)))do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: selection=%p, new focus=%p, offsets=(%u,%u), focus mode=%i"
, __FUNCTION__, mDomSelections[index] ? mDomSelections[index]
.get() : nullptr, aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aFocusMode)); } } while (0)
;
1181 }
1182
1183 mDesiredCaretPos.Invalidate();
1184
1185 if (aFocusMode != FocusMode::kExtendSelection) {
1186 mMaintainedRange.mRange = nullptr;
1187 if (!IsValidSelectionPoint(aNewFocus)) {
1188 mLimiters.mAncestorLimiter = nullptr;
1189 }
1190 }
1191
1192 // Don't take focus when dragging off of a table
1193 if (!mTableSelection.mDragSelectingCells) {
1194 BidiLevelFromClick(aNewFocus, aContentOffset);
1195 SetChangeReasons(nsISelectionListener::MOUSEDOWN_REASON +
1196 nsISelectionListener::DRAG_REASON);
1197
1198 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1199 RefPtr<Selection> selection = mDomSelections[index];
1200 MOZ_ASSERT(selection)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(selection)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(selection))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("selection", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "selection" ")"
); do { *((volatile int*)__null) = 1200; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1201
1202 if (aFocusMode == FocusMode::kExtendSelection) {
1203 mMaintainedRange.AdjustNormalSelection(aNewFocus, aContentOffset,
1204 *selection);
1205 }
1206
1207 AutoPrepareFocusRange prep(selection,
1208 aFocusMode == FocusMode::kMultiRangeSelection);
1209 return TakeFocus(*aNewFocus, aContentOffset, aContentEndOffset, aHint,
1210 aFocusMode);
1211 }
1212
1213 return NS_OK;
1214}
1215
1216void nsFrameSelection::HandleDrag(nsIFrame* aFrame, const nsPoint& aPoint) {
1217 if (!aFrame || !mPresShell) {
1218 return;
1219 }
1220
1221 nsresult result;
1222 nsIFrame* newFrame = 0;
1223 nsPoint newPoint;
1224
1225 result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame,
1226 newPoint);
1227 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) return;
1228 if (!newFrame) return;
1229
1230 nsIFrame::ContentOffsets offsets =
1231 newFrame->GetContentOffsetsFromPoint(newPoint);
1232 if (!offsets.content) return;
1233
1234 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1235 RefPtr<Selection> selection = mDomSelections[index];
1236 if (newFrame->IsSelected() && selection) {
1237 // `MOZ_KnownLive` required because of
1238 // https://bugzilla.mozilla.org/show_bug.cgi?id=1636889.
1239 mMaintainedRange.AdjustNormalSelection(MOZ_KnownLive(offsets.content)(offsets.content),
1240 offsets.offset, *selection);
1241 }
1242
1243 mMaintainedRange.AdjustContentOffsets(
1244 offsets, mLimiters.mLimiter ? MaintainedRange::StopAtScroller::Yes
1245 : MaintainedRange::StopAtScroller::No);
1246
1247 // TODO: no click has happened, rename `HandleClick`.
1248 HandleClick(MOZ_KnownLive(offsets.content)(offsets.content) /* bug 1636889 */, offsets.offset,
1249 offsets.offset, FocusMode::kExtendSelection, offsets.associate);
1250}
1251
1252nsresult nsFrameSelection::StartAutoScrollTimer(nsIFrame* aFrame,
1253 const nsPoint& aPoint,
1254 uint32_t aDelay) {
1255 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1256 if (!mDomSelections[index]) {
1257 return NS_ERROR_NULL_POINTER;
1258 }
1259
1260 RefPtr<Selection> selection = mDomSelections[index];
1261 return selection->StartAutoScrollTimer(aFrame, aPoint, aDelay);
1262}
1263
1264void nsFrameSelection::StopAutoScrollTimer() {
1265 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1266 if (!mDomSelections[index]) {
1267 return;
1268 }
1269
1270 mDomSelections[index]->StopAutoScrollTimer();
1271}
1272
1273// static
1274nsINode* nsFrameSelection::TableSelection::IsContentInActivelyEditableTableCell(
1275 nsPresContext* aContext, nsIContent* aContent) {
1276 if (!aContext) {
1277 return nullptr;
1278 }
1279
1280 RefPtr<HTMLEditor> htmlEditor = nsContentUtils::GetHTMLEditor(aContext);
1281 if (!htmlEditor) {
1282 return nullptr;
1283 }
1284
1285 nsINode* inclusiveTableCellAncestor =
1286 GetClosestInclusiveTableCellAncestor(aContent);
1287 if (!inclusiveTableCellAncestor) {
1288 return nullptr;
1289 }
1290
1291 const Element* editingHost = htmlEditor->ComputeEditingHost();
1292 if (!editingHost) {
1293 return nullptr;
1294 }
1295
1296 const bool editableCell =
1297 inclusiveTableCellAncestor->IsInclusiveDescendantOf(editingHost);
1298 return editableCell ? inclusiveTableCellAncestor : nullptr;
1299}
1300
1301namespace {
1302struct ParentAndOffset {
1303 explicit ParentAndOffset(const nsINode& aNode)
1304 : mParent{aNode.GetParent()},
1305 mOffset{mParent ? mParent->ComputeIndexOf_Deprecated(&aNode) : 0} {}
1306
1307 nsINode* mParent;
1308
1309 // 0, if there's no parent.
1310 int32_t mOffset;
1311};
1312
1313} // namespace
1314/**
1315hard to go from nodes to frames, easy the other way!
1316 */
1317nsresult nsFrameSelection::TakeFocus(nsIContent& aNewFocus,
1318 uint32_t aContentOffset,
1319 uint32_t aContentEndOffset,
1320 CaretAssociationHint aHint,
1321 const FocusMode aFocusMode) {
1322 NS_ENSURE_STATE(mPresShell)do { if ((__builtin_expect(!!(!(mPresShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mPresShell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1322); return NS_ERROR_UNEXPECTED; } } while (false)
;
1323
1324 if (!IsValidSelectionPoint(&aNewFocus)) {
1325 return NS_ERROR_FAILURE;
1326 }
1327
1328 MOZ_LOG(sFrameSelectionLog, LogLevel::Verbose,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "%s: new focus=%p, offsets=(%u, %u), hint=%i, focusMode=%i"
, __FUNCTION__, &aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aHint), static_cast<int>(aFocusMode
)); } } while (0)
1329 ("%s: new focus=%p, offsets=(%u, %u), hint=%i, focusMode=%i",do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "%s: new focus=%p, offsets=(%u, %u), hint=%i, focusMode=%i"
, __FUNCTION__, &aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aHint), static_cast<int>(aFocusMode
)); } } while (0)
1330 __FUNCTION__, &aNewFocus, aContentOffset, aContentEndOffset,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "%s: new focus=%p, offsets=(%u, %u), hint=%i, focusMode=%i"
, __FUNCTION__, &aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aHint), static_cast<int>(aFocusMode
)); } } while (0)
1331 static_cast<int>(aHint), static_cast<int>(aFocusMode)))do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "%s: new focus=%p, offsets=(%u, %u), hint=%i, focusMode=%i"
, __FUNCTION__, &aNewFocus, aContentOffset, aContentEndOffset
, static_cast<int>(aHint), static_cast<int>(aFocusMode
)); } } while (0)
;
1332
1333 mPresShell->FrameSelectionWillTakeFocus(*this);
1334
1335 // Clear all table selection data
1336 mTableSelection.mMode = TableSelectionMode::None;
1337 mTableSelection.mDragSelectingCells = false;
1338 mTableSelection.mStartSelectedCell = nullptr;
1339 mTableSelection.mEndSelectedCell = nullptr;
1340 mTableSelection.mAppendStartSelectedCell = nullptr;
1341 mCaret.mHint = aHint;
1342
1343 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1344 if (!mDomSelections[index]) return NS_ERROR_NULL_POINTER;
1345
1346 Maybe<Selection::AutoUserInitiated> userSelect;
1347 if (IsUserSelectionReason()) {
1348 userSelect.emplace(mDomSelections[index]);
1349 }
1350
1351 // traverse through document and unselect crap here
1352 switch (aFocusMode) {
1353 case FocusMode::kCollapseToNewPoint:
1354 [[fallthrough]];
1355 case FocusMode::kMultiRangeSelection: {
1356 // single click? setting cursor down
1357 const Batching saveBatching =
1358 mBatching; // hack to use the collapse code.
1359 mBatching.mCounter = 1;
1360
1361 RefPtr<Selection> selection = mDomSelections[index];
1362
1363 if (aFocusMode == FocusMode::kMultiRangeSelection) {
1364 // Remove existing collapsed ranges as there's no point in having
1365 // non-anchor/focus collapsed ranges.
1366 selection->RemoveCollapsedRanges();
1367
1368 ErrorResult error;
1369 RefPtr<nsRange> newRange = nsRange::Create(
1370 &aNewFocus, aContentOffset, &aNewFocus, aContentOffset, error);
1371 if (NS_WARN_IF(error.Failed())NS_warn_if_impl(error.Failed(), "error.Failed()", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1371)
) {
1372 return error.StealNSResult();
1373 }
1374 MOZ_ASSERT(newRange)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(newRange)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(newRange))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("newRange", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1374); AnnotateMozCrashReason("MOZ_ASSERT" "(" "newRange" ")"
); do { *((volatile int*)__null) = 1374; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1375 selection->AddRangeAndSelectFramesAndNotifyListeners(*newRange,
1376 IgnoreErrors());
1377 } else {
1378 bool oldDesiredPosSet =
1379 mDesiredCaretPos.mIsSet; // need to keep old desired
1380 // position if it was set.
1381 selection->CollapseInLimiter(&aNewFocus, aContentOffset);
1382 mDesiredCaretPos.mIsSet =
1383 oldDesiredPosSet; // now reset desired pos back.
1384 }
1385
1386 mBatching = saveBatching;
1387
1388 if (aContentEndOffset != aContentOffset) {
1389 selection->Extend(&aNewFocus, aContentEndOffset);
1390 }
1391
1392 // find out if we are inside a table. if so, find out which one and which
1393 // cell once we do that, the next time we get a takefocus, check the
1394 // parent tree. if we are no longer inside same table ,cell then switch to
1395 // table selection mode. BUT only do this in an editor
1396
1397 NS_ENSURE_STATE(mPresShell)do { if ((__builtin_expect(!!(!(mPresShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mPresShell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1397); return NS_ERROR_UNEXPECTED; } } while (false)
;
1398 RefPtr<nsPresContext> context = mPresShell->GetPresContext();
1399 mTableSelection.mClosestInclusiveTableCellAncestor = nullptr;
1400 if (nsINode* inclusiveTableCellAncestor =
1401 TableSelection::IsContentInActivelyEditableTableCell(
1402 context, &aNewFocus)) {
1403 mTableSelection.mClosestInclusiveTableCellAncestor =
1404 inclusiveTableCellAncestor;
1405 MOZ_LOG(sFrameSelectionLog, LogLevel::Debug,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: Collapsing into new cell", __FUNCTION__
); } } while (0)
1406 ("%s: Collapsing into new cell", __FUNCTION__))do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: Collapsing into new cell", __FUNCTION__
); } } while (0)
;
1407 }
1408
1409 break;
1410 }
1411 case FocusMode::kExtendSelection: {
1412 // Now update the range list:
1413 nsINode* inclusiveTableCellAncestor =
1414 GetClosestInclusiveTableCellAncestor(&aNewFocus);
1415 if (mTableSelection.mClosestInclusiveTableCellAncestor &&
1416 inclusiveTableCellAncestor &&
1417 inclusiveTableCellAncestor !=
1418 mTableSelection
1419 .mClosestInclusiveTableCellAncestor) // switch to cell
1420 // selection mode
1421 {
1422 MOZ_LOG(sFrameSelectionLog, LogLevel::Debug,do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: moving into new cell", __FUNCTION__);
} } while (0)
1423 ("%s: moving into new cell", __FUNCTION__))do { const ::mozilla::LogModule* moz_real_module = sFrameSelectionLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "%s: moving into new cell", __FUNCTION__);
} } while (0)
;
1424
1425 WidgetMouseEvent event(false, eVoidEvent, nullptr,
1426 WidgetMouseEvent::eReal);
1427
1428 // Start selecting in the cell we were in before
1429 ParentAndOffset parentAndOffset{
1430 *mTableSelection.mClosestInclusiveTableCellAncestor};
1431 if (parentAndOffset.mParent) {
1432 const nsresult result = HandleTableSelection(
1433 parentAndOffset.mParent, parentAndOffset.mOffset,
1434 TableSelectionMode::Cell, &event);
1435 if (NS_WARN_IF(NS_FAILED(result))NS_warn_if_impl(((bool)(__builtin_expect(!!(NS_FAILED_impl(result
)), 0))), "NS_FAILED(result)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1435)
) {
1436 return result;
1437 }
1438 }
1439
1440 // Find the parent of this new cell and extend selection to it
1441 parentAndOffset = ParentAndOffset{*inclusiveTableCellAncestor};
1442
1443 // XXXX We need to REALLY get the current key shift state
1444 // (we'd need to add event listener -- let's not bother for now)
1445 event.mModifiers &= ~MODIFIER_SHIFT; // aContinueSelection;
1446 if (parentAndOffset.mParent) {
1447 mTableSelection.mClosestInclusiveTableCellAncestor =
1448 inclusiveTableCellAncestor;
1449 // Continue selection into next cell
1450 const nsresult result = HandleTableSelection(
1451 parentAndOffset.mParent, parentAndOffset.mOffset,
1452 TableSelectionMode::Cell, &event);
1453 if (NS_WARN_IF(NS_FAILED(result))NS_warn_if_impl(((bool)(__builtin_expect(!!(NS_FAILED_impl(result
)), 0))), "NS_FAILED(result)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1453)
) {
1454 return result;
1455 }
1456 }
1457 } else {
1458 RefPtr<Selection> selection = mDomSelections[index];
1459 // XXXX Problem: Shift+click in browser is appending text selection to
1460 // selected table!!!
1461 // is this the place to erase selected cells ?????
1462 uint32_t offset =
1463 (selection->GetDirection() == eDirNext &&
1464 aContentEndOffset > aContentOffset) // didn't go far enough
1465 ? aContentEndOffset // this will only redraw the diff
1466 : aContentOffset;
1467 selection->Extend(&aNewFocus, offset);
1468 }
1469 break;
1470 }
1471 }
1472
1473 // Be aware, the Selection instance may be destroyed after this call.
1474 return NotifySelectionListeners(SelectionType::eNormal);
1475}
1476
1477UniquePtr<SelectionDetails> nsFrameSelection::LookUpSelection(
1478 nsIContent* aContent, int32_t aContentOffset, int32_t aContentLength,
1479 bool aSlowCheck) const {
1480 if (!aContent || !mPresShell) {
1481 return nullptr;
1482 }
1483
1484 // TODO: Layout should use `uint32_t` for handling offset in DOM nodes
1485 // (for example: bug 1735262)
1486 MOZ_ASSERT(aContentOffset >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContentOffset >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContentOffset >= 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aContentOffset >= 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1486); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aContentOffset >= 0"
")"); do { *((volatile int*)__null) = 1486; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1487 MOZ_ASSERT(aContentLength >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContentLength >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContentLength >= 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aContentLength >= 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1487); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aContentLength >= 0"
")"); do { *((volatile int*)__null) = 1487; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1488 if (MOZ_UNLIKELY(aContentOffset < 0)(__builtin_expect(!!(aContentOffset < 0), 0)) || MOZ_UNLIKELY(aContentLength < 0)(__builtin_expect(!!(aContentLength < 0), 0))) {
1489 return nullptr;
1490 }
1491
1492 UniquePtr<SelectionDetails> details;
1493
1494 for (size_t j = 0; j < ArrayLength(mDomSelections); j++) {
1495 if (mDomSelections[j]) {
1496 details = mDomSelections[j]->LookUpSelection(
1497 aContent, static_cast<uint32_t>(aContentOffset),
1498 static_cast<uint32_t>(aContentLength), std::move(details),
1499 kPresentSelectionTypes[j], aSlowCheck);
1500 }
1501 }
1502
1503 // This may seem counter intuitive at first. Highlight selections need to be
1504 // iterated from back to front:
1505 //
1506 // - `mHighlightSelections` is ordered by insertion, i.e. if two or more
1507 // highlights overlap, the latest must take precedence.
1508 // - however, the `LookupSelection()` algorithm reverses the order by setting
1509 // the current `details` as `mNext`.
1510 for (const auto& iter : Reversed(mHighlightSelections)) {
1511 details = iter.second()->LookUpSelection(
1512 aContent, static_cast<uint32_t>(aContentOffset),
1513 static_cast<uint32_t>(aContentLength), std::move(details),
1514 SelectionType::eHighlight, aSlowCheck);
1515 }
1516
1517 return details;
1518}
1519
1520void nsFrameSelection::SetDragState(bool aState) {
1521 if (mDragState == aState) return;
1522
1523 mDragState = aState;
1524
1525 if (!mDragState) {
1526 mTableSelection.mDragSelectingCells = false;
1527 // Notify that reason is mouse up.
1528 SetChangeReasons(nsISelectionListener::MOUSEUP_REASON);
1529
1530 // flag is set to NotApplicable in `Selection::NotifySelectionListeners`.
1531 // since this function call is part of click event, this would immediately
1532 // reset the flag, rendering it useless.
1533 AutoRestore<ClickSelectionType> restoreClickSelectionType(
1534 mClickSelectionType);
1535 // Be aware, the Selection instance may be destroyed after this call.
1536 NotifySelectionListeners(SelectionType::eNormal);
1537 }
1538}
1539
1540Selection* nsFrameSelection::GetSelection(SelectionType aSelectionType) const {
1541 int8_t index = GetIndexFromSelectionType(aSelectionType);
1542 if (index < 0) return nullptr;
1543
1544 return mDomSelections[index];
1545}
1546
1547void nsFrameSelection::AddHighlightSelection(
1548 nsAtom* aHighlightName, mozilla::dom::Highlight& aHighlight) {
1549 RefPtr<Selection> selection =
1550 aHighlight.CreateHighlightSelection(aHighlightName, this);
1551 if (auto iter =
1552 std::find_if(mHighlightSelections.begin(), mHighlightSelections.end(),
1553 [&aHighlightName](auto const& aElm) {
1554 return aElm.first() == aHighlightName;
1555 });
1556 iter != mHighlightSelections.end()) {
1557 iter->second() = std::move(selection);
1558 } else {
1559 mHighlightSelections.AppendElement(
1560 CompactPair<RefPtr<nsAtom>, RefPtr<Selection>>(aHighlightName,
1561 std::move(selection)));
1562 }
1563}
1564
1565void nsFrameSelection::RemoveHighlightSelection(nsAtom* aHighlightName) {
1566 if (auto iter =
1567 std::find_if(mHighlightSelections.begin(), mHighlightSelections.end(),
1568 [&aHighlightName](auto const& aElm) {
1569 return aElm.first() == aHighlightName;
1570 });
1571 iter != mHighlightSelections.end()) {
1572 RefPtr<Selection> selection = iter->second();
1573 selection->RemoveAllRanges(IgnoreErrors());
1574 mHighlightSelections.RemoveElementAt(iter);
1575 }
1576}
1577
1578void nsFrameSelection::AddHighlightSelectionRange(
1579 nsAtom* aHighlightName, mozilla::dom::Highlight& aHighlight,
1580 mozilla::dom::AbstractRange& aRange) {
1581 if (auto iter =
1582 std::find_if(mHighlightSelections.begin(), mHighlightSelections.end(),
1583 [&aHighlightName](auto const& aElm) {
1584 return aElm.first() == aHighlightName;
1585 });
1586 iter != mHighlightSelections.end()) {
1587 RefPtr<Selection> selection = iter->second();
1588 selection->AddHighlightRangeAndSelectFramesAndNotifyListeners(aRange);
1589 } else {
1590 // if the selection does not exist yet, add all of its ranges and exit.
1591 RefPtr<Selection> selection =
1592 aHighlight.CreateHighlightSelection(aHighlightName, this);
1593 mHighlightSelections.AppendElement(
1594 CompactPair<RefPtr<nsAtom>, RefPtr<Selection>>(aHighlightName,
1595 std::move(selection)));
1596 }
1597}
1598
1599void nsFrameSelection::RemoveHighlightSelectionRange(
1600 nsAtom* aHighlightName, mozilla::dom::AbstractRange& aRange) {
1601 if (auto iter =
1602 std::find_if(mHighlightSelections.begin(), mHighlightSelections.end(),
1603 [&aHighlightName](auto const& aElm) {
1604 return aElm.first() == aHighlightName;
1605 });
1606 iter != mHighlightSelections.end()) {
1607 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
1608 RefPtr<Selection> selection = iter->second();
1609 selection->RemoveRangeAndUnselectFramesAndNotifyListeners(aRange,
1610 IgnoreErrors());
1611 }
1612}
1613
1614nsresult nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,
1615 SelectionRegion aRegion,
1616 int16_t aFlags) const {
1617 int8_t index = GetIndexFromSelectionType(aSelectionType);
1618 if (index < 0) return NS_ERROR_INVALID_ARG;
1619
1620 if (!mDomSelections[index]) return NS_ERROR_NULL_POINTER;
1621
1622 const auto vScroll = [&]() -> WhereToScroll {
1623 if (aFlags & nsISelectionController::SCROLL_VERTICAL_START) {
1624 return WhereToScroll::Start;
1625 }
1626 if (aFlags & nsISelectionController::SCROLL_VERTICAL_END) {
1627 return WhereToScroll::End;
1628 }
1629 if (aFlags & nsISelectionController::SCROLL_VERTICAL_CENTER) {
1630 return WhereToScroll::Center;
1631 }
1632 return WhereToScroll::Nearest;
1633 }();
1634 int32_t flags = Selection::SCROLL_DO_FLUSH;
1635 if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
1636 flags |= Selection::SCROLL_SYNCHRONOUS;
1637 } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
1638 flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY;
1639 }
1640 if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) {
1641 flags |= Selection::SCROLL_OVERFLOW_HIDDEN;
1642 }
1643 if (aFlags & nsISelectionController::SCROLL_FOR_CARET_MOVE) {
1644 flags |= Selection::SCROLL_FOR_CARET_MOVE;
1645 }
1646
1647 // After ScrollSelectionIntoView(), the pending notifications might be
1648 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
1649 RefPtr<Selection> sel = mDomSelections[index];
1650 return sel->ScrollIntoView(aRegion, ScrollAxis(vScroll), ScrollAxis(), flags);
1651}
1652
1653nsresult nsFrameSelection::RepaintSelection(SelectionType aSelectionType) {
1654 int8_t index = GetIndexFromSelectionType(aSelectionType);
1655 if (index < 0) return NS_ERROR_INVALID_ARG;
1656 if (!mDomSelections[index]) return NS_ERROR_NULL_POINTER;
1657 NS_ENSURE_STATE(mPresShell)do { if ((__builtin_expect(!!(!(mPresShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mPresShell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1657); return NS_ERROR_UNEXPECTED; } } while (false)
;
1658
1659// On macOS, update the selection cache to the new active selection
1660// aka the current selection.
1661#ifdef XP_MACOSX
1662 // Check that we're in the an active window and, if this is Web content,
1663 // in the frontmost tab.
1664 Document* doc = mPresShell->GetDocument();
1665 if (doc && IsInActiveTab(doc) && aSelectionType == SelectionType::eNormal) {
1666 UpdateSelectionCacheOnRepaintSelection(mDomSelections[index]);
1667 }
1668#endif
1669 return mDomSelections[index]->Repaint(mPresShell->GetPresContext());
1670}
1671
1672nsIFrame* nsFrameSelection::GetFrameToPageSelect() const {
1673 if (NS_WARN_IF(!mPresShell)NS_warn_if_impl(!mPresShell, "!mPresShell", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1673)
) {
1674 return nullptr;
1675 }
1676
1677 nsIFrame* rootFrameToSelect;
1678 if (mLimiters.mLimiter) {
1679 rootFrameToSelect = mLimiters.mLimiter->GetPrimaryFrame();
1680 if (NS_WARN_IF(!rootFrameToSelect)NS_warn_if_impl(!rootFrameToSelect, "!rootFrameToSelect", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1680)
) {
1681 return nullptr;
1682 }
1683 } else if (mLimiters.mAncestorLimiter) {
1684 rootFrameToSelect = mLimiters.mAncestorLimiter->GetPrimaryFrame();
1685 if (NS_WARN_IF(!rootFrameToSelect)NS_warn_if_impl(!rootFrameToSelect, "!rootFrameToSelect", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1685)
) {
1686 return nullptr;
1687 }
1688 } else {
1689 rootFrameToSelect = mPresShell->GetRootScrollContainerFrame();
1690 if (NS_WARN_IF(!rootFrameToSelect)NS_warn_if_impl(!rootFrameToSelect, "!rootFrameToSelect", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1690)
) {
1691 return nullptr;
1692 }
1693 }
1694
1695 nsCOMPtr<nsIContent> contentToSelect = mPresShell->GetContentForScrolling();
1696 if (contentToSelect) {
1697 // If there is selected content, look for nearest and vertical scrollable
1698 // parent under the root frame.
1699 for (nsIFrame* frame = contentToSelect->GetPrimaryFrame();
1700 frame && frame != rootFrameToSelect; frame = frame->GetParent()) {
1701 ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(frame);
1702 if (!scrollContainerFrame) {
1703 continue;
1704 }
1705 ScrollStyles scrollStyles = scrollContainerFrame->GetScrollStyles();
1706 if (scrollStyles.mVertical == StyleOverflow::Hidden) {
1707 continue;
1708 }
1709 layers::ScrollDirections directions =
1710 scrollContainerFrame->GetAvailableScrollingDirections();
1711 if (directions.contains(layers::ScrollDirection::eVertical)) {
1712 // If there is sub scrollable frame, let's use its page size to select.
1713 return frame;
1714 }
1715 }
1716 }
1717 // Otherwise, i.e., there is no scrollable frame or only the root frame is
1718 // scrollable, let's return the root frame because Shift + PageUp/PageDown
1719 // should expand the selection in the root content even if it's not
1720 // scrollable.
1721 return rootFrameToSelect;
1722}
1723
1724nsresult nsFrameSelection::PageMove(bool aForward, bool aExtend,
1725 nsIFrame* aFrame,
1726 SelectionIntoView aSelectionIntoView) {
1727 MOZ_ASSERT(aFrame)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFrame)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aFrame))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aFrame", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1727); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFrame" ")"
); do { *((volatile int*)__null) = 1727; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1728
1729 // expected behavior for PageMove is to scroll AND move the caret
1730 // and remain relative position of the caret in view. see Bug 4302.
1731
1732 // Get the scroll container frame. If aFrame is not scrollable, this is
1733 // nullptr.
1734 ScrollContainerFrame* scrollContainerFrame = aFrame->GetScrollTargetFrame();
1735 // Get the scrolled frame. If aFrame is not scrollable, this is aFrame
1736 // itself.
1737 nsIFrame* scrolledFrame =
1738 scrollContainerFrame ? scrollContainerFrame->GetScrolledFrame() : aFrame;
1739 if (!scrolledFrame) {
1740 return NS_OK;
1741 }
1742
1743 // find out where the caret is.
1744 // we should know mDesiredCaretPos.mValue value of nsFrameSelection, but I
1745 // havent seen that behavior in other windows applications yet.
1746 RefPtr<Selection> selection = GetSelection(SelectionType::eNormal);
1747 if (!selection) {
1748 return NS_OK;
1749 }
1750
1751 nsRect caretPos;
1752 nsIFrame* caretFrame = nsCaret::GetGeometry(selection, &caretPos);
1753 if (!caretFrame) {
1754 return NS_OK;
1755 }
1756
1757 // If the scrolled frame is outside of current selection limiter,
1758 // we need to scroll the frame but keep moving selection in the limiter.
1759 nsIFrame* frameToClick = scrolledFrame;
1760 if (!IsValidSelectionPoint(scrolledFrame->GetContent())) {
1761 frameToClick = GetFrameToPageSelect();
1762 if (NS_WARN_IF(!frameToClick)NS_warn_if_impl(!frameToClick, "!frameToClick", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1762)
) {
1763 return NS_OK;
1764 }
1765 }
1766
1767 if (scrollContainerFrame) {
1768 // If there is a scrollable frame, adjust pseudo-click position with page
1769 // scroll amount.
1770 // XXX This may scroll more than one page if ScrollSelectionIntoView is
1771 // called later because caret may not fully visible. E.g., if
1772 // clicking line will be visible only half height with scrolling
1773 // the frame, ScrollSelectionIntoView additionally scrolls to show
1774 // the caret entirely.
1775 if (aForward) {
1776 caretPos.y += scrollContainerFrame->GetPageScrollAmount().height;
1777 } else {
1778 caretPos.y -= scrollContainerFrame->GetPageScrollAmount().height;
1779 }
1780 } else {
1781 // Otherwise, adjust pseudo-click position with the frame size.
1782 if (aForward) {
1783 caretPos.y += frameToClick->GetSize().height;
1784 } else {
1785 caretPos.y -= frameToClick->GetSize().height;
1786 }
1787 }
1788
1789 caretPos += caretFrame->GetOffsetTo(frameToClick);
1790
1791 // get a content at desired location
1792 nsPoint desiredPoint;
1793 desiredPoint.x = caretPos.x;
1794 desiredPoint.y = caretPos.y + caretPos.height / 2;
1795 nsIFrame::ContentOffsets offsets =
1796 frameToClick->GetContentOffsetsFromPoint(desiredPoint);
1797
1798 if (!offsets.content) {
1799 // XXX Do we need to handle ScrollSelectionIntoView in this case?
1800 return NS_OK;
1801 }
1802
1803 // First, place the caret.
1804 bool selectionChanged;
1805 {
1806 // We don't want any script to run until we check whether selection is
1807 // modified by HandleClick.
1808 SelectionBatcher ensureNoSelectionChangeNotifications(selection,
1809 __FUNCTION__);
1810
1811 RangeBoundary oldAnchor = selection->AnchorRef();
1812 RangeBoundary oldFocus = selection->FocusRef();
1813
1814 const FocusMode focusMode =
1815 aExtend ? FocusMode::kExtendSelection : FocusMode::kCollapseToNewPoint;
1816 HandleClick(MOZ_KnownLive(offsets.content)(offsets.content) /* bug 1636889 */,
1817 offsets.offset, offsets.offset, focusMode,
1818 CaretAssociationHint::After);
1819
1820 selectionChanged = selection->AnchorRef() != oldAnchor ||
1821 selection->FocusRef() != oldFocus;
1822 }
1823
1824 bool doScrollSelectionIntoView = !(
1825 aSelectionIntoView == SelectionIntoView::IfChanged && !selectionChanged);
1826
1827 // Then, scroll the given frame one page.
1828 if (scrollContainerFrame) {
1829 // If we'll call ScrollSelectionIntoView later and selection wasn't
1830 // changed and we scroll outside of selection limiter, we shouldn't use
1831 // smooth scroll here because ScrollContainerFrame uses normal runnable,
1832 // but ScrollSelectionIntoView uses early runner and it cancels the
1833 // pending smooth scroll. Therefore, if we used smooth scroll in such
1834 // case, ScrollSelectionIntoView would scroll to show caret instead of
1835 // page scroll of an element outside selection limiter.
1836 ScrollMode scrollMode = doScrollSelectionIntoView && !selectionChanged &&
1837 scrolledFrame != frameToClick
1838 ? ScrollMode::Instant
1839 : ScrollMode::Smooth;
1840 scrollContainerFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
1841 ScrollUnit::PAGES, scrollMode);
1842 }
1843
1844 // Finally, scroll selection into view if requested.
1845 if (!doScrollSelectionIntoView) {
1846 return NS_OK;
1847 }
1848 return ScrollSelectionIntoView(
1849 SelectionType::eNormal, nsISelectionController::SELECTION_FOCUS_REGION,
1850 nsISelectionController::SCROLL_SYNCHRONOUS |
1851 nsISelectionController::SCROLL_FOR_CARET_MOVE);
1852}
1853
1854nsresult nsFrameSelection::PhysicalMove(int16_t aDirection, int16_t aAmount,
1855 bool aExtend) {
1856 NS_ENSURE_STATE(mPresShell)do { if ((__builtin_expect(!!(!(mPresShell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mPresShell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1856); return NS_ERROR_UNEXPECTED; } } while (false)
;
1857 // Flush out layout, since we need it to be up to date to do caret
1858 // positioning.
1859 OwningNonNull<PresShell> presShell(*mPresShell);
1860 presShell->FlushPendingNotifications(FlushType::Layout);
1861
1862 if (!mPresShell) {
1863 return NS_OK;
1864 }
1865
1866 // Check that parameters are safe
1867 if (aDirection < 0 || aDirection > 3 || aAmount < 0 || aAmount > 1) {
1868 return NS_ERROR_FAILURE;
1869 }
1870
1871 nsPresContext* context = mPresShell->GetPresContext();
1872 if (!context) {
1873 return NS_ERROR_FAILURE;
1874 }
1875
1876 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
1877 RefPtr<Selection> sel = mDomSelections[index];
1878 if (!sel) {
1879 return NS_ERROR_NULL_POINTER;
1880 }
1881
1882 // Map the abstract movement amounts (0-1) to direction-specific
1883 // selection units.
1884 static const nsSelectionAmount inlineAmount[] = {eSelectCluster, eSelectWord};
1885 static const nsSelectionAmount blockPrevAmount[] = {eSelectLine,
1886 eSelectBeginLine};
1887 static const nsSelectionAmount blockNextAmount[] = {eSelectLine,
1888 eSelectEndLine};
1889
1890 struct PhysicalToLogicalMapping {
1891 nsDirection direction;
1892 const nsSelectionAmount* amounts;
1893 };
1894 static const PhysicalToLogicalMapping verticalLR[4] = {
1895 {eDirPrevious, blockPrevAmount}, // left
1896 {eDirNext, blockNextAmount}, // right
1897 {eDirPrevious, inlineAmount}, // up
1898 {eDirNext, inlineAmount} // down
1899 };
1900 static const PhysicalToLogicalMapping verticalRL[4] = {
1901 {eDirNext, blockNextAmount},
1902 {eDirPrevious, blockPrevAmount},
1903 {eDirPrevious, inlineAmount},
1904 {eDirNext, inlineAmount}};
1905 static const PhysicalToLogicalMapping horizontal[4] = {
1906 {eDirPrevious, inlineAmount},
1907 {eDirNext, inlineAmount},
1908 {eDirPrevious, blockPrevAmount},
1909 {eDirNext, blockNextAmount}};
1910
1911 WritingMode wm;
1912 const PrimaryFrameData frameForFocus =
1913 sel->GetPrimaryFrameForCaretAtFocusNode(true);
1914 if (frameForFocus.mFrame) {
1915 // FYI: Setting the caret association hint was done during a call of
1916 // GetPrimaryFrameForCaretAtFocusNode. Therefore, this may not be intended
1917 // by the original author.
1918 sel->GetFrameSelection()->SetHint(frameForFocus.mHint);
1919
1920 if (!frameForFocus.mFrame->Style()->IsTextCombined()) {
1921 wm = frameForFocus.mFrame->GetWritingMode();
1922 } else {
1923 // Using different direction for horizontal-in-vertical would
1924 // make it hard to navigate via keyboard. Inherit the moving
1925 // direction from its parent.
1926 MOZ_ASSERT(frameForFocus.mFrame->IsTextFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameForFocus.mFrame->IsTextFrame())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(frameForFocus.mFrame->IsTextFrame()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("frameForFocus.mFrame->IsTextFrame()"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameForFocus.mFrame->IsTextFrame()"
")"); do { *((volatile int*)__null) = 1926; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1927 wm = frameForFocus.mFrame->GetParent()->GetWritingMode();
1928 MOZ_ASSERT(wm.IsVertical(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wm.IsVertical())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wm.IsVertical()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wm.IsVertical()"
" (" "Text combined " "can only appear in vertical text" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wm.IsVertical()"
") (" "Text combined " "can only appear in vertical text" ")"
); do { *((volatile int*)__null) = 1930; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1929 "Text combined "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wm.IsVertical())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wm.IsVertical()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wm.IsVertical()"
" (" "Text combined " "can only appear in vertical text" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wm.IsVertical()"
") (" "Text combined " "can only appear in vertical text" ")"
); do { *((volatile int*)__null) = 1930; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1930 "can only appear in vertical text")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wm.IsVertical())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wm.IsVertical()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wm.IsVertical()"
" (" "Text combined " "can only appear in vertical text" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wm.IsVertical()"
") (" "Text combined " "can only appear in vertical text" ")"
); do { *((volatile int*)__null) = 1930; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1931 }
1932 }
1933
1934 const PhysicalToLogicalMapping& mapping =
1935 wm.IsVertical()
1936 ? wm.IsVerticalLR() ? verticalLR[aDirection] : verticalRL[aDirection]
1937 : horizontal[aDirection];
1938
1939 nsresult rv =
1940 MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount], eVisual);
1941 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
1942 // If we tried to do a line move, but couldn't move in the given direction,
1943 // then we'll "promote" this to a line-edge move instead.
1944 if (mapping.amounts[aAmount] == eSelectLine) {
1945 rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount + 1],
1946 eVisual);
1947 }
1948 // And if it was a next-word move that failed (which can happen when
1949 // eat_space_to_next_word is true, see bug 1153237), then just move forward
1950 // to the line-edge.
1951 else if (mapping.amounts[aAmount] == eSelectWord &&
1952 mapping.direction == eDirNext) {
1953 rv = MoveCaret(eDirNext, aExtend, eSelectEndLine, eVisual);
1954 }
1955 }
1956
1957 return rv;
1958}
1959
1960nsresult nsFrameSelection::CharacterMove(bool aForward, bool aExtend) {
1961 return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectCluster,
1962 eUsePrefStyle);
1963}
1964
1965nsresult nsFrameSelection::WordMove(bool aForward, bool aExtend) {
1966 return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectWord,
1967 eUsePrefStyle);
1968}
1969
1970nsresult nsFrameSelection::LineMove(bool aForward, bool aExtend) {
1971 return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectLine,
1972 eUsePrefStyle);
1973}
1974
1975nsresult nsFrameSelection::IntraLineMove(bool aForward, bool aExtend) {
1976 if (aForward) {
1977 return MoveCaret(eDirNext, aExtend, eSelectEndLine, eLogical);
1978 } else {
1979 return MoveCaret(eDirPrevious, aExtend, eSelectBeginLine, eLogical);
1980 }
1981}
1982
1983template <typename RangeType>
1984Result<RefPtr<RangeType>, nsresult>
1985nsFrameSelection::CreateRangeExtendedToSomewhere(
1986 nsDirection aDirection, const nsSelectionAmount aAmount,
1987 CaretMovementStyle aMovementStyle) {
1988 MOZ_ASSERT(aDirection == eDirNext || aDirection == eDirPrevious)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDirection == eDirNext || aDirection == eDirPrevious
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aDirection == eDirNext || aDirection == eDirPrevious
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aDirection == eDirNext || aDirection == eDirPrevious", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1988); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDirection == eDirNext || aDirection == eDirPrevious"
")"); do { *((volatile int*)__null) = 1988; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1989 MOZ_ASSERT(aAmount == eSelectCharacter || aAmount == eSelectCluster ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAmount == eSelectCharacter || aAmount == eSelectCluster
|| aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount
== eSelectEndLine)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAmount == eSelectCharacter ||
aAmount == eSelectCluster || aAmount == eSelectWord || aAmount
== eSelectBeginLine || aAmount == eSelectEndLine))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aAmount == eSelectCharacter || aAmount == eSelectCluster || aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount == eSelectEndLine"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1991); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAmount == eSelectCharacter || aAmount == eSelectCluster || aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount == eSelectEndLine"
")"); do { *((volatile int*)__null) = 1991; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1990 aAmount == eSelectWord || aAmount == eSelectBeginLine ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAmount == eSelectCharacter || aAmount == eSelectCluster
|| aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount
== eSelectEndLine)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAmount == eSelectCharacter ||
aAmount == eSelectCluster || aAmount == eSelectWord || aAmount
== eSelectBeginLine || aAmount == eSelectEndLine))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aAmount == eSelectCharacter || aAmount == eSelectCluster || aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount == eSelectEndLine"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1991); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAmount == eSelectCharacter || aAmount == eSelectCluster || aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount == eSelectEndLine"
")"); do { *((volatile int*)__null) = 1991; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1991 aAmount == eSelectEndLine)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAmount == eSelectCharacter || aAmount == eSelectCluster
|| aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount
== eSelectEndLine)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAmount == eSelectCharacter ||
aAmount == eSelectCluster || aAmount == eSelectWord || aAmount
== eSelectBeginLine || aAmount == eSelectEndLine))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aAmount == eSelectCharacter || aAmount == eSelectCluster || aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount == eSelectEndLine"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1991); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAmount == eSelectCharacter || aAmount == eSelectCluster || aAmount == eSelectWord || aAmount == eSelectBeginLine || aAmount == eSelectEndLine"
")"); do { *((volatile int*)__null) = 1991; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1992 MOZ_ASSERT(aMovementStyle == eLogical || aMovementStyle == eVisual ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMovementStyle == eLogical || aMovementStyle == eVisual
|| aMovementStyle == eUsePrefStyle)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aMovementStyle == eLogical ||
aMovementStyle == eVisual || aMovementStyle == eUsePrefStyle
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aMovementStyle == eLogical || aMovementStyle == eVisual || aMovementStyle == eUsePrefStyle"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aMovementStyle == eLogical || aMovementStyle == eVisual || aMovementStyle == eUsePrefStyle"
")"); do { *((volatile int*)__null) = 1993; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1993 aMovementStyle == eUsePrefStyle)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMovementStyle == eLogical || aMovementStyle == eVisual
|| aMovementStyle == eUsePrefStyle)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aMovementStyle == eLogical ||
aMovementStyle == eVisual || aMovementStyle == eUsePrefStyle
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aMovementStyle == eLogical || aMovementStyle == eVisual || aMovementStyle == eUsePrefStyle"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 1993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aMovementStyle == eLogical || aMovementStyle == eVisual || aMovementStyle == eUsePrefStyle"
")"); do { *((volatile int*)__null) = 1993; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1994
1995 if (!mPresShell) {
1996 return Err(NS_ERROR_UNEXPECTED);
1997 }
1998 OwningNonNull<PresShell> presShell(*mPresShell);
1999 presShell->FlushPendingNotifications(FlushType::Layout);
2000 if (!mPresShell) {
2001 return Err(NS_ERROR_FAILURE);
2002 }
2003 Selection* selection =
2004 mDomSelections[GetIndexFromSelectionType(SelectionType::eNormal)];
2005 if (!selection || selection->RangeCount() != 1) {
2006 return Err(NS_ERROR_FAILURE);
2007 }
2008 RefPtr<const nsRange> firstRange = selection->GetRangeAt(0);
2009 if (!firstRange || !firstRange->IsPositioned()) {
2010 return Err(NS_ERROR_FAILURE);
2011 }
2012 Result<PeekOffsetStruct, nsresult> result = PeekOffsetForCaretMove(
2013 aDirection, true, aAmount, aMovementStyle, nsPoint(0, 0));
2014 if (result.isErr()) {
2015 return Err(NS_ERROR_FAILURE);
2016 }
2017 const PeekOffsetStruct& pos = result.inspect();
2018 RefPtr<RangeType> range;
2019 if (NS_WARN_IF(!pos.mResultContent)NS_warn_if_impl(!pos.mResultContent, "!pos.mResultContent", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2019)
) {
2020 return range;
2021 }
2022 if (aDirection == eDirPrevious) {
2023 range = RangeType::Create(
2024 RawRangeBoundary(pos.mResultContent, pos.mContentOffset),
2025 firstRange->EndRef(), IgnoreErrors());
2026 } else {
2027 range = RangeType::Create(
2028 firstRange->StartRef(),
2029 RawRangeBoundary(pos.mResultContent, pos.mContentOffset),
2030 IgnoreErrors());
2031 }
2032 return range;
2033}
2034
2035//////////END FRAMESELECTION
2036
2037LazyLogModule gBatchLog("SelectionBatch");
2038
2039void nsFrameSelection::StartBatchChanges(const char* aRequesterFuncName) {
2040 MOZ_LOG(gBatchLog, LogLevel::Info,do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::StartBatchChanges(%s)"
, this, std::string((mBatching.mCounter + 1) * 2, ' ').c_str(
), aRequesterFuncName); } } while (0)
2041 ("%p%snsFrameSelection::StartBatchChanges(%s)", this,do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::StartBatchChanges(%s)"
, this, std::string((mBatching.mCounter + 1) * 2, ' ').c_str(
), aRequesterFuncName); } } while (0)
2042 std::string((mBatching.mCounter + 1) * 2, ' ').c_str(),do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::StartBatchChanges(%s)"
, this, std::string((mBatching.mCounter + 1) * 2, ' ').c_str(
), aRequesterFuncName); } } while (0)
2043 aRequesterFuncName))do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::StartBatchChanges(%s)"
, this, std::string((mBatching.mCounter + 1) * 2, ' ').c_str(
), aRequesterFuncName); } } while (0)
;
2044 mBatching.mCounter++;
2045}
2046
2047void nsFrameSelection::EndBatchChanges(const char* aRequesterFuncName,
2048 int16_t aReasons) {
2049 MOZ_LOG(gBatchLog, LogLevel::Info,do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::EndBatchChanges (%s, %s)"
, this, std::string(mBatching.mCounter * 2, ' ').c_str(), aRequesterFuncName
, SelectionChangeReasonsToCString(aReasons).get()); } } while
(0)
2050 ("%p%snsFrameSelection::EndBatchChanges (%s, %s)", this,do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::EndBatchChanges (%s, %s)"
, this, std::string(mBatching.mCounter * 2, ' ').c_str(), aRequesterFuncName
, SelectionChangeReasonsToCString(aReasons).get()); } } while
(0)
2051 std::string(mBatching.mCounter * 2, ' ').c_str(), aRequesterFuncName,do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::EndBatchChanges (%s, %s)"
, this, std::string(mBatching.mCounter * 2, ' ').c_str(), aRequesterFuncName
, SelectionChangeReasonsToCString(aReasons).get()); } } while
(0)
2052 SelectionChangeReasonsToCString(aReasons).get()))do { const ::mozilla::LogModule* moz_real_module = gBatchLog;
if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Info)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Info, "%p%snsFrameSelection::EndBatchChanges (%s, %s)"
, this, std::string(mBatching.mCounter * 2, ' ').c_str(), aRequesterFuncName
, SelectionChangeReasonsToCString(aReasons).get()); } } while
(0)
;
2053 MOZ_ASSERT(mBatching.mCounter > 0, "Bad mBatching.mCounter")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mBatching.mCounter > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mBatching.mCounter > 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("mBatching.mCounter > 0"
" (" "Bad mBatching.mCounter" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mBatching.mCounter > 0"
") (" "Bad mBatching.mCounter" ")"); do { *((volatile int*)__null
) = 2053; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
2054 mBatching.mCounter--;
2055
2056 if (mBatching.mCounter == 0) {
2057 AddChangeReasons(aReasons);
2058 mCaretMoveAmount = eSelectNoAmount;
2059 // Be aware, the Selection instance may be destroyed after this call,
2060 // hence make sure that this instance remains until the end of this call.
2061 RefPtr frameSelection = this;
2062 for (auto selectionType : kPresentSelectionTypes) {
2063 // This returns NS_ERROR_FAILURE if being called for a selection that is
2064 // not present. We don't care about that here, so we silently ignore it
2065 // and continue.
2066 Unused << NotifySelectionListeners(selectionType, IsBatchingEnd::Yes);
2067 }
2068 }
2069}
2070
2071nsresult nsFrameSelection::NotifySelectionListeners(
2072 SelectionType aSelectionType, IsBatchingEnd aEndBatching) {
2073 int8_t index = GetIndexFromSelectionType(aSelectionType);
2074 if (index >= 0 && mDomSelections[index]) {
2075 RefPtr<Selection> selection = mDomSelections[index];
2076 if (aEndBatching == IsBatchingEnd::Yes &&
2077 !selection->ChangesDuringBatching()) {
2078 return NS_OK;
2079 }
2080 selection->NotifySelectionListeners();
2081 mCaretMoveAmount = eSelectNoAmount;
2082 return NS_OK;
2083 }
2084 return NS_ERROR_FAILURE;
2085}
2086
2087// Start of Table Selection methods
2088
2089static bool IsCell(nsIContent* aContent) {
2090 return aContent->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
2091}
2092
2093// static
2094nsITableCellLayout* nsFrameSelection::GetCellLayout(
2095 const nsIContent* aCellContent) {
2096 nsITableCellLayout* cellLayoutObject =
2097 do_QueryFrame(aCellContent->GetPrimaryFrame());
2098 return cellLayoutObject;
2099}
2100
2101nsresult nsFrameSelection::ClearNormalSelection() {
2102 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
2103 RefPtr<Selection> selection = mDomSelections[index];
2104 if (!selection) {
2105 return NS_ERROR_NULL_POINTER;
2106 }
2107
2108 ErrorResult err;
2109 selection->RemoveAllRanges(err);
2110 return err.StealNSResult();
2111}
2112
2113static nsIContent* GetFirstSelectedContent(const nsRange* aRange) {
2114 if (!aRange) {
2115 return nullptr;
2116 }
2117
2118 MOZ_ASSERT(aRange->GetStartContainer(), "Must have start parent!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRange->GetStartContainer())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aRange->GetStartContainer
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aRange->GetStartContainer()" " (" "Must have start parent!"
")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2118); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aRange->GetStartContainer()"
") (" "Must have start parent!" ")"); do { *((volatile int*)
__null) = 2118; __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2119 MOZ_ASSERT(aRange->GetStartContainer()->IsElement(), "Unexpected parent")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRange->GetStartContainer()->IsElement())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aRange->GetStartContainer()->IsElement()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aRange->GetStartContainer()->IsElement()"
" (" "Unexpected parent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aRange->GetStartContainer()->IsElement()"
") (" "Unexpected parent" ")"); do { *((volatile int*)__null
) = 2119; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
2120
2121 return aRange->GetChildAtStartOffset();
2122}
2123
2124// Table selection support.
2125// TODO: Separate table methods into a separate nsITableSelection interface
2126nsresult nsFrameSelection::HandleTableSelection(nsINode* aParentContent,
2127 int32_t aContentOffset,
2128 TableSelectionMode aTarget,
2129 WidgetMouseEvent* aMouseEvent) {
2130 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
2131 RefPtr<Selection> selection = mDomSelections[index];
2132 if (!selection) {
2133 return NS_ERROR_NULL_POINTER;
2134 }
2135
2136 return mTableSelection.HandleSelection(aParentContent, aContentOffset,
2137 aTarget, aMouseEvent, mDragState,
2138 *selection);
2139}
2140
2141nsresult nsFrameSelection::TableSelection::HandleSelection(
2142 nsINode* aParentContent, int32_t aContentOffset, TableSelectionMode aTarget,
2143 WidgetMouseEvent* aMouseEvent, bool aDragState,
2144 Selection& aNormalSelection) {
2145 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2145); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2145; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2146
2147 NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER)do { if ((__builtin_expect(!!(!(aParentContent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aParentContent" ") failed"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2147); return NS_ERROR_NULL_POINTER; } } while (false)
;
2148 NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER)do { if ((__builtin_expect(!!(!(aMouseEvent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aMouseEvent" ") failed"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2148); return NS_ERROR_NULL_POINTER; } } while (false)
;
2149
2150 if (aDragState && mDragSelectingCells &&
2151 aTarget == TableSelectionMode::Table) {
2152 // We were selecting cells and user drags mouse in table border or inbetween
2153 // cells,
2154 // just do nothing
2155 return NS_OK;
2156 }
2157
2158 RefPtr<nsIContent> childContent =
2159 aParentContent->GetChildAt_Deprecated(aContentOffset);
2160
2161 // When doing table selection, always set the direction to next so
2162 // we can be sure that anchorNode's offset always points to the
2163 // selected cell
2164 aNormalSelection.SetDirection(eDirNext);
2165
2166 // Stack-class to wrap all table selection changes in
2167 // BeginBatchChanges() / EndBatchChanges()
2168 SelectionBatcher selectionBatcher(&aNormalSelection, __FUNCTION__);
2169
2170 if (aDragState && mDragSelectingCells) {
2171 return HandleDragSelecting(aTarget, childContent, aMouseEvent,
2172 aNormalSelection);
2173 }
2174
2175 return HandleMouseUpOrDown(aTarget, aDragState, childContent, aParentContent,
2176 aContentOffset, aMouseEvent, aNormalSelection);
2177}
2178
2179class nsFrameSelection::TableSelection::RowAndColumnRelation {
2180 public:
2181 static Result<RowAndColumnRelation, nsresult> Create(
2182 const nsIContent* aFirst, const nsIContent* aSecond) {
2183 RowAndColumnRelation result;
2184
2185 nsresult errorResult =
2186 GetCellIndexes(aFirst, result.mFirst.mRow, result.mFirst.mColumn);
2187 if (NS_FAILED(errorResult)((bool)(__builtin_expect(!!(NS_FAILED_impl(errorResult)), 0))
)
) {
2188 return Err(errorResult);
2189 }
2190
2191 errorResult =
2192 GetCellIndexes(aSecond, result.mSecond.mRow, result.mSecond.mColumn);
2193 if (NS_FAILED(errorResult)((bool)(__builtin_expect(!!(NS_FAILED_impl(errorResult)), 0))
)
) {
2194 return Err(errorResult);
2195 }
2196
2197 return result;
2198 }
2199
2200 bool IsSameColumn() const { return mFirst.mColumn == mSecond.mColumn; }
2201
2202 bool IsSameRow() const { return mFirst.mRow == mSecond.mRow; }
2203
2204 private:
2205 RowAndColumnRelation() = default;
2206
2207 struct RowAndColumn {
2208 int32_t mRow = 0;
2209 int32_t mColumn = 0;
2210 };
2211
2212 RowAndColumn mFirst;
2213 RowAndColumn mSecond;
2214};
2215
2216nsresult nsFrameSelection::TableSelection::HandleDragSelecting(
2217 TableSelectionMode aTarget, nsIContent* aChildContent,
2218 const WidgetMouseEvent* aMouseEvent, Selection& aNormalSelection) {
2219 // We are drag-selecting
2220 if (aTarget != TableSelectionMode::Table) {
2221 // If dragging in the same cell as last event, do nothing
2222 if (mEndSelectedCell == aChildContent) {
2223 return NS_OK;
2224 }
2225
2226#ifdef DEBUG_TABLE_SELECTION
2227 printf(
2228 " mStartSelectedCell = %p, "
2229 "mEndSelectedCell = %p, aChildContent = %p "
2230 "\n",
2231 mStartSelectedCell.get(), mEndSelectedCell.get(), aChildContent);
2232#endif
2233 // aTarget can be any "cell mode",
2234 // so we can easily drag-select rows and columns
2235 // Once we are in row or column mode,
2236 // we can drift into any cell to stay in that mode
2237 // even if aTarget = TableSelectionMode::Cell
2238
2239 if (mMode == TableSelectionMode::Row ||
2240 mMode == TableSelectionMode::Column) {
2241 if (mEndSelectedCell) {
2242 Result<RowAndColumnRelation, nsresult> rowAndColumnRelation =
2243 RowAndColumnRelation::Create(mEndSelectedCell, aChildContent);
2244
2245 if (rowAndColumnRelation.isErr()) {
2246 return rowAndColumnRelation.unwrapErr();
2247 }
2248
2249 if ((mMode == TableSelectionMode::Row &&
2250 rowAndColumnRelation.inspect().IsSameRow()) ||
2251 (mMode == TableSelectionMode::Column &&
2252 rowAndColumnRelation.inspect().IsSameColumn())) {
2253 return NS_OK;
2254 }
2255 }
2256#ifdef DEBUG_TABLE_SELECTION
2257 printf(" Dragged into a new column or row\n");
2258#endif
2259 // Continue dragging row or column selection
2260
2261 return SelectRowOrColumn(aChildContent, aNormalSelection);
2262 }
2263 if (mMode == TableSelectionMode::Cell) {
2264#ifdef DEBUG_TABLE_SELECTION
2265 printf("HandleTableSelection: Dragged into a new cell\n");
2266#endif
2267 // Trick for quick selection of rows and columns
2268 // Hold down shift, then start selecting in one direction
2269 // If next cell dragged into is in same row, select entire row,
2270 // if next cell is in same column, select entire column
2271 if (mStartSelectedCell && aMouseEvent->IsShift()) {
2272 Result<RowAndColumnRelation, nsresult> rowAndColumnRelation =
2273 RowAndColumnRelation::Create(mStartSelectedCell, aChildContent);
2274 if (rowAndColumnRelation.isErr()) {
2275 return rowAndColumnRelation.unwrapErr();
2276 }
2277
2278 if (rowAndColumnRelation.inspect().IsSameRow() ||
2279 rowAndColumnRelation.inspect().IsSameColumn()) {
2280 // Force new selection block
2281 mStartSelectedCell = nullptr;
2282 aNormalSelection.RemoveAllRanges(IgnoreErrors());
2283
2284 if (rowAndColumnRelation.inspect().IsSameRow()) {
2285 mMode = TableSelectionMode::Row;
2286 } else {
2287 mMode = TableSelectionMode::Column;
2288 }
2289
2290 return SelectRowOrColumn(aChildContent, aNormalSelection);
2291 }
2292 }
2293
2294 // Reselect block of cells to new end location
2295 return SelectBlockOfCells(mStartSelectedCell, aChildContent,
2296 aNormalSelection);
2297 }
2298 }
2299 // Do nothing if dragging in table, but outside a cell
2300 return NS_OK;
2301}
2302
2303nsresult nsFrameSelection::TableSelection::HandleMouseUpOrDown(
2304 TableSelectionMode aTarget, bool aDragState, nsIContent* aChildContent,
2305 nsINode* aParentContent, int32_t aContentOffset,
2306 const WidgetMouseEvent* aMouseEvent, Selection& aNormalSelection) {
2307 nsresult result = NS_OK;
2308 // Not dragging -- mouse event is down or up
2309 if (aDragState) {
2310#ifdef DEBUG_TABLE_SELECTION
2311 printf("HandleTableSelection: Mouse down event\n");
2312#endif
2313 // Clear cell we stored in mouse-down
2314 mUnselectCellOnMouseUp = nullptr;
2315
2316 if (aTarget == TableSelectionMode::Cell) {
2317 bool isSelected = false;
2318
2319 // Check if we have other selected cells
2320 nsIContent* previousCellNode =
2321 GetFirstSelectedContent(GetFirstCellRange(aNormalSelection));
2322 if (previousCellNode) {
2323 // We have at least 1 other selected cell
2324
2325 // Check if new cell is already selected
2326 nsIFrame* cellFrame = aChildContent->GetPrimaryFrame();
2327 if (!cellFrame) {
2328 return NS_ERROR_NULL_POINTER;
2329 }
2330 isSelected = cellFrame->IsSelected();
2331 } else {
2332 // No cells selected -- remove non-cell selection
2333 aNormalSelection.RemoveAllRanges(IgnoreErrors());
2334 }
2335 mDragSelectingCells = true; // Signal to start drag-cell-selection
2336 mMode = aTarget;
2337 // Set start for new drag-selection block (not appended)
2338 mStartSelectedCell = aChildContent;
2339 // The initial block end is same as the start
2340 mEndSelectedCell = aChildContent;
2341
2342 if (isSelected) {
2343 // Remember this cell to (possibly) unselect it on mouseup
2344 mUnselectCellOnMouseUp = aChildContent;
2345#ifdef DEBUG_TABLE_SELECTION
2346 printf(
2347 "HandleTableSelection: Saving "
2348 "mUnselectCellOnMouseUp\n");
2349#endif
2350 } else {
2351 // Select an unselected cell
2352 // but first remove existing selection if not in same table
2353 if (previousCellNode &&
2354 !IsInSameTable(previousCellNode, aChildContent)) {
2355 aNormalSelection.RemoveAllRanges(IgnoreErrors());
2356 // Reset selection mode that is cleared in RemoveAllRanges
2357 mMode = aTarget;
2358 }
2359
2360 return ::SelectCellElement(aChildContent, aNormalSelection);
2361 }
2362
2363 return NS_OK;
2364 }
2365 if (aTarget == TableSelectionMode::Table) {
2366 // TODO: We currently select entire table when clicked between cells,
2367 // should we restrict to only around border?
2368 // *** How do we get location data for cell and click?
2369 mDragSelectingCells = false;
2370 mStartSelectedCell = nullptr;
2371 mEndSelectedCell = nullptr;
2372
2373 // Remove existing selection and select the table
2374 aNormalSelection.RemoveAllRanges(IgnoreErrors());
2375 return CreateAndAddRange(aParentContent, aContentOffset,
2376 aNormalSelection);
2377 }
2378 if (aTarget == TableSelectionMode::Row ||
2379 aTarget == TableSelectionMode::Column) {
2380#ifdef DEBUG_TABLE_SELECTION
2381 printf("aTarget == %d\n", aTarget);
2382#endif
2383
2384 // Start drag-selecting mode so multiple rows/cols can be selected
2385 // Note: Currently, nsIFrame::GetDataForTableSelection
2386 // will never call us for row or column selection on mouse down
2387 mDragSelectingCells = true;
2388
2389 // Force new selection block
2390 mStartSelectedCell = nullptr;
2391 aNormalSelection.RemoveAllRanges(IgnoreErrors());
2392 // Always do this AFTER RemoveAllRanges
2393 mMode = aTarget;
2394
2395 return SelectRowOrColumn(aChildContent, aNormalSelection);
2396 }
2397 } else {
2398#ifdef DEBUG_TABLE_SELECTION
2399 printf(
2400 "HandleTableSelection: Mouse UP event. "
2401 "mDragSelectingCells=%d, "
2402 "mStartSelectedCell=%p\n",
2403 mDragSelectingCells, mStartSelectedCell.get());
2404#endif
2405 // First check if we are extending a block selection
2406 const uint32_t rangeCount = aNormalSelection.RangeCount();
2407
2408 if (rangeCount > 0 && aMouseEvent->IsShift() && mAppendStartSelectedCell &&
2409 mAppendStartSelectedCell != aChildContent) {
2410 // Shift key is down: append a block selection
2411 mDragSelectingCells = false;
2412
2413 return SelectBlockOfCells(mAppendStartSelectedCell, aChildContent,
2414 aNormalSelection);
2415 }
2416
2417 if (mDragSelectingCells) {
2418 mAppendStartSelectedCell = mStartSelectedCell;
2419 }
2420
2421 mDragSelectingCells = false;
2422 mStartSelectedCell = nullptr;
2423 mEndSelectedCell = nullptr;
2424
2425 // Any other mouseup actions require that Ctrl or Cmd key is pressed
2426 // else stop table selection mode
2427 bool doMouseUpAction = false;
2428#ifdef XP_MACOSX
2429 doMouseUpAction = aMouseEvent->IsMeta();
2430#else
2431 doMouseUpAction = aMouseEvent->IsControl();
2432#endif
2433 if (!doMouseUpAction) {
2434#ifdef DEBUG_TABLE_SELECTION
2435 printf(
2436 "HandleTableSelection: Ending cell selection on mouseup: "
2437 "mAppendStartSelectedCell=%p\n",
2438 mAppendStartSelectedCell.get());
2439#endif
2440 return NS_OK;
2441 }
2442 // Unselect a cell only if it wasn't
2443 // just selected on mousedown
2444 if (aChildContent == mUnselectCellOnMouseUp) {
2445 // Scan ranges to find the cell to unselect (the selection range to
2446 // remove)
2447 // XXXbz it's really weird that this lives outside the loop, so once we
2448 // find one we keep looking at it even if we find no more cells...
2449 nsINode* previousCellParent = nullptr;
2450#ifdef DEBUG_TABLE_SELECTION
2451 printf(
2452 "HandleTableSelection: Unselecting "
2453 "mUnselectCellOnMouseUp; "
2454 "rangeCount=%d\n",
2455 rangeCount);
2456#endif
2457 for (const uint32_t i : IntegerRange(rangeCount)) {
2458 MOZ_ASSERT(aNormalSelection.RangeCount() == rangeCount)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.RangeCount() == rangeCount)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aNormalSelection.RangeCount() == rangeCount))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aNormalSelection.RangeCount() == rangeCount"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2458); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.RangeCount() == rangeCount"
")"); do { *((volatile int*)__null) = 2458; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2459 // Strong reference, because sometimes we want to remove
2460 // this range, and then we might be the only owner.
2461 RefPtr<nsRange> range = aNormalSelection.GetRangeAt(i);
2462 if (MOZ_UNLIKELY(!range)(__builtin_expect(!!(!range), 0))) {
2463 return NS_ERROR_NULL_POINTER;
2464 }
2465
2466 nsINode* container = range->GetStartContainer();
2467 if (!container) {
2468 return NS_ERROR_NULL_POINTER;
2469 }
2470
2471 int32_t offset = range->StartOffset();
2472 // Be sure previous selection is a table cell
2473 nsIContent* child = range->GetChildAtStartOffset();
2474 if (child && IsCell(child)) {
2475 previousCellParent = container;
2476 }
2477
2478 // We're done if we didn't find parent of a previously-selected cell
2479 if (!previousCellParent) {
2480 break;
2481 }
2482
2483 if (previousCellParent == aParentContent && offset == aContentOffset) {
2484 // Cell is already selected
2485 if (rangeCount == 1) {
2486#ifdef DEBUG_TABLE_SELECTION
2487 printf("HandleTableSelection: Unselecting single selected cell\n");
2488#endif
2489 // This was the only cell selected.
2490 // Collapse to "normal" selection inside the cell
2491 mStartSelectedCell = nullptr;
2492 mEndSelectedCell = nullptr;
2493 mAppendStartSelectedCell = nullptr;
2494 // TODO: We need a "Collapse to just before deepest child" routine
2495 // Even better, should we collapse to just after the LAST deepest
2496 // child
2497 // (i.e., at the end of the cell's contents)?
2498 return aNormalSelection.CollapseInLimiter(aChildContent, 0);
2499 }
2500#ifdef DEBUG_TABLE_SELECTION
2501 printf(
2502 "HandleTableSelection: Removing cell from multi-cell "
2503 "selection\n");
2504#endif
2505 // Unselecting the start of previous block
2506 // XXX What do we use now!
2507 if (aChildContent == mAppendStartSelectedCell) {
2508 mAppendStartSelectedCell = nullptr;
2509 }
2510
2511 // Deselect cell by removing its range from selection
2512 ErrorResult err;
2513 aNormalSelection.RemoveRangeAndUnselectFramesAndNotifyListeners(
2514 *range, err);
2515 return err.StealNSResult();
2516 }
2517 }
2518 mUnselectCellOnMouseUp = nullptr;
2519 }
2520 }
2521 return result;
2522}
2523
2524nsresult nsFrameSelection::TableSelection::SelectBlockOfCells(
2525 nsIContent* aStartCell, nsIContent* aEndCell, Selection& aNormalSelection) {
2526 NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER)do { if ((__builtin_expect(!!(!(aStartCell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aStartCell" ") failed",
nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2526); return NS_ERROR_NULL_POINTER; } } while (false)
;
2527 NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER)do { if ((__builtin_expect(!!(!(aEndCell)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aEndCell" ") failed", nullptr
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2527); return NS_ERROR_NULL_POINTER; } } while (false)
;
2528 mEndSelectedCell = aEndCell;
2529
2530 nsresult result = NS_OK;
2531
2532 // If new end cell is in a different table, do nothing
2533 const RefPtr<const nsIContent> table = IsInSameTable(aStartCell, aEndCell);
2534 if (!table) {
2535 return NS_OK;
2536 }
2537
2538 // Get starting and ending cells' location in the cellmap
2539 int32_t startRowIndex, startColIndex, endRowIndex, endColIndex;
2540 result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
2541 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) return result;
2542 result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
2543 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) return result;
2544
2545 if (mDragSelectingCells) {
2546 // Drag selecting: remove selected cells outside of new block limits
2547 // TODO: `UnselectCells`'s return value shouldn't be ignored.
2548 UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
2549 true, aNormalSelection);
2550 }
2551
2552 // Note that we select block in the direction of user's mouse dragging,
2553 // which means start cell may be after the end cell in either row or column
2554 return AddCellsToSelection(table, startRowIndex, startColIndex, endRowIndex,
2555 endColIndex, aNormalSelection);
2556}
2557
2558nsresult nsFrameSelection::TableSelection::UnselectCells(
2559 const nsIContent* aTableContent, int32_t aStartRowIndex,
2560 int32_t aStartColumnIndex, int32_t aEndRowIndex, int32_t aEndColumnIndex,
2561 bool aRemoveOutsideOfCellRange, mozilla::dom::Selection& aNormalSelection) {
2562 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2562); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2562; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2563
2564 nsTableWrapperFrame* tableFrame =
2565 do_QueryFrame(aTableContent->GetPrimaryFrame());
2566 if (!tableFrame) return NS_ERROR_FAILURE;
2567
2568 int32_t minRowIndex = std::min(aStartRowIndex, aEndRowIndex);
2569 int32_t maxRowIndex = std::max(aStartRowIndex, aEndRowIndex);
2570 int32_t minColIndex = std::min(aStartColumnIndex, aEndColumnIndex);
2571 int32_t maxColIndex = std::max(aStartColumnIndex, aEndColumnIndex);
2572
2573 // Strong reference because we sometimes remove the range
2574 RefPtr<nsRange> range = GetFirstCellRange(aNormalSelection);
2575 nsIContent* cellNode = GetFirstSelectedContent(range);
2576 MOZ_ASSERT(!range || cellNode, "Must have cellNode if had a range")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!range || cellNode)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!range || cellNode))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!range || cellNode"
" (" "Must have cellNode if had a range" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2576); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!range || cellNode"
") (" "Must have cellNode if had a range" ")"); do { *((volatile
int*)__null) = 2576; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2577
2578 int32_t curRowIndex, curColIndex;
2579 while (cellNode) {
2580 nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
2581 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) return result;
2582
2583#ifdef DEBUG_TABLE_SELECTION
2584 if (!range) printf("RemoveCellsToSelection -- range is null\n");
2585#endif
2586
2587 if (range) {
2588 if (aRemoveOutsideOfCellRange) {
2589 if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
2590 curColIndex < minColIndex || curColIndex > maxColIndex) {
2591 aNormalSelection.RemoveRangeAndUnselectFramesAndNotifyListeners(
2592 *range, IgnoreErrors());
2593 // Since we've removed the range, decrement pointer to next range
2594 mSelectedCellIndex--;
2595 }
2596
2597 } else {
2598 // Remove cell from selection if it belongs to the given cells range or
2599 // it is spanned onto the cells range.
2600 nsTableCellFrame* cellFrame =
2601 tableFrame->GetCellFrameAt(curRowIndex, curColIndex);
2602
2603 uint32_t origRowIndex = cellFrame->RowIndex();
2604 uint32_t origColIndex = cellFrame->ColIndex();
2605 uint32_t actualRowSpan =
2606 tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex);
2607 uint32_t actualColSpan =
2608 tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex);
2609 if (origRowIndex <= static_cast<uint32_t>(maxRowIndex) &&
2610 maxRowIndex >= 0 &&
2611 origRowIndex + actualRowSpan - 1 >=
2612 static_cast<uint32_t>(minRowIndex) &&
2613 origColIndex <= static_cast<uint32_t>(maxColIndex) &&
2614 maxColIndex >= 0 &&
2615 origColIndex + actualColSpan - 1 >=
2616 static_cast<uint32_t>(minColIndex)) {
2617 aNormalSelection.RemoveRangeAndUnselectFramesAndNotifyListeners(
2618 *range, IgnoreErrors());
2619 // Since we've removed the range, decrement pointer to next range
2620 mSelectedCellIndex--;
2621 }
2622 }
2623 }
2624
2625 range = GetNextCellRange(aNormalSelection);
2626 cellNode = GetFirstSelectedContent(range);
2627 MOZ_ASSERT(!range || cellNode, "Must have cellNode if had a range")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!range || cellNode)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!range || cellNode))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!range || cellNode"
" (" "Must have cellNode if had a range" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!range || cellNode"
") (" "Must have cellNode if had a range" ")"); do { *((volatile
int*)__null) = 2627; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2628 }
2629
2630 return NS_OK;
2631}
2632
2633nsresult SelectCellElement(nsIContent* aCellElement,
2634 Selection& aNormalSelection) {
2635 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2635); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2635; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2636
2637 nsIContent* parent = aCellElement->GetParent();
2638
2639 // Get child offset
2640 const int32_t offset = parent->ComputeIndexOf_Deprecated(aCellElement);
2641
2642 return CreateAndAddRange(parent, offset, aNormalSelection);
2643}
2644
2645static nsresult AddCellsToSelection(const nsIContent* aTableContent,
2646 int32_t aStartRowIndex,
2647 int32_t aStartColumnIndex,
2648 int32_t aEndRowIndex,
2649 int32_t aEndColumnIndex,
2650 Selection& aNormalSelection) {
2651 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2651); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2651; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2652
2653 nsTableWrapperFrame* tableFrame =
2654 do_QueryFrame(aTableContent->GetPrimaryFrame());
2655 if (!tableFrame) { // Check that |table| is a table.
2656 return NS_ERROR_FAILURE;
2657 }
2658
2659 nsresult result = NS_OK;
2660 uint32_t row = aStartRowIndex;
2661 while (true) {
2662 uint32_t col = aStartColumnIndex;
2663 while (true) {
2664 nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col);
2665
2666 // Skip cells that are spanned from previous locations or are already
2667 // selected
2668 if (cellFrame) {
2669 uint32_t origRow = cellFrame->RowIndex();
2670 uint32_t origCol = cellFrame->ColIndex();
2671 if (origRow == row && origCol == col && !cellFrame->IsSelected()) {
2672 result = SelectCellElement(cellFrame->GetContent(), aNormalSelection);
2673 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
2674 return result;
2675 }
2676 }
2677 }
2678 // Done when we reach end column
2679 if (col == static_cast<uint32_t>(aEndColumnIndex)) {
2680 break;
2681 }
2682
2683 if (aStartColumnIndex < aEndColumnIndex) {
2684 col++;
2685 } else {
2686 col--;
2687 }
2688 }
2689 if (row == static_cast<uint32_t>(aEndRowIndex)) {
2690 break;
2691 }
2692
2693 if (aStartRowIndex < aEndRowIndex) {
2694 row++;
2695 } else {
2696 row--;
2697 }
2698 }
2699 return result;
2700}
2701
2702nsresult nsFrameSelection::RemoveCellsFromSelection(nsIContent* aTable,
2703 int32_t aStartRowIndex,
2704 int32_t aStartColumnIndex,
2705 int32_t aEndRowIndex,
2706 int32_t aEndColumnIndex) {
2707 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
2708 const RefPtr<mozilla::dom::Selection> selection = mDomSelections[index];
2709 if (!selection) {
2710 return NS_ERROR_NULL_POINTER;
2711 }
2712
2713 return mTableSelection.UnselectCells(aTable, aStartRowIndex,
2714 aStartColumnIndex, aEndRowIndex,
2715 aEndColumnIndex, false, *selection);
2716}
2717
2718nsresult nsFrameSelection::RestrictCellsToSelection(nsIContent* aTable,
2719 int32_t aStartRowIndex,
2720 int32_t aStartColumnIndex,
2721 int32_t aEndRowIndex,
2722 int32_t aEndColumnIndex) {
2723 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
2724 const RefPtr<mozilla::dom::Selection> selection = mDomSelections[index];
2725 if (!selection) {
2726 return NS_ERROR_NULL_POINTER;
2727 }
2728
2729 return mTableSelection.UnselectCells(aTable, aStartRowIndex,
2730 aStartColumnIndex, aEndRowIndex,
2731 aEndColumnIndex, true, *selection);
2732}
2733
2734Result<nsFrameSelection::TableSelection::FirstAndLastCell, nsresult>
2735nsFrameSelection::TableSelection::FindFirstAndLastCellOfRowOrColumn(
2736 const nsIContent& aCellContent) const {
2737 const nsIContent* table = GetParentTable(&aCellContent);
2738 if (!table) {
2739 return Err(NS_ERROR_NULL_POINTER);
2740 }
2741
2742 // Get table and cell layout interfaces to access
2743 // cell data based on cellmap location
2744 // Frames are not ref counted, so don't use an nsCOMPtr
2745 nsTableWrapperFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame());
2746 if (!tableFrame) {
2747 return Err(NS_ERROR_FAILURE);
2748 }
2749 nsITableCellLayout* cellLayout = GetCellLayout(&aCellContent);
2750 if (!cellLayout) {
2751 return Err(NS_ERROR_FAILURE);
2752 }
2753
2754 // Get location of target cell:
2755 int32_t rowIndex, colIndex;
2756 nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
2757 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
2758 return Err(result);
2759 }
2760
2761 // Be sure we start at proper beginning
2762 // (This allows us to select row or col given ANY cell!)
2763 if (mMode == TableSelectionMode::Row) {
2764 colIndex = 0;
2765 }
2766 if (mMode == TableSelectionMode::Column) {
2767 rowIndex = 0;
2768 }
2769
2770 FirstAndLastCell firstAndLastCell;
2771 while (true) {
2772 // Loop through all cells in column or row to find first and last
2773 nsCOMPtr<nsIContent> curCellContent =
2774 tableFrame->GetCellAt(rowIndex, colIndex);
2775 if (!curCellContent) {
2776 break;
2777 }
2778
2779 if (!firstAndLastCell.mFirst) {
2780 firstAndLastCell.mFirst = curCellContent;
2781 }
2782
2783 firstAndLastCell.mLast = std::move(curCellContent);
2784
2785 // Move to next cell in cellmap, skipping spanned locations
2786 if (mMode == TableSelectionMode::Row) {
2787 colIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
2788 } else {
2789 rowIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex);
2790 }
2791 }
2792 return firstAndLastCell;
2793}
2794
2795nsresult nsFrameSelection::TableSelection::SelectRowOrColumn(
2796 nsIContent* aCellContent, Selection& aNormalSelection) {
2797 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2797; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2798
2799 if (!aCellContent) {
2800 return NS_ERROR_NULL_POINTER;
2801 }
2802
2803 Result<FirstAndLastCell, nsresult> firstAndLastCell =
2804 FindFirstAndLastCellOfRowOrColumn(*aCellContent);
2805 if (firstAndLastCell.isErr()) {
2806 return firstAndLastCell.unwrapErr();
2807 }
2808
2809 // Use SelectBlockOfCells:
2810 // This will replace existing selection,
2811 // but allow unselecting by dragging out of selected region
2812 if (firstAndLastCell.inspect().mFirst && firstAndLastCell.inspect().mLast) {
2813 nsresult rv{NS_OK};
2814
2815 if (!mStartSelectedCell) {
2816 // We are starting a new block, so select the first cell
2817 rv = ::SelectCellElement(firstAndLastCell.inspect().mFirst,
2818 aNormalSelection);
2819 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
2820 return rv;
2821 }
2822 mStartSelectedCell = firstAndLastCell.inspect().mFirst;
2823 }
2824
2825 rv = SelectBlockOfCells(mStartSelectedCell,
2826 firstAndLastCell.inspect().mLast, aNormalSelection);
2827
2828 // This gets set to the cell at end of row/col,
2829 // but we need it to be the cell under cursor
2830 mEndSelectedCell = aCellContent;
2831 return rv;
2832 }
2833
2834#if 0
2835// This is a more efficient strategy that appends row to current selection,
2836// but doesn't allow dragging OFF of an existing selection to unselect!
2837 do {
2838 // Loop through all cells in column or row
2839 result = tableLayout->GetCellDataAt(rowIndex, colIndex,
2840 getter_AddRefs(cellElement),
2841 curRowIndex, curColIndex,
2842 rowSpan, colSpan,
2843 actualRowSpan, actualColSpan,
2844 isSelected);
2845 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) return result;
2846 // We're done when cell is not found
2847 if (!cellElement) break;
2848
2849
2850 // Check spans else we infinitely loop
2851 NS_ASSERTION(actualColSpan, "actualColSpan is 0!")do { if (!(actualColSpan)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "actualColSpan is 0!", "actualColSpan", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2851); MOZ_PretendNoReturn(); } } while (0)
;
2852 NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!")do { if (!(actualRowSpan)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "actualRowSpan is 0!", "actualRowSpan", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2852); MOZ_PretendNoReturn(); } } while (0)
;
2853
2854 // Skip cells that are already selected or span from outside our region
2855 if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
2856 {
2857 result = SelectCellElement(cellElement);
2858 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) return result;
2859 }
2860 // Move to next row or column in cellmap, skipping spanned locations
2861 if (mMode == TableSelectionMode::Row)
2862 colIndex += actualColSpan;
2863 else
2864 rowIndex += actualRowSpan;
2865 }
2866 while (cellElement);
2867#endif
2868
2869 return NS_OK;
2870}
2871
2872// static
2873nsIContent* nsFrameSelection::GetFirstCellNodeInRange(const nsRange* aRange) {
2874 if (!aRange) return nullptr;
2875
2876 nsIContent* childContent = aRange->GetChildAtStartOffset();
2877 if (!childContent) return nullptr;
2878 // Don't return node if not a cell
2879 if (!IsCell(childContent)) return nullptr;
2880
2881 return childContent;
2882}
2883
2884nsRange* nsFrameSelection::TableSelection::GetFirstCellRange(
2885 const mozilla::dom::Selection& aNormalSelection) {
2886 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2886); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2886; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2887
2888 nsRange* firstRange = aNormalSelection.GetRangeAt(0);
2889 if (!GetFirstCellNodeInRange(firstRange)) {
2890 return nullptr;
2891 }
2892
2893 // Setup for next cell
2894 mSelectedCellIndex = 1;
2895
2896 return firstRange;
2897}
2898
2899nsRange* nsFrameSelection::TableSelection::GetNextCellRange(
2900 const mozilla::dom::Selection& aNormalSelection) {
2901 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2901; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2902
2903 nsRange* range =
2904 aNormalSelection.GetRangeAt(AssertedCast<uint32_t>(mSelectedCellIndex));
2905
2906 // Get first node in next range of selection - test if it's a cell
2907 if (!GetFirstCellNodeInRange(range)) {
2908 return nullptr;
2909 }
2910
2911 // Setup for next cell
2912 mSelectedCellIndex++;
2913
2914 return range;
2915}
2916
2917// static
2918nsresult nsFrameSelection::GetCellIndexes(const nsIContent* aCell,
2919 int32_t& aRowIndex,
2920 int32_t& aColIndex) {
2921 if (!aCell) return NS_ERROR_NULL_POINTER;
2922
2923 aColIndex = 0; // initialize out params
2924 aRowIndex = 0;
2925
2926 nsITableCellLayout* cellLayoutObject = GetCellLayout(aCell);
2927 if (!cellLayoutObject) return NS_ERROR_FAILURE;
2928 return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
2929}
2930
2931// static
2932nsIContent* nsFrameSelection::IsInSameTable(const nsIContent* aContent1,
2933 const nsIContent* aContent2) {
2934 if (!aContent1 || !aContent2) return nullptr;
2935
2936 nsIContent* tableNode1 = GetParentTable(aContent1);
2937 nsIContent* tableNode2 = GetParentTable(aContent2);
2938
2939 // Must be in the same table. Note that we want to return false for
2940 // the test if both tables are null.
2941 return (tableNode1 == tableNode2) ? tableNode1 : nullptr;
2942}
2943
2944// static
2945nsIContent* nsFrameSelection::GetParentTable(const nsIContent* aCell) {
2946 if (!aCell) {
2947 return nullptr;
2948 }
2949
2950 for (nsIContent* parent = aCell->GetParent(); parent;
2951 parent = parent->GetParent()) {
2952 if (parent->IsHTMLElement(nsGkAtoms::table)) {
2953 return parent;
2954 }
2955 }
2956
2957 return nullptr;
2958}
2959
2960nsresult nsFrameSelection::SelectCellElement(nsIContent* aCellElement) {
2961 const int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
2962 const RefPtr<Selection> selection = mDomSelections[index];
2963 if (!selection) {
2964 return NS_ERROR_NULL_POINTER;
2965 }
2966
2967 return ::SelectCellElement(aCellElement, *selection);
2968}
2969
2970nsresult CreateAndAddRange(nsINode* aContainer, int32_t aOffset,
2971 Selection& aNormalSelection) {
2972 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNormalSelection.Type() == SelectionType::eNormal)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aNormalSelection.Type() == SelectionType::eNormal)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aNormalSelection.Type() == SelectionType::eNormal"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNormalSelection.Type() == SelectionType::eNormal"
")"); do { *((volatile int*)__null) = 2972; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2973
2974 if (!aContainer) {
2975 return NS_ERROR_NULL_POINTER;
2976 }
2977
2978 // Set range around child at given offset
2979 ErrorResult error;
2980 RefPtr<nsRange> range =
2981 nsRange::Create(aContainer, aOffset, aContainer, aOffset + 1, error);
2982 if (NS_WARN_IF(error.Failed())NS_warn_if_impl(error.Failed(), "error.Failed()", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2982)
) {
2983 return error.StealNSResult();
2984 }
2985 MOZ_ASSERT(range)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(range)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(range))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("range", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 2985); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range" ")")
; do { *((volatile int*)__null) = 2985; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2986
2987 ErrorResult err;
2988 aNormalSelection.AddRangeAndSelectFramesAndNotifyListeners(*range, err);
2989 return err.StealNSResult();
2990}
2991
2992// End of Table Selection
2993
2994void nsFrameSelection::SetAncestorLimiter(nsIContent* aLimiter) {
2995 if (mLimiters.mAncestorLimiter != aLimiter) {
2996 mLimiters.mAncestorLimiter = aLimiter;
2997 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
2998 if (!mDomSelections[index]) return;
2999
3000 if (!IsValidSelectionPoint(mDomSelections[index]->GetFocusNode())) {
3001 ClearNormalSelection();
3002 if (mLimiters.mAncestorLimiter) {
3003 SetChangeReasons(nsISelectionListener::NO_REASON);
3004 nsCOMPtr<nsIContent> limiter(mLimiters.mAncestorLimiter);
3005 const nsresult rv =
3006 TakeFocus(*limiter, 0, 0, CaretAssociationHint::Before,
3007 FocusMode::kCollapseToNewPoint);
3008 Unused << NS_WARN_IF(NS_FAILED(rv))NS_warn_if_impl(((bool)(__builtin_expect(!!(NS_FAILED_impl(rv
)), 0))), "NS_FAILED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3008)
;
3009 // TODO: in case of failure, propagate it to the callers.
3010 }
3011 }
3012 }
3013}
3014
3015void nsFrameSelection::SetDelayedCaretData(WidgetMouseEvent* aMouseEvent) {
3016 if (aMouseEvent) {
3017 mDelayedMouseEvent.mIsValid = true;
3018 mDelayedMouseEvent.mIsShift = aMouseEvent->IsShift();
3019 mDelayedMouseEvent.mClickCount = aMouseEvent->mClickCount;
3020 } else {
3021 mDelayedMouseEvent.mIsValid = false;
3022 }
3023}
3024
3025void nsFrameSelection::DisconnectFromPresShell() {
3026 if (mAccessibleCaretEnabled) {
3027 int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
3028 mDomSelections[index]->StopNotifyingAccessibleCaretEventHub();
3029 }
3030
3031 StopAutoScrollTimer();
3032 for (size_t i = 0; i < ArrayLength(mDomSelections); i++) {
3033 mDomSelections[i]->Clear(nullptr);
3034 }
3035 mPresShell = nullptr;
3036}
3037
3038#ifdef XP_MACOSX
3039/**
3040 * See Bug 1288453.
3041 *
3042 * Update the selection cache on repaint to handle when a pre-existing
3043 * selection becomes active aka the current selection.
3044 *
3045 * 1. Change the current selection by click n dragging another selection.
3046 * - Make a selection on content page. Make a selection in a text editor.
3047 * - You can click n drag the content selection to make it active again.
3048 * 2. Change the current selection when switching to a tab with a selection.
3049 * - Make selection in tab.
3050 * - Switching tabs will make its respective selection active.
3051 *
3052 * Therefore, we only update the selection cache on a repaint
3053 * if the current selection being repainted is not an empty selection.
3054 *
3055 * If the current selection is empty. The current selection cache
3056 * would be cleared by AutoCopyListener::OnSelectionChange().
3057 */
3058static nsresult UpdateSelectionCacheOnRepaintSelection(Selection* aSel) {
3059 PresShell* presShell = aSel->GetPresShell();
3060 if (!presShell) {
3061 return NS_OK;
3062 }
3063 nsCOMPtr<Document> aDoc = presShell->GetDocument();
3064
3065 if (aDoc && aSel && !aSel->IsCollapsed()) {
3066 return nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
3067 aSel, aDoc, nsIClipboard::kSelectionCache, false);
3068 }
3069
3070 return NS_OK;
3071}
3072#endif // XP_MACOSX
3073
3074// mozilla::AutoCopyListener
3075
3076/*
3077 * What we do now:
3078 * On every selection change, we copy to the clipboard anew, creating a
3079 * HTML buffer, a transferable, an nsISupportsString and
3080 * a huge mess every time. This is basically what
3081 * nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() does to move the
3082 * selection into the clipboard for Edit->Copy.
3083 *
3084 * What we should do, to make our end of the deal faster:
3085 * Create a singleton transferable with our own magic converter. When selection
3086 * changes (use a quick cache to detect ``real'' changes), we put the new
3087 * Selection in the transferable. Our magic converter will take care of
3088 * transferable->whatever-other-format when the time comes to actually
3089 * hand over the clipboard contents.
3090 *
3091 * Other issues:
3092 * - which X clipboard should we populate?
3093 * - should we use a different one than Edit->Copy, so that inadvertant
3094 * selections (or simple clicks, which currently cause a selection
3095 * notification, regardless of if they're in the document which currently has
3096 * selection!) don't lose the contents of the ``application''? Or should we
3097 * just put some intelligence in the ``is this a real selection?'' code to
3098 * protect our selection against clicks in other documents that don't create
3099 * selections?
3100 * - maybe we should just never clear the X clipboard? That would make this
3101 * problem just go away, which is very tempting.
3102 *
3103 * On macOS,
3104 * nsIClipboard::kSelectionCache is the flag for current selection cache.
3105 * Set the current selection cache on the parent process in
3106 * widget cocoa nsClipboard whenever selection changes.
3107 */
3108
3109// static
3110void AutoCopyListener::OnSelectionChange(Document* aDocument,
3111 Selection& aSelection,
3112 int16_t aReason) {
3113 MOZ_ASSERT(IsEnabled())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsEnabled())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsEnabled()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("IsEnabled()", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3113); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsEnabled()"
")"); do { *((volatile int*)__null) = 3113; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3114
3115 // For now, we should prevent any updates caused by a call of Selection API.
3116 // We should allow this in some cases later, though. See the valid usage in
3117 // bug 1567160.
3118 if (aReason & nsISelectionListener::JS_REASON) {
3119 return;
3120 }
3121
3122 if (sClipboardID == nsIClipboard::kSelectionCache) {
3123 // Do nothing if this isn't in the active window and,
3124 // in the case of Web content, in the frontmost tab.
3125 if (!aDocument || !IsInActiveTab(aDocument)) {
3126 return;
3127 }
3128 }
3129
3130 static const int16_t kResasonsToHandle =
3131 nsISelectionListener::MOUSEUP_REASON |
3132 nsISelectionListener::SELECTALL_REASON |
3133 nsISelectionListener::KEYPRESS_REASON;
3134 if (!(aReason & kResasonsToHandle)) {
3135 return; // Don't care if we are still dragging.
3136 }
3137
3138 if (!aDocument ||
3139 aSelection.AreNormalAndCrossShadowBoundaryRangesCollapsed()) {
3140#ifdef DEBUG_CLIPBOARD
3141 fprintf(stderrstderr, "CLIPBOARD: no selection/collapsed selection\n");
3142#endif
3143 if (sClipboardID != nsIClipboard::kSelectionCache) {
3144 // XXX Should we clear X clipboard?
3145 return;
3146 }
3147
3148 // If on macOS, clear the current selection transferable cached
3149 // on the parent process (nsClipboard) when the selection is empty.
3150 DebugOnly<nsresult> rv = nsCopySupport::ClearSelectionCache();
3151 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_WARNING, "nsCopySupport::ClearSelectionCache() failed"
, "NS_SUCCEEDED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3152); } } while (false)
3152 "nsCopySupport::ClearSelectionCache() failed")do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_WARNING, "nsCopySupport::ClearSelectionCache() failed"
, "NS_SUCCEEDED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3152); } } while (false)
;
3153 return;
3154 }
3155
3156 DebugOnly<nsresult> rv =
3157 nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
3158 &aSelection, aDocument, sClipboardID, false);
3159 NS_WARNING_ASSERTION(do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_WARNING, "nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() failed"
, "NS_SUCCEEDED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3161); } } while (false)
3160 NS_SUCCEEDED(rv),do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_WARNING, "nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() failed"
, "NS_SUCCEEDED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3161); } } while (false)
3161 "nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() failed")do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_WARNING, "nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() failed"
, "NS_SUCCEEDED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/generic/nsFrameSelection.cpp"
, 3161); } } while (false)
;
3162}