accessible/src/base/NotificationController.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial