Commit 691628a3 authored by Bill McCloskey's avatar Bill McCloskey
Browse files

Bug 1364570 - Dispatch link prefetch events asynchronously to avoid DocGroup mismatches (r=bz)

When we send out a prefetch request, we act as if the load came
from one of the possibly many documents containing <link> element
for the given URL. The docgroup assigned to this request is
derived from this document. Later, when the load finishes, the
OnStopRequest code runs in a runnable labeled with this
docgroup. OnStopRequest dispatches a load event to *all* the link
elements, including some that might be in different docgroups
from the OnStopRequest runnable. This generates an assertion.

To fix this, I decided to dispatch the load events
asynchronously. I'm hoping the extra round trip through the event
loop shouldn't hurt us too much since I doubt anyone actually
listens for these events.

MozReview-Commit-ID: FTkjuHO7RFp
parent ea01a3bc
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -39,6 +39,13 @@ AsyncEventDispatcher::Run()
  if (mCanceled) {
    return NS_OK;
  }
  if (mCheckStillInDoc) {
    nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
    MOZ_ASSERT(node);
    if (!node->IsInComposedDoc()) {
      return NS_OK;
    }
  }
  mTarget->AsyncEventRunning(this);
  RefPtr<Event> event = mEvent ? mEvent->InternalDOMEvent() : nullptr;
  if (!event) {
@@ -88,6 +95,17 @@ AsyncEventDispatcher::RunDOMEventWhenSafe()
  nsContentUtils::AddScriptRunner(this);
}

void
AsyncEventDispatcher::RequireNodeInDocument()
{
#ifdef DEBUG
  nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
  MOZ_ASSERT(node);
#endif

  mCheckStillInDoc = true;
}

/******************************************************************************
 * mozilla::LoadBlockingAsyncEventDispatcher
 ******************************************************************************/
+6 −0
Original line number Diff line number Diff line
@@ -64,12 +64,18 @@ public:
  nsresult PostDOMEvent();
  void RunDOMEventWhenSafe();

  // Calling this causes the Run() method to check that
  // mTarget->IsInComposedDoc(). mTarget must be an nsINode or else we'll
  // assert.
  void RequireNodeInDocument();

  nsCOMPtr<dom::EventTarget> mTarget;
  nsCOMPtr<nsIDOMEvent> mEvent;
  nsString              mEventType;
  bool                  mBubbles = false;
  bool                  mOnlyChromeDispatch = false;
  bool                  mCanceled = false;
  bool                  mCheckStillInDoc = false;
};

class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher
+18 −11
Original line number Diff line number Diff line
@@ -4,6 +4,13 @@
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsPrefetchService.h"

#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/Preferences.h"

#include "nsICacheEntry.h"
#include "nsIServiceManager.h"
#include "nsICategoryManager.h"
@@ -25,10 +32,6 @@
#include "mozilla/Logging.h"
#include "plstr.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "mozilla/Preferences.h"
#include "mozilla/Attributes.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "nsIDOMNode.h"
#include "nsINode.h"
#include "nsIDocument.h"
@@ -494,13 +497,17 @@ nsPrefetchService::DispatchEvent(nsPrefetchNode *node, bool aSuccess)
    for (uint32_t i = 0; i < node->mSources.Length(); i++) {
      nsCOMPtr<nsINode> domNode = do_QueryReferent(node->mSources.ElementAt(i));
      if (domNode && domNode->IsInComposedDoc()) {
        nsContentUtils::DispatchTrustedEvent(domNode->OwnerDoc(),
                                             domNode,
        // We don't dispatch synchronously since |node| might be in a DocGroup
        // that we're not allowed to touch. (Our network request happens in the
        // DocGroup of one of the mSources nodes--not necessarily this one).
        RefPtr<AsyncEventDispatcher> dispatcher =
          new AsyncEventDispatcher(domNode,
                                   aSuccess ?
                                    NS_LITERAL_STRING("load") :
                                    NS_LITERAL_STRING("error"),
                                             /* aCanBubble = */ false,
                                             /* aCancelable = */ false);
                                   /* aCanBubble = */ false);
        dispatcher->RequireNodeInDocument();
        dispatcher->PostDOMEvent();
      }
    }
}