Bug Summary

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