Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "NotificationController.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "DocAccessible-inl.h" |
michael@0 | 9 | #include "TextLeafAccessible.h" |
michael@0 | 10 | #include "TextUpdater.h" |
michael@0 | 11 | |
michael@0 | 12 | #include "mozilla/dom/Element.h" |
michael@0 | 13 | #include "mozilla/Telemetry.h" |
michael@0 | 14 | |
michael@0 | 15 | using namespace mozilla; |
michael@0 | 16 | using namespace mozilla::a11y; |
michael@0 | 17 | |
michael@0 | 18 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 19 | // NotificationCollector |
michael@0 | 20 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 21 | |
michael@0 | 22 | NotificationController::NotificationController(DocAccessible* aDocument, |
michael@0 | 23 | nsIPresShell* aPresShell) : |
michael@0 | 24 | EventQueue(aDocument), mObservingState(eNotObservingRefresh), |
michael@0 | 25 | mPresShell(aPresShell) |
michael@0 | 26 | { |
michael@0 | 27 | // Schedule initial accessible tree construction. |
michael@0 | 28 | ScheduleProcessing(); |
michael@0 | 29 | } |
michael@0 | 30 | |
michael@0 | 31 | NotificationController::~NotificationController() |
michael@0 | 32 | { |
michael@0 | 33 | NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!"); |
michael@0 | 34 | if (mDocument) |
michael@0 | 35 | Shutdown(); |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 39 | // NotificationCollector: AddRef/Release and cycle collection |
michael@0 | 40 | |
michael@0 | 41 | NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController) |
michael@0 | 42 | NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController) |
michael@0 | 43 | |
michael@0 | 44 | NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController) |
michael@0 | 45 | |
michael@0 | 46 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController) |
michael@0 | 47 | if (tmp->mDocument) |
michael@0 | 48 | tmp->Shutdown(); |
michael@0 | 49 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 50 | |
michael@0 | 51 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController) |
michael@0 | 52 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments) |
michael@0 | 53 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions) |
michael@0 | 54 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents) |
michael@0 | 55 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 56 | |
michael@0 | 57 | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef) |
michael@0 | 58 | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release) |
michael@0 | 59 | |
michael@0 | 60 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 61 | // NotificationCollector: public |
michael@0 | 62 | |
michael@0 | 63 | void |
michael@0 | 64 | NotificationController::Shutdown() |
michael@0 | 65 | { |
michael@0 | 66 | if (mObservingState != eNotObservingRefresh && |
michael@0 | 67 | mPresShell->RemoveRefreshObserver(this, Flush_Display)) { |
michael@0 | 68 | mObservingState = eNotObservingRefresh; |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | // Shutdown handling child documents. |
michael@0 | 72 | int32_t childDocCount = mHangingChildDocuments.Length(); |
michael@0 | 73 | for (int32_t idx = childDocCount - 1; idx >= 0; idx--) { |
michael@0 | 74 | if (!mHangingChildDocuments[idx]->IsDefunct()) |
michael@0 | 75 | mHangingChildDocuments[idx]->Shutdown(); |
michael@0 | 76 | } |
michael@0 | 77 | |
michael@0 | 78 | mHangingChildDocuments.Clear(); |
michael@0 | 79 | |
michael@0 | 80 | mDocument = nullptr; |
michael@0 | 81 | mPresShell = nullptr; |
michael@0 | 82 | |
michael@0 | 83 | mTextHash.Clear(); |
michael@0 | 84 | mContentInsertions.Clear(); |
michael@0 | 85 | mNotifications.Clear(); |
michael@0 | 86 | mEvents.Clear(); |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | void |
michael@0 | 90 | NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument) |
michael@0 | 91 | { |
michael@0 | 92 | // Schedule child document binding to the tree. |
michael@0 | 93 | mHangingChildDocuments.AppendElement(aDocument); |
michael@0 | 94 | ScheduleProcessing(); |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | void |
michael@0 | 98 | NotificationController::ScheduleContentInsertion(Accessible* aContainer, |
michael@0 | 99 | nsIContent* aStartChildNode, |
michael@0 | 100 | nsIContent* aEndChildNode) |
michael@0 | 101 | { |
michael@0 | 102 | nsRefPtr<ContentInsertion> insertion = new ContentInsertion(mDocument, |
michael@0 | 103 | aContainer); |
michael@0 | 104 | if (insertion && insertion->InitChildList(aStartChildNode, aEndChildNode) && |
michael@0 | 105 | mContentInsertions.AppendElement(insertion)) { |
michael@0 | 106 | ScheduleProcessing(); |
michael@0 | 107 | } |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 111 | // NotificationCollector: protected |
michael@0 | 112 | |
michael@0 | 113 | void |
michael@0 | 114 | NotificationController::ScheduleProcessing() |
michael@0 | 115 | { |
michael@0 | 116 | // If notification flush isn't planed yet start notification flush |
michael@0 | 117 | // asynchronously (after style and layout). |
michael@0 | 118 | if (mObservingState == eNotObservingRefresh) { |
michael@0 | 119 | if (mPresShell->AddRefreshObserver(this, Flush_Display)) |
michael@0 | 120 | mObservingState = eRefreshObserving; |
michael@0 | 121 | } |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | bool |
michael@0 | 125 | NotificationController::IsUpdatePending() |
michael@0 | 126 | { |
michael@0 | 127 | return mPresShell->IsLayoutFlushObserver() || |
michael@0 | 128 | mObservingState == eRefreshProcessingForUpdate || |
michael@0 | 129 | mContentInsertions.Length() != 0 || mNotifications.Length() != 0 || |
michael@0 | 130 | mTextHash.Count() != 0 || |
michael@0 | 131 | !mDocument->HasLoadState(DocAccessible::eTreeConstructed); |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 135 | // NotificationCollector: private |
michael@0 | 136 | |
michael@0 | 137 | void |
michael@0 | 138 | NotificationController::WillRefresh(mozilla::TimeStamp aTime) |
michael@0 | 139 | { |
michael@0 | 140 | Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer; |
michael@0 | 141 | |
michael@0 | 142 | // If the document accessible that notification collector was created for is |
michael@0 | 143 | // now shut down, don't process notifications anymore. |
michael@0 | 144 | NS_ASSERTION(mDocument, |
michael@0 | 145 | "The document was shut down while refresh observer is attached!"); |
michael@0 | 146 | if (!mDocument) |
michael@0 | 147 | return; |
michael@0 | 148 | |
michael@0 | 149 | if (mObservingState == eRefreshProcessing || |
michael@0 | 150 | mObservingState == eRefreshProcessingForUpdate) |
michael@0 | 151 | return; |
michael@0 | 152 | |
michael@0 | 153 | // Any generic notifications should be queued if we're processing content |
michael@0 | 154 | // insertions or generic notifications. |
michael@0 | 155 | mObservingState = eRefreshProcessingForUpdate; |
michael@0 | 156 | |
michael@0 | 157 | // Initial accessible tree construction. |
michael@0 | 158 | if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) { |
michael@0 | 159 | // If document is not bound to parent at this point then the document is not |
michael@0 | 160 | // ready yet (process notifications later). |
michael@0 | 161 | if (!mDocument->IsBoundToParent()) { |
michael@0 | 162 | mObservingState = eRefreshObserving; |
michael@0 | 163 | return; |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | #ifdef A11Y_LOG |
michael@0 | 167 | if (logging::IsEnabled(logging::eTree)) { |
michael@0 | 168 | logging::MsgBegin("TREE", "initial tree created"); |
michael@0 | 169 | logging::Address("document", mDocument); |
michael@0 | 170 | logging::MsgEnd(); |
michael@0 | 171 | } |
michael@0 | 172 | #endif |
michael@0 | 173 | |
michael@0 | 174 | mDocument->DoInitialUpdate(); |
michael@0 | 175 | |
michael@0 | 176 | NS_ASSERTION(mContentInsertions.Length() == 0, |
michael@0 | 177 | "Pending content insertions while initial accessible tree isn't created!"); |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | // Initialize scroll support if needed. |
michael@0 | 181 | if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized)) |
michael@0 | 182 | mDocument->AddScrollListener(); |
michael@0 | 183 | |
michael@0 | 184 | // Process content inserted notifications to update the tree. Process other |
michael@0 | 185 | // notifications like DOM events and then flush event queue. If any new |
michael@0 | 186 | // notifications are queued during this processing then they will be processed |
michael@0 | 187 | // on next refresh. If notification processing queues up new events then they |
michael@0 | 188 | // are processed in this refresh. If events processing queues up new events |
michael@0 | 189 | // then new events are processed on next refresh. |
michael@0 | 190 | // Note: notification processing or event handling may shut down the owning |
michael@0 | 191 | // document accessible. |
michael@0 | 192 | |
michael@0 | 193 | // Process only currently queued content inserted notifications. |
michael@0 | 194 | nsTArray<nsRefPtr<ContentInsertion> > contentInsertions; |
michael@0 | 195 | contentInsertions.SwapElements(mContentInsertions); |
michael@0 | 196 | |
michael@0 | 197 | uint32_t insertionCount = contentInsertions.Length(); |
michael@0 | 198 | for (uint32_t idx = 0; idx < insertionCount; idx++) { |
michael@0 | 199 | contentInsertions[idx]->Process(); |
michael@0 | 200 | if (!mDocument) |
michael@0 | 201 | return; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | // Process rendered text change notifications. |
michael@0 | 205 | mTextHash.EnumerateEntries(TextEnumerator, mDocument); |
michael@0 | 206 | mTextHash.Clear(); |
michael@0 | 207 | |
michael@0 | 208 | // Bind hanging child documents. |
michael@0 | 209 | uint32_t hangingDocCnt = mHangingChildDocuments.Length(); |
michael@0 | 210 | for (uint32_t idx = 0; idx < hangingDocCnt; idx++) { |
michael@0 | 211 | DocAccessible* childDoc = mHangingChildDocuments[idx]; |
michael@0 | 212 | if (childDoc->IsDefunct()) |
michael@0 | 213 | continue; |
michael@0 | 214 | |
michael@0 | 215 | nsIContent* ownerContent = mDocument->DocumentNode()-> |
michael@0 | 216 | FindContentForSubDocument(childDoc->DocumentNode()); |
michael@0 | 217 | if (ownerContent) { |
michael@0 | 218 | Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent); |
michael@0 | 219 | if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { |
michael@0 | 220 | if (mDocument->AppendChildDocument(childDoc)) |
michael@0 | 221 | continue; |
michael@0 | 222 | |
michael@0 | 223 | outerDocAcc->RemoveChild(childDoc); |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | // Failed to bind the child document, destroy it. |
michael@0 | 227 | childDoc->Shutdown(); |
michael@0 | 228 | } |
michael@0 | 229 | } |
michael@0 | 230 | mHangingChildDocuments.Clear(); |
michael@0 | 231 | |
michael@0 | 232 | // If the document is ready and all its subdocuments are completely loaded |
michael@0 | 233 | // then process the document load. |
michael@0 | 234 | if (mDocument->HasLoadState(DocAccessible::eReady) && |
michael@0 | 235 | !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) && |
michael@0 | 236 | hangingDocCnt == 0) { |
michael@0 | 237 | uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0; |
michael@0 | 238 | for (; childDocIdx < childDocCnt; childDocIdx++) { |
michael@0 | 239 | DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx); |
michael@0 | 240 | if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded)) |
michael@0 | 241 | break; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | if (childDocIdx == childDocCnt) { |
michael@0 | 245 | mDocument->ProcessLoad(); |
michael@0 | 246 | if (!mDocument) |
michael@0 | 247 | return; |
michael@0 | 248 | } |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | // Process only currently queued generic notifications. |
michael@0 | 252 | nsTArray < nsRefPtr<Notification> > notifications; |
michael@0 | 253 | notifications.SwapElements(mNotifications); |
michael@0 | 254 | |
michael@0 | 255 | uint32_t notificationCount = notifications.Length(); |
michael@0 | 256 | for (uint32_t idx = 0; idx < notificationCount; idx++) { |
michael@0 | 257 | notifications[idx]->Process(); |
michael@0 | 258 | if (!mDocument) |
michael@0 | 259 | return; |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | // Process invalidation list of the document after all accessible tree |
michael@0 | 263 | // modification are done. |
michael@0 | 264 | mDocument->ProcessInvalidationList(); |
michael@0 | 265 | |
michael@0 | 266 | // If a generic notification occurs after this point then we may be allowed to |
michael@0 | 267 | // process it synchronously. However we do not want to reenter if fireing |
michael@0 | 268 | // events causes script to run. |
michael@0 | 269 | mObservingState = eRefreshProcessing; |
michael@0 | 270 | |
michael@0 | 271 | ProcessEventQueue(); |
michael@0 | 272 | mObservingState = eRefreshObserving; |
michael@0 | 273 | if (!mDocument) |
michael@0 | 274 | return; |
michael@0 | 275 | |
michael@0 | 276 | // Stop further processing if there are no new notifications of any kind or |
michael@0 | 277 | // events and document load is processed. |
michael@0 | 278 | if (mContentInsertions.IsEmpty() && mNotifications.IsEmpty() && |
michael@0 | 279 | mEvents.IsEmpty() && mTextHash.Count() == 0 && |
michael@0 | 280 | mHangingChildDocuments.IsEmpty() && |
michael@0 | 281 | mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) && |
michael@0 | 282 | mPresShell->RemoveRefreshObserver(this, Flush_Display)) { |
michael@0 | 283 | mObservingState = eNotObservingRefresh; |
michael@0 | 284 | } |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 288 | // Notification controller: text leaf accessible text update |
michael@0 | 289 | |
michael@0 | 290 | PLDHashOperator |
michael@0 | 291 | NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry, |
michael@0 | 292 | void* aUserArg) |
michael@0 | 293 | { |
michael@0 | 294 | DocAccessible* document = static_cast<DocAccessible*>(aUserArg); |
michael@0 | 295 | nsIContent* textNode = aEntry->GetKey(); |
michael@0 | 296 | Accessible* textAcc = document->GetAccessible(textNode); |
michael@0 | 297 | |
michael@0 | 298 | // If the text node is not in tree or doesn't have frame then this case should |
michael@0 | 299 | // have been handled already by content removal notifications. |
michael@0 | 300 | nsINode* containerNode = textNode->GetParentNode(); |
michael@0 | 301 | if (!containerNode) { |
michael@0 | 302 | NS_ASSERTION(!textAcc, |
michael@0 | 303 | "Text node was removed but accessible is kept alive!"); |
michael@0 | 304 | return PL_DHASH_NEXT; |
michael@0 | 305 | } |
michael@0 | 306 | |
michael@0 | 307 | nsIFrame* textFrame = textNode->GetPrimaryFrame(); |
michael@0 | 308 | if (!textFrame) { |
michael@0 | 309 | NS_ASSERTION(!textAcc, |
michael@0 | 310 | "Text node isn't rendered but accessible is kept alive!"); |
michael@0 | 311 | return PL_DHASH_NEXT; |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | nsIContent* containerElm = containerNode->IsElement() ? |
michael@0 | 315 | containerNode->AsElement() : nullptr; |
michael@0 | 316 | |
michael@0 | 317 | nsAutoString text; |
michael@0 | 318 | textFrame->GetRenderedText(&text); |
michael@0 | 319 | |
michael@0 | 320 | // Remove text accessible if rendered text is empty. |
michael@0 | 321 | if (textAcc) { |
michael@0 | 322 | if (text.IsEmpty()) { |
michael@0 | 323 | #ifdef A11Y_LOG |
michael@0 | 324 | if (logging::IsEnabled(logging::eTree | logging::eText)) { |
michael@0 | 325 | logging::MsgBegin("TREE", "text node lost its content"); |
michael@0 | 326 | logging::Node("container", containerElm); |
michael@0 | 327 | logging::Node("content", textNode); |
michael@0 | 328 | logging::MsgEnd(); |
michael@0 | 329 | } |
michael@0 | 330 | #endif |
michael@0 | 331 | |
michael@0 | 332 | document->ContentRemoved(containerElm, textNode); |
michael@0 | 333 | return PL_DHASH_NEXT; |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | // Update text of the accessible and fire text change events. |
michael@0 | 337 | #ifdef A11Y_LOG |
michael@0 | 338 | if (logging::IsEnabled(logging::eText)) { |
michael@0 | 339 | logging::MsgBegin("TEXT", "text may be changed"); |
michael@0 | 340 | logging::Node("container", containerElm); |
michael@0 | 341 | logging::Node("content", textNode); |
michael@0 | 342 | logging::MsgEntry("old text '%s'", |
michael@0 | 343 | NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get()); |
michael@0 | 344 | logging::MsgEntry("new text: '%s'", |
michael@0 | 345 | NS_ConvertUTF16toUTF8(text).get()); |
michael@0 | 346 | logging::MsgEnd(); |
michael@0 | 347 | } |
michael@0 | 348 | #endif |
michael@0 | 349 | |
michael@0 | 350 | TextUpdater::Run(document, textAcc->AsTextLeaf(), text); |
michael@0 | 351 | return PL_DHASH_NEXT; |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | // Append an accessible if rendered text is not empty. |
michael@0 | 355 | if (!text.IsEmpty()) { |
michael@0 | 356 | #ifdef A11Y_LOG |
michael@0 | 357 | if (logging::IsEnabled(logging::eTree | logging::eText)) { |
michael@0 | 358 | logging::MsgBegin("TREE", "text node gains new content"); |
michael@0 | 359 | logging::Node("container", containerElm); |
michael@0 | 360 | logging::Node("content", textNode); |
michael@0 | 361 | logging::MsgEnd(); |
michael@0 | 362 | } |
michael@0 | 363 | #endif |
michael@0 | 364 | |
michael@0 | 365 | // Make sure the text node is in accessible document still. |
michael@0 | 366 | Accessible* container = document->GetAccessibleOrContainer(containerNode); |
michael@0 | 367 | NS_ASSERTION(container, |
michael@0 | 368 | "Text node having rendered text hasn't accessible document!"); |
michael@0 | 369 | if (container) { |
michael@0 | 370 | nsTArray<nsCOMPtr<nsIContent> > insertedContents; |
michael@0 | 371 | insertedContents.AppendElement(textNode); |
michael@0 | 372 | document->ProcessContentInserted(container, &insertedContents); |
michael@0 | 373 | } |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | return PL_DHASH_NEXT; |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | |
michael@0 | 380 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 381 | // NotificationController: content inserted notification |
michael@0 | 382 | |
michael@0 | 383 | NotificationController::ContentInsertion:: |
michael@0 | 384 | ContentInsertion(DocAccessible* aDocument, Accessible* aContainer) : |
michael@0 | 385 | mDocument(aDocument), mContainer(aContainer) |
michael@0 | 386 | { |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | bool |
michael@0 | 390 | NotificationController::ContentInsertion:: |
michael@0 | 391 | InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode) |
michael@0 | 392 | { |
michael@0 | 393 | bool haveToUpdate = false; |
michael@0 | 394 | |
michael@0 | 395 | nsIContent* node = aStartChildNode; |
michael@0 | 396 | while (node != aEndChildNode) { |
michael@0 | 397 | // Notification triggers for content insertion even if no content was |
michael@0 | 398 | // actually inserted, check if the given content has a frame to discard |
michael@0 | 399 | // this case early. |
michael@0 | 400 | if (node->GetPrimaryFrame()) { |
michael@0 | 401 | if (mInsertedContent.AppendElement(node)) |
michael@0 | 402 | haveToUpdate = true; |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | node = node->GetNextSibling(); |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | return haveToUpdate; |
michael@0 | 409 | } |
michael@0 | 410 | |
michael@0 | 411 | NS_IMPL_CYCLE_COLLECTION(NotificationController::ContentInsertion, |
michael@0 | 412 | mContainer) |
michael@0 | 413 | |
michael@0 | 414 | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController::ContentInsertion, |
michael@0 | 415 | AddRef) |
michael@0 | 416 | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController::ContentInsertion, |
michael@0 | 417 | Release) |
michael@0 | 418 | |
michael@0 | 419 | void |
michael@0 | 420 | NotificationController::ContentInsertion::Process() |
michael@0 | 421 | { |
michael@0 | 422 | mDocument->ProcessContentInserted(mContainer, &mInsertedContent); |
michael@0 | 423 | |
michael@0 | 424 | mDocument = nullptr; |
michael@0 | 425 | mContainer = nullptr; |
michael@0 | 426 | mInsertedContent.Clear(); |
michael@0 | 427 | } |
michael@0 | 428 |