FilteredContentIterator.cpp (10769B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "FilteredContentIterator.h" 7 8 #include <utility> 9 10 #include "mozilla/ContentIterator.h" 11 #include "mozilla/dom/AbstractRange.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/mozalloc.h" 14 #include "nsAtom.h" 15 #include "nsComponentManagerUtils.h" 16 #include "nsComposeTxtSrvFilter.h" 17 #include "nsContentUtils.h" 18 #include "nsDebug.h" 19 #include "nsError.h" 20 #include "nsIContent.h" 21 #include "nsINode.h" 22 #include "nsISupports.h" 23 #include "nsISupportsUtils.h" 24 #include "nsRange.h" 25 26 namespace mozilla { 27 28 using namespace dom; 29 30 FilteredContentIterator::FilteredContentIterator( 31 UniquePtr<nsComposeTxtSrvFilter> aFilter) 32 : mCurrentIterator(nullptr), 33 mFilter(std::move(aFilter)), 34 mDidSkip(false), 35 mIsOutOfRange(false), 36 mDirection(eDirNotSet) {} 37 38 FilteredContentIterator::~FilteredContentIterator() {} 39 40 NS_IMPL_CYCLE_COLLECTION(FilteredContentIterator, mPostIterator, mPreIterator, 41 mRange) 42 43 nsresult FilteredContentIterator::Init(nsINode* aRoot) { 44 NS_ENSURE_ARG_POINTER(aRoot); 45 mIsOutOfRange = false; 46 mDirection = eForward; 47 mCurrentIterator = &mPreIterator; 48 49 mRange = nsRange::Create(aRoot); 50 mRange->SelectNode(*aRoot, IgnoreErrors()); 51 52 nsresult rv = mPreIterator.Init(mRange); 53 NS_ENSURE_SUCCESS(rv, rv); 54 return mPostIterator.Init(mRange); 55 } 56 57 nsresult FilteredContentIterator::Init(const AbstractRange* aAbstractRange) { 58 if (NS_WARN_IF(!aAbstractRange)) { 59 return NS_ERROR_INVALID_ARG; 60 } 61 62 if (NS_WARN_IF(!aAbstractRange->IsPositioned())) { 63 return NS_ERROR_INVALID_ARG; 64 } 65 66 mRange = nsRange::Create(aAbstractRange, IgnoreErrors()); 67 if (NS_WARN_IF(!mRange)) { 68 return NS_ERROR_FAILURE; 69 } 70 return InitWithRange(); 71 } 72 73 nsresult FilteredContentIterator::Init(nsINode* aStartContainer, 74 uint32_t aStartOffset, 75 nsINode* aEndContainer, 76 uint32_t aEndOffset) { 77 return Init(RawRangeBoundary(aStartContainer, aStartOffset), 78 RawRangeBoundary(aEndContainer, aEndOffset)); 79 } 80 81 nsresult FilteredContentIterator::Init(const RawRangeBoundary& aStartBoundary, 82 const RawRangeBoundary& aEndBoundary) { 83 RefPtr<nsRange> range = 84 nsRange::Create(aStartBoundary, aEndBoundary, IgnoreErrors()); 85 if (NS_WARN_IF(!range) || NS_WARN_IF(!range->IsPositioned())) { 86 return NS_ERROR_INVALID_ARG; 87 } 88 89 MOZ_ASSERT(range->StartRef() == aStartBoundary); 90 MOZ_ASSERT(range->EndRef() == aEndBoundary); 91 92 mRange = std::move(range); 93 94 return InitWithRange(); 95 } 96 97 nsresult FilteredContentIterator::InitWithRange() { 98 MOZ_ASSERT(mRange); 99 MOZ_ASSERT(mRange->IsPositioned()); 100 101 mIsOutOfRange = false; 102 mDirection = eForward; 103 mCurrentIterator = &mPreIterator; 104 105 nsresult rv = mPreIterator.Init(mRange); 106 if (NS_WARN_IF(NS_FAILED(rv))) { 107 return rv; 108 } 109 return mPostIterator.Init(mRange); 110 } 111 112 nsresult FilteredContentIterator::SwitchDirections(bool aChangeToForward) { 113 nsINode* node = mCurrentIterator->GetCurrentNode(); 114 115 if (aChangeToForward) { 116 mCurrentIterator = &mPreIterator; 117 mDirection = eForward; 118 } else { 119 mCurrentIterator = &mPostIterator; 120 mDirection = eBackward; 121 } 122 123 if (node) { 124 nsresult rv = mCurrentIterator->PositionAt(node); 125 if (NS_FAILED(rv)) { 126 mIsOutOfRange = true; 127 return rv; 128 } 129 } 130 return NS_OK; 131 } 132 133 void FilteredContentIterator::First() { 134 if (!mCurrentIterator) { 135 NS_ERROR("Missing iterator!"); 136 137 return; 138 } 139 140 // If we are switching directions then 141 // we need to switch how we process the nodes 142 if (mDirection != eForward) { 143 mCurrentIterator = &mPreIterator; 144 mDirection = eForward; 145 mIsOutOfRange = false; 146 } 147 148 mCurrentIterator->First(); 149 150 if (mCurrentIterator->IsDone()) { 151 return; 152 } 153 154 nsINode* currentNode = mCurrentIterator->GetCurrentNode(); 155 156 bool didCross; 157 CheckAdvNode(currentNode, didCross, eForward); 158 } 159 160 void FilteredContentIterator::Last() { 161 if (!mCurrentIterator) { 162 NS_ERROR("Missing iterator!"); 163 164 return; 165 } 166 167 // If we are switching directions then 168 // we need to switch how we process the nodes 169 if (mDirection != eBackward) { 170 mCurrentIterator = &mPostIterator; 171 mDirection = eBackward; 172 mIsOutOfRange = false; 173 } 174 175 mCurrentIterator->Last(); 176 177 if (mCurrentIterator->IsDone()) { 178 return; 179 } 180 181 nsINode* currentNode = mCurrentIterator->GetCurrentNode(); 182 183 bool didCross; 184 CheckAdvNode(currentNode, didCross, eBackward); 185 } 186 187 /////////////////////////////////////////////////////////////////////////// 188 // ContentIsInTraversalRange: returns true if content is visited during 189 // the traversal of the range in the specified mode. 190 // 191 static bool ContentIsInTraversalRange(nsIContent* aContent, bool aIsPreMode, 192 const RangeBoundary& aStartBoundary, 193 const RangeBoundary& aEndBoundary) { 194 NS_ENSURE_TRUE(aStartBoundary.IsSet() && aEndBoundary.IsSet() && aContent, 195 false); 196 197 nsIContent* parentContent = aContent->GetParent(); 198 if (NS_WARN_IF(!parentContent) || 199 NS_WARN_IF(parentContent->IsRootOfNativeAnonymousSubtree())) { 200 return false; 201 } 202 const RawRangeBoundary compPoint( 203 parentContent, aIsPreMode ? aContent->GetPreviousSibling() : aContent); 204 205 const Maybe<int32_t> startRes = 206 nsContentUtils::ComparePoints(aStartBoundary, compPoint); 207 if (NS_WARN_IF(!startRes)) { 208 return false; 209 } 210 const Maybe<int32_t> endRes = 211 nsContentUtils::ComparePoints(aEndBoundary, compPoint); 212 if (NS_WARN_IF(!endRes)) { 213 return false; 214 } 215 return *startRes <= 0 && *endRes >= 0; 216 } 217 218 static bool ContentIsInTraversalRange(nsRange* aRange, nsIContent* aNextContent, 219 bool aIsPreMode) { 220 // XXXbz we have a caller below (in AdvanceNode) who passes null for 221 // aNextContent! 222 NS_ENSURE_TRUE(aNextContent && aRange, false); 223 224 return ContentIsInTraversalRange(aNextContent, aIsPreMode, aRange->StartRef(), 225 aRange->EndRef()); 226 } 227 228 // Helper function to advance to the next or previous node 229 nsresult FilteredContentIterator::AdvanceNode(nsINode* aNode, 230 nsINode*& aNewNode, 231 eDirectionType aDir) { 232 nsCOMPtr<nsIContent> nextNode; 233 if (aDir == eForward) { 234 nextNode = aNode->GetNextSibling(); 235 } else { 236 nextNode = aNode->GetPreviousSibling(); 237 } 238 239 if (nextNode) { 240 // If we got here, that means we found the nxt/prv node 241 // make sure it is in our DOMRange 242 bool intersects = 243 ContentIsInTraversalRange(mRange, nextNode, aDir == eForward); 244 if (intersects) { 245 aNewNode = nextNode; 246 NS_ADDREF(aNewNode); 247 return NS_OK; 248 } 249 } else { 250 // The next node was null so we need to walk up the parent(s) 251 nsCOMPtr<nsINode> parent = aNode->GetParentNode(); 252 NS_ASSERTION(parent, "parent can't be nullptr"); 253 254 // Make sure the parent is in the DOMRange before going further 255 // XXXbz why are we passing nextNode, not the parent??? If this gets fixed, 256 // then ContentIsInTraversalRange can stop null-checking its second arg. 257 bool intersects = 258 ContentIsInTraversalRange(mRange, nextNode, aDir == eForward); 259 if (intersects) { 260 // Now find the nxt/prv node after/before this node 261 nsresult rv = AdvanceNode(parent, aNewNode, aDir); 262 if (NS_SUCCEEDED(rv) && aNewNode) { 263 return NS_OK; 264 } 265 } 266 } 267 268 // if we get here it pretty much means 269 // we went out of the DOM Range 270 mIsOutOfRange = true; 271 272 return NS_ERROR_FAILURE; 273 } 274 275 // Helper function to see if the next/prev node should be skipped 276 void FilteredContentIterator::CheckAdvNode(nsINode* aNode, bool& aDidSkip, 277 eDirectionType aDir) { 278 aDidSkip = false; 279 mIsOutOfRange = false; 280 281 if (aNode && mFilter) { 282 nsCOMPtr<nsINode> currentNode = aNode; 283 while (1) { 284 if (mFilter->Skip(aNode)) { 285 aDidSkip = true; 286 // Get the next/prev node and then 287 // see if we should skip that 288 nsCOMPtr<nsINode> advNode; 289 nsresult rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir); 290 if (NS_SUCCEEDED(rv) && advNode) { 291 aNode = advNode; 292 } else { 293 return; // fell out of range 294 } 295 } else { 296 if (aNode != currentNode) { 297 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode)); 298 (void)mCurrentIterator->PositionAt(content); 299 } 300 return; // found something 301 } 302 } 303 } 304 } 305 306 void FilteredContentIterator::Next() { 307 if (mIsOutOfRange || !mCurrentIterator) { 308 NS_ASSERTION(mCurrentIterator, "Missing iterator!"); 309 310 return; 311 } 312 313 // If we are switching directions then 314 // we need to switch how we process the nodes 315 if (mDirection != eForward) { 316 nsresult rv = SwitchDirections(true); 317 if (NS_FAILED(rv)) { 318 return; 319 } 320 } 321 322 mCurrentIterator->Next(); 323 324 if (mCurrentIterator->IsDone()) { 325 return; 326 } 327 328 // If we can't get the current node then 329 // don't check to see if we can skip it 330 nsINode* currentNode = mCurrentIterator->GetCurrentNode(); 331 332 CheckAdvNode(currentNode, mDidSkip, eForward); 333 } 334 335 void FilteredContentIterator::Prev() { 336 if (mIsOutOfRange || !mCurrentIterator) { 337 NS_ASSERTION(mCurrentIterator, "Missing iterator!"); 338 339 return; 340 } 341 342 // If we are switching directions then 343 // we need to switch how we process the nodes 344 if (mDirection != eBackward) { 345 nsresult rv = SwitchDirections(false); 346 if (NS_FAILED(rv)) { 347 return; 348 } 349 } 350 351 mCurrentIterator->Prev(); 352 353 if (mCurrentIterator->IsDone()) { 354 return; 355 } 356 357 // If we can't get the current node then 358 // don't check to see if we can skip it 359 nsINode* currentNode = mCurrentIterator->GetCurrentNode(); 360 361 CheckAdvNode(currentNode, mDidSkip, eBackward); 362 } 363 364 nsINode* FilteredContentIterator::GetCurrentNode() { 365 if (mIsOutOfRange || !mCurrentIterator) { 366 return nullptr; 367 } 368 369 return mCurrentIterator->GetCurrentNode(); 370 } 371 372 bool FilteredContentIterator::IsDone() { 373 if (mIsOutOfRange || !mCurrentIterator) { 374 return true; 375 } 376 377 return mCurrentIterator->IsDone(); 378 } 379 380 nsresult FilteredContentIterator::PositionAt(nsINode* aCurNode) { 381 NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE); 382 mIsOutOfRange = false; 383 return mCurrentIterator->PositionAt(aCurNode); 384 } 385 386 } // namespace mozilla