Bug XXX - Use XUL prototype cache with HTML. r=? draft
authorBrendan Dahl <bdahl@mozilla.com>
Mon, 26 Nov 2018 16:21:14 -0800
changeset 1770712 5899e4b79198d96fd4dc5fcb0b0481368b4106db
parent 1757807 84b9cfcef11a337689bfb15944cf480e3cec415a
child 1770713 bfeeef15632b46169f8c1d5dd82756d4c0a2c9fd
push id318039
push userbdahl@mozilla.com
push dateTue, 27 Nov 2018 00:22:46 +0000
treeherdertry@bfeeef15632b [default view] [failures only]
milestone65.0a1
Bug XXX - Use XUL prototype cache with HTML. r=?
browser/base/content/browser.js
browser/base/content/browser.xul
dom/html/nsHTMLDocument.cpp
dom/xul/XULDocument.cpp
dom/xul/XULDocument.h
dom/xul/nsXULPrototypeCache.cpp
layout/build/nsContentDLF.cpp
parser/cache/CacheParser.cpp
parser/cache/CacheParser.h
parser/cache/moz.build
parser/moz.build
toolkit/content/editMenuCommands.inc.xul
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1279,16 +1279,18 @@ var gBrowserInit = {
       document.documentElement.setAttribute("width", width);
       document.documentElement.setAttribute("height", height);
 
       if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
         document.documentElement.setAttribute("sizemode", "maximized");
       }
     }
 
+    document.documentElement.removeAttribute("hidden");
+
     // Run menubar initialization first, to avoid TabsInTitlebar code picking
     // up mutations from it and causing a reflow.
     AutoHideMenubar.init();
     // Update the chromemargin attribute so the window can be sized correctly.
     window.TabBarVisibility.update();
     TabsInTitlebar.init();
 
     new LightweightThemeConsumer(document);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -89,27 +89,27 @@ xmlns="http://www.w3.org/1999/xhtml"
 >
   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this);
   Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser.js", this);
 
   window.onload = gBrowserInit.onLoad.bind(gBrowserInit);
   window.onunload = gBrowserInit.onUnload.bind(gBrowserInit);
   window.onclose = WindowIsClosing;
 
-#ifdef BROWSER_XHTML
+if (!(window.document instanceof XULDocument)) {
   window.addEventListener("readystatechange", () => {
     // We initially hide the window to prevent layouts during parse. This lets us
     // avoid accidental XBL construction and better match browser.xul (see Bug 1497975).
     gBrowserInit.onBeforeInitialXULLayout();
     document.documentElement.removeAttribute("hidden");
   }, { once: true, capture: true });
-#else
+} else {
   window.addEventListener("MozBeforeInitialXULLayout",
     gBrowserInit.onBeforeInitialXULLayout.bind(gBrowserInit), { once: true });
-#endif
+}
   // The listener of DOMContentLoaded must be set on window, rather than
   // document, because the window can go away before the event is fired.
   // In that case, we don't want to initialize anything, otherwise we
   // may be leaking things because they will never be destroyed after.
   window.addEventListener("DOMContentLoaded",
     gBrowserInit.onDOMContentLoaded.bind(gBrowserInit), { once: true });
 </script>
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -47,16 +47,17 @@
 
 #include "nsNetCID.h"
 #include "nsICookieService.h"
 
 #include "nsIServiceManager.h"
 #include "nsIConsoleService.h"
 #include "nsIComponentManager.h"
 #include "nsParserCIID.h"
+#include "mozilla/parser/CacheParser.h"
 #include "nsNameSpaceManager.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/css/Loader.h"
 #include "nsIHttpChannel.h"
 #include "nsIFile.h"
 #include "nsFrameSelection.h"
 
 #include "nsContentUtils.h"
@@ -104,16 +105,17 @@
 #include "nsFocusManager.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/Unused.h"
 #include "nsCommandParams.h"
+#include "nsXULPrototypeCache.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
 
 #include "prtime.h"
 
@@ -601,18 +603,25 @@ nsHTMLDocument::StartDocumentLoad(const 
         mParser->MarkAsNotScriptCreated("plain-text");
       }
     } else if (viewSource && !html) {
       mParser->MarkAsNotScriptCreated("view-source-xml");
     } else {
       mParser->MarkAsNotScriptCreated(aCommand);
     }
   } else {
-    mParser = do_CreateInstance(kCParserCID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIURI> url;
+    aChannel->GetURI(getter_AddRefs(url));
+    if (nsXULPrototypeCache::GetInstance()->IsCached(url)) {
+      printf(">>> Creating cached parser url=%s\n", url->GetSpecOrDefault().get());
+      mParser = new mozilla::parser::CacheParser();
+    } else {
+      mParser = do_CreateInstance(kCParserCID, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
   }
 
   // Look for the parent document.  Note that at this point we don't have our
   // content viewer set up yet, and therefore do not have a useful
   // mParentDocument.
 
   // in this block of code, if we get an error result, we return it
   // but if we get a null pointer, that's perfectly legal for parent
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -121,17 +121,17 @@ LazyLogModule XULDocument::gXULLog("XULD
 //
 // ctors & dtors
 //
 
 namespace mozilla {
 namespace dom {
 
 XULDocument::XULDocument(void)
-    : XMLDocument("application/vnd.mozilla.xul+xml"),
+    : nsHTMLDocument(),
       mNextSrcLoadWaiter(nullptr),
       mIsWritingFastLoad(false),
       mDocumentLoaded(false),
       mStillWalking(false),
       mPendingSheets(0),
       mCurrentScriptProto(nullptr),
       mOffThreadCompiling(false),
       mOffThreadCompileStringBuf(nullptr),
@@ -142,16 +142,17 @@ XULDocument::XULDocument(void)
     mCharacterSet = UTF_8_ENCODING;
 
     mDefaultElementType = kNameSpaceID_XUL;
     mType = eXUL;
 
     mDelayFrameLoaderInitialization = true;
 
     mAllowXULXBL = eTriTrue;
+    SetContentTypeInternal(nsDependentCString("application/vnd.mozilla.xul+xml"));
 }
 
 XULDocument::~XULDocument()
 {
     NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
         "unreferenced document still waiting for script source to load?");
 
     Preferences::UnregisterCallback(XULDocument::DirectionChanged,
@@ -189,32 +190,32 @@ namespace dom {
 
 //----------------------------------------------------------------------
 //
 // nsISupports interface
 //
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, nsHTMLDocument)
     NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
                  "Shouldn't traverse XULDocument!");
     // XXX tmp->mContextStack?
 
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, nsHTMLDocument)
     //XXX We should probably unlink all the objects we traverse.
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULDocument,
-                                             XMLDocument,
+                                             nsHTMLDocument,
                                              nsIStreamLoaderObserver,
                                              nsICSSLoaderObserver,
                                              nsIOffThreadScriptReceiver)
 
 
 //----------------------------------------------------------------------
 //
 // nsIDocument interface
@@ -522,17 +523,17 @@ XULDocument::Clone(mozilla::dom::NodeInf
 //----------------------------------------------------------------------
 //
 // Implementation methods
 //
 
 nsresult
 XULDocument::Init()
 {
-    nsresult rv = XMLDocument::Init();
+    nsresult rv = nsHTMLDocument::Init();
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (gRefCnt++ == 0) {
         // ensure that the XUL prototype cache is instantiated successfully,
         // so that we can use nsXULPrototypeCache::GetInstance() without
         // null-checks in the rest of the class.
         nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
         if (!cache) {
@@ -884,16 +885,17 @@ XULDocument::ResumeWalk()
             nsXULPrototypeElement* proto;
             nsCOMPtr<nsIContent> element;
             int32_t indx; // all children of proto before indx (not
                           // inclusive) have already been constructed
             rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
             if (NS_FAILED(rv)) return rv;
 
             if (indx >= (int32_t)proto->mChildren.Length()) {
+
                 if (element) {
                     // We've processed all of the prototype's children. If
                     // we're in the master prototype, do post-order
                     // document-level hookup.
                     AddElementToDocumentPost(element->AsElement());
 
                     if (element->NodeInfo()->Equals(nsGkAtoms::style,
                                                     kNameSpaceID_XHTML) ||
@@ -1135,17 +1137,17 @@ XULDocument::StyleSheetLoaded(StyleSheet
     }
 
     return NS_OK;
 }
 
 void
 XULDocument::EndUpdate()
 {
-    XMLDocument::EndUpdate();
+    nsHTMLDocument::EndUpdate();
 }
 
 nsresult
 XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
 {
     // Load a transcluded script
     nsresult rv;
 
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -7,16 +7,17 @@
 #define mozilla_dom_XULDocument_h
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsXULPrototypeDocument.h"
 #include "nsTArray.h"
 
 #include "mozilla/dom/XMLDocument.h"
+#include "nsHTMLDocument.h"
 #include "mozilla/StyleSheet.h"
 #include "nsIContent.h"
 #include "nsCOMArray.h"
 #include "nsIURI.h"
 #include "nsIStreamListener.h"
 #include "nsIStreamLoader.h"
 #include "nsICSSLoaderObserver.h"
 
@@ -44,17 +45,17 @@ class nsIObjectOutputStream;
  */
 
 // Factory function.
 nsresult NS_NewXULDocument(nsIDocument** result);
 
 namespace mozilla {
 namespace dom {
 
-class XULDocument final : public XMLDocument,
+class XULDocument final : public nsHTMLDocument,
                           public nsIStreamLoaderObserver,
                           public nsICSSLoaderObserver,
                           public nsIOffThreadScriptReceiver
 {
 public:
     XULDocument();
 
     // nsISupports interface
@@ -113,17 +114,17 @@ public:
 
     /**
      * Reset the document direction so that it is recomputed.
      */
     void ResetDocumentDirection();
 
     NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override;
 
-    NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument)
+    NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, nsHTMLDocument)
 
     void TraceProtos(JSTracer* aTrc);
 
 protected:
     virtual ~XULDocument();
 
     // Implementation methods
     friend nsresult
--- a/dom/xul/nsXULPrototypeCache.cpp
+++ b/dom/xul/nsXULPrototypeCache.cpp
@@ -465,18 +465,20 @@ nsXULPrototypeCache::HasData(nsIURI* uri
 
 nsresult
 nsXULPrototypeCache::BeginCaching(nsIURI* aURI)
 {
     nsresult rv, tmp;
 
     nsAutoCString path;
     aURI->GetPathQueryRef(path);
-    if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul")))
+    if (!(StringEndsWith(path, NS_LITERAL_CSTRING(".xul")) ||
+          StringEndsWith(path, NS_LITERAL_CSTRING(".xhtml")))) {
         return NS_ERROR_NOT_AVAILABLE;
+    }
 
     StartupCache* startupCache = StartupCache::GetSingleton();
     if (!startupCache)
         return NS_ERROR_FAILURE;
 
     if (gDisableXULCache)
         return NS_ERROR_NOT_AVAILABLE;
 
--- a/layout/build/nsContentDLF.cpp
+++ b/layout/build/nsContentDLF.cpp
@@ -27,16 +27,17 @@
 #include "nsIViewSourceChannel.h"
 #include "nsContentUtils.h"
 #include "imgLoader.h"
 #include "nsCharsetSource.h"
 #include "nsMimeTypes.h"
 #include "DecoderTraits.h"
 #ifdef MOZ_XUL
 #include "XULDocument.h"
+#include "nsXULPrototypeCache.h"
 #endif
 
 
 // plugins
 #include "nsIPluginHost.h"
 #include "nsPluginHost.h"
 
 // Factory code for creating variations on html documents
@@ -170,16 +171,26 @@ nsContentDLF::CreateInstance(const char*
   } else if (aContentType.EqualsLiteral(VIEWSOURCE_CONTENT_TYPE)) {
     aChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
     contentType = TEXT_PLAIN;
   }
 
   // Try html or plaintext; both use the same document CID
   if (IsTypeInList(contentType, gHTMLTypes) ||
       nsContentUtils::IsPlainTextType(contentType)) {
+
+    nsCOMPtr<nsIURI> url;
+    aChannel->GetURI(getter_AddRefs(url));
+    if (contentType.EqualsLiteral("application/xhtml+xml") &&
+        MayUseXULXBL(aChannel) && !nsXULPrototypeCache::GetInstance()->IsCached(url)) {
+      printf(">>> Creating XUL document url=%s\n", url->GetSpecOrDefault().get());
+      return CreateXULDocument(aCommand, aChannel, aLoadGroup, aContainer,
+                               aExtraInfo, aDocListener, aDocViewer);
+    }
+    printf(">>> Creating plain document url=%s\n", url->GetSpecOrDefault().get());
     return CreateDocument(aCommand,
                           aChannel, aLoadGroup,
                           aContainer, [] () -> already_AddRefed<nsIDocument> {
                             nsCOMPtr<nsIDocument> doc;
                             nsresult rv = NS_NewHTMLDocument(getter_AddRefs(doc));
                             NS_ENSURE_SUCCESS(rv, nullptr);
                             return doc.forget();
                           },
new file mode 100644
--- /dev/null
+++ b/parser/cache/CacheParser.cpp
@@ -0,0 +1,579 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "CacheParser.h"
+#include "nsXULPrototypeCache.h"
+#include "nsXULElement.h"
+#include "nsIExpatSink.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "js/CompilationAndEvaluation.h"
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace parser {
+
+NS_INTERFACE_TABLE_HEAD(CacheParser)
+  NS_INTERFACE_TABLE(CacheParser,
+                     nsIParser, nsIStreamListener, nsIRequestObserver)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(CacheParser)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CacheParser)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CacheParser)
+
+NS_IMPL_CYCLE_COLLECTION(CacheParser)
+
+CacheParser::CacheParser()
+{
+}
+
+CacheParser::~CacheParser()
+{
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::SetContentSink(nsIContentSink* aSink)
+{
+  MOZ_ASSERT(aSink, "sink cannot be null!");
+  mSink = do_QueryInterface(aSink);;
+  mOriginalSink = aSink;
+
+  if (aSink) {
+    aSink->SetParser(this);
+  }
+}
+
+NS_IMETHODIMP_(nsIContentSink*)
+CacheParser::GetContentSink()
+{
+  return mOriginalSink;
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::GetCommand(nsCString& aCommand)
+{
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::SetCommand(const char* aCommand)
+{
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::SetCommand(eParserCommands aParserCommand)
+{
+}
+
+void
+CacheParser::SetDocumentCharset(NotNull<const Encoding*> aEncoding,
+                                int32_t aSource)
+{
+}
+
+NS_IMETHODIMP
+CacheParser::GetChannel(nsIChannel** aChannel)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CacheParser::GetDTD(nsIDTD** aDTD)
+{
+  return NS_OK;
+}
+
+nsIStreamListener*
+CacheParser::GetStreamListener()
+{
+  return this;
+}
+
+NS_IMETHODIMP
+CacheParser::ContinueInterruptedParsing()
+{
+  printf(">>> CacheParser::ContinueInterruptedParsing\n");
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::BlockParser()
+{
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::UnblockParser()
+{
+}
+
+NS_IMETHODIMP_(void)
+CacheParser::ContinueInterruptedParsingAsync()
+{
+  printf(">>> CacheParser::ContinueInterruptedParsingAsync\n");
+}
+
+NS_IMETHODIMP_(bool)
+CacheParser::IsParserEnabled()
+{
+  return true;
+}
+
+NS_IMETHODIMP_(bool)
+CacheParser::IsComplete()
+{
+  return false;
+}
+
+NS_IMETHODIMP
+CacheParser::Parse(nsIURI* aURL,
+                 nsIRequestObserver* aListener,
+                 void* aKey,
+                 nsDTDMode aMode)
+{
+  nsXULPrototypeDocument* proto = nsXULPrototypeCache::GetInstance()->GetPrototype(aURL);
+  mCurrentPrototype = proto;
+
+  return NS_OK;
+}
+
+nsresult
+CacheParser::Parse(const nsAString& aSourceBuffer,
+               void* aKey,
+               const nsACString& aContentType,
+               bool aLastCall,
+               nsDTDMode aMode)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CacheParser::Terminate()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CacheParser::ParseFragment(const nsAString& aSourceBuffer,
+                         nsTArray<nsString>& aTagStack)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CacheParser::BuildModel()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CacheParser::CancelParsingEvents()
+{
+  return NS_OK;
+}
+
+void
+CacheParser::Reset()
+{
+}
+
+bool
+CacheParser::IsInsertionPointDefined()
+{
+  // ??????
+  return true;
+}
+
+void
+CacheParser::PushDefinedInsertionPoint()
+{
+  // ??????
+}
+
+void
+CacheParser::PopDefinedInsertionPoint()
+{
+  // ??????
+}
+
+void
+CacheParser::MarkAsNotScriptCreated(const char* aCommand)
+{
+  // ??????
+}
+
+bool
+CacheParser::IsScriptCreated()
+{
+  return false;
+}
+
+nsresult
+CacheParser::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI)
+{
+  mSink->HandleProcessingInstruction(aProtoPI->mTarget.get(), aProtoPI->mData.get());
+  // !!! probably need to pause!
+  return NS_OK;
+}
+
+void
+CacheParser::PrepareToWalk()
+{
+  mOriginalSink->WillBuildModel(eDTDMode_full_standards);
+  // Get the prototype's root element and initialize the context
+  // stack for the prototype walk.
+
+  const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
+      mCurrentPrototype->GetProcessingInstructions();
+  uint32_t total = processingInstructions.Length();
+  for (uint32_t i = 0; i < total; ++i) {
+    CreateAndInsertPI(processingInstructions[i]);
+  }
+
+  nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
+  nsresult rv = HandleStartElement(proto);
+  mContextStack.Push(proto);
+  if (NS_FAILED(rv)) {
+    printf(">>> BAILING OUT 3\n");
+    return;
+  }
+
+  // !!!! TODO AUDIT xul documents prepare to walk and make sure we do everything.
+}
+
+ContextStack::ContextStack()
+    : mTop(nullptr), mDepth(0)
+{
+}
+
+ContextStack::~ContextStack()
+{
+  while (mTop) {
+    Entry* doomed = mTop;
+    mTop = mTop->mNext;
+    delete doomed;
+  }
+}
+
+nsresult
+ContextStack::Push(nsXULPrototypeElement* aPrototype)
+{
+  Entry* entry = new Entry;
+  entry->mPrototype = aPrototype;
+  entry->mIndex     = 0;
+
+  entry->mNext = mTop;
+  mTop = entry;
+
+  ++mDepth;
+  return NS_OK;
+}
+
+nsresult
+ContextStack::Pop()
+{
+  if (mDepth == 0) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  Entry* doomed = mTop;
+  mTop = mTop->mNext;
+  --mDepth;
+
+  delete doomed;
+  return NS_OK;
+}
+
+nsresult
+ContextStack::Peek(nsXULPrototypeElement** aPrototype,
+                                int32_t* aIndex)
+{
+  if (mDepth == 0) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  *aPrototype = mTop->mPrototype;
+  *aIndex     = mTop->mIndex;
+
+  return NS_OK;
+}
+
+
+nsresult
+ContextStack::SetTopIndex(int32_t aIndex)
+{
+  if (mDepth == 0) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mTop->mIndex = aIndex;
+  return NS_OK;
+}
+
+static int dumpDepth = 0;
+
+char16_t*
+ToExpatName(nsXULPrototypeElement* aPrototype)
+{
+  nsAutoString namespaceStr;
+  aPrototype->mNodeInfo->GetNamespaceURI(namespaceStr);
+
+  nsAutoString name2 = namespaceStr;
+
+  nsAutoString prefixStr;
+  aPrototype->mNodeInfo->GetPrefix(prefixStr);
+  if (!prefixStr.IsEmpty()) {
+    name2 += NS_LITERAL_STRING("\xFFFF") + prefixStr;
+  }
+  nsAutoString nameStr;
+  aPrototype->mNodeInfo->GetName(nameStr);
+  name2 += NS_LITERAL_STRING("\xFFFF") + nameStr;
+
+  return ToNewUnicode(name2);
+}
+
+char16_t*
+ToExpatName(nsXULPrototypeAttribute* aAttribute)
+{
+  nsAutoString name2;
+  nsAutoString namespaceStr;
+  if(!aAttribute->mName.IsAtom()) {
+    aAttribute->mName.NodeInfo()->GetNamespaceURI(namespaceStr);
+    if (!namespaceStr.IsEmpty()) {
+      name2 += namespaceStr + NS_LITERAL_STRING("\xFFFF");
+    }
+  }
+
+  nsAutoString nameStr;
+  aAttribute->mName.LocalName()->ToString(nameStr);
+  name2 += nameStr;
+
+  nsAtom* attrPrefix = aAttribute->mName.GetPrefix();
+  nsAutoString prefixStr;
+  if(attrPrefix) {
+    attrPrefix->ToString(prefixStr);
+    if (!prefixStr.IsEmpty()) {
+      name2 += NS_LITERAL_STRING("\xFFFF") + prefixStr;
+    }
+  }
+
+  return ToNewUnicode(name2);
+}
+
+nsresult
+CacheParser::HandleStartElement(nsXULPrototypeElement* aPrototype)
+{
+  uint32_t attrArrayLength = aPrototype->mNumAttributes * 2;
+  const char16_t** attributes = new const char16_t*[attrArrayLength + 1];
+  attributes[attrArrayLength] = nullptr;
+
+  uint32_t outIndex = 0;
+  for (uint32_t i = 0; i < aPrototype->mNumAttributes; i++) {
+    nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
+    nsAutoString valueStr;
+    protoattr->mValue.ToString(valueStr);
+    // !!! MAYBE WE SHOULD CHANGE THE CONTENT SINK SO WE CAN JUST PASS IN A NODE INFO
+    attributes[outIndex++] = ToExpatName(protoattr);
+    attributes[outIndex++] = ToNewUnicode(valueStr);
+  }
+  const char16_t* name = ToExpatName(aPrototype);
+  nsresult rv = mSink->
+    HandleStartElement(name, attributes, attrArrayLength,
+                       0,
+                       0);
+  // !!! do i need to free the char buffers from above
+  delete[] attributes;
+  return rv;
+}
+
+nsresult
+CacheParser::HandleEndElement(nsXULPrototypeElement* aPrototype)
+{
+  return mSink->HandleEndElement(ToExpatName(aPrototype));
+}
+
+nsresult
+CacheParser::ExecuteScript(nsXULPrototypeScript *aScript)
+{
+  MOZ_ASSERT(aScript != nullptr, "null ptr");
+  NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
+
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(mOriginalSink->GetTarget());
+  MOZ_ASSERT(doc, "Must have document.\n");
+  nsIScriptGlobalObject* scriptGlobalObject;
+  bool aHasHadScriptHandlingObject;
+  scriptGlobalObject = doc->GetScriptHandlingObject(aHasHadScriptHandlingObject);
+
+
+  NS_ENSURE_TRUE(scriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv;
+  rv = scriptGlobalObject->EnsureScriptEnvironment();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Execute the precompiled script with the given version
+  nsAutoMicroTask mt;
+
+  // We're about to run script via JS::CloneAndExecuteScript, so we need an
+  // AutoEntryScript. This is Gecko specific and not in any spec.
+  AutoEntryScript aes(scriptGlobalObject, "precompiled XUL <script> element");
+  JSContext* cx = aes.cx();
+
+  JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
+  NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
+
+  JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+  NS_ENSURE_TRUE(xpc::Scriptability::Get(global).Allowed(), NS_OK);
+
+  JS::ExposeObjectToActiveJS(global);
+  JSAutoRealm ar(cx, global);
+
+  // The script is in the compilation scope. Clone it into the target scope
+  // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
+  // there is no need to manually check the return value.
+  JS::RootedValue rval(cx);
+  JS::CloneAndExecuteScript(cx, scriptObject, &rval);
+
+  return NS_OK;
+}
+
+void
+CacheParser::ResumeWalk()
+{
+  mOriginalSink->WillParse();
+  mOriginalSink->WillResume();
+  nsresult rv;
+  while (1) {
+    // Begin (or resume) walking the current prototype.
+    while (mContextStack.Depth() > 0) {
+      // Look at the top of the stack to determine what we're
+      // currently working on.
+      // This will always be a node already constructed and
+      // inserted to the actual document.
+      nsXULPrototypeElement* proto;
+      int32_t indx; // all children of proto before indx (not
+                    // inclusive) have already been constructed
+      mContextStack.Peek(&proto, &indx);
+      if (indx >= (int32_t)proto->mChildren.Length()) {
+        rv = HandleEndElement(proto);
+        // Now pop the context stack back up to the parent
+        // element and continue the prototype walk.
+        mContextStack.Pop();
+        if (NS_FAILED(rv)) {
+          printf(">>> BAILING OUT 1\n");
+          return;
+        }
+        continue;
+      }
+
+      // Grab the next child, and advance the current context stack
+      // to the next sibling to our right.
+      nsXULPrototypeNode* childproto = proto->mChildren[indx];
+      mContextStack.SetTopIndex(++indx);
+      switch (childproto->mType) {
+        case nsXULPrototypeNode::eType_Element: {
+          // An 'element', which may contain more content.
+          nsXULPrototypeElement* protoele =
+            static_cast<nsXULPrototypeElement*>(childproto);
+            rv = HandleStartElement(protoele);
+            // If it has children, push the element onto the context
+            // stack and begin to process them.
+            if (protoele->mChildren.Length() > 0) {
+              mContextStack.Push(protoele);
+              if (NS_FAILED(rv)) {
+                printf("!!!!!!!!!!!!!!!!!! HANDLE THIS\n");
+              }
+            } else {
+              // If there are no children, do post-order document hookup
+              // immediately.
+              rv = HandleEndElement(protoele);
+              if (NS_FAILED(rv)) {
+                printf(">>> BAILING OUT 2\n");
+                return;
+              }
+            }
+        }
+        break;
+
+        case nsXULPrototypeNode::eType_Script: {
+          // A script reference. Execute the script immediately;
+          // this may have side effects in the content model.
+          nsXULPrototypeScript* scriptproto =
+              static_cast<nsXULPrototypeScript*>(childproto);
+
+          if (scriptproto->mSrcURI) {
+            printf("!!!!!!!!!!!!!!!!!!!!!! HANDLE THIS TOO\n");
+          } else if (scriptproto->HasScriptObject()) {
+            // An inline script
+            rv = ExecuteScript(scriptproto);
+            if (NS_FAILED(rv)) {
+              printf("!!!!!!!!!!!!!!!!!!!! HANDLE THIS THREE\n");
+              return;
+            }
+          }
+        }
+        break;
+
+        case nsXULPrototypeNode::eType_Text: {
+          // A simple text node.
+          nsXULPrototypeText* textproto =
+              static_cast<nsXULPrototypeText*>(childproto);
+          nsresult rv = mSink->HandleCharacterData(textproto->mValue.get(), textproto->mValue.Length());
+          if (NS_FAILED(rv)) {
+            printf("!!!!!!!!!!!!!!!!!!!! HANDLE THIS FOUR\n");
+            return;
+          }
+        }
+        break;
+
+        case nsXULPrototypeNode::eType_PI: {
+          // !!! i don't think we need to support this
+        }
+        break;
+
+        default:
+          MOZ_ASSERT_UNREACHABLE("Unexpected nsXULPrototypeNode::Type");
+      }
+    }
+    // Once we get here, the context stack will have been
+    // depleted. That means that the entire prototype has been
+    // walked and content has been constructed.
+    break;
+  }
+  mOriginalSink->DidBuildModel(false);
+}
+
+NS_IMETHODIMP
+CacheParser::OnStartRequest(nsIRequest *request,
+                                                        nsISupports* acontext)
+{
+  return NS_ERROR_PARSED_DATA_CACHED;
+}
+
+
+NS_IMETHODIMP
+CacheParser::OnStopRequest(nsIRequest *request,
+                                                       nsISupports* aContext,
+                                                       nsresult aStatus)
+{
+  PrepareToWalk();
+  ResumeWalk();
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+CacheParser::OnDataAvailable(nsIRequest *request,
+                                                         nsISupports* aContext,
+                                                         nsIInputStream* aInStr,
+                                                         uint64_t aSourceOffset,
+                                                         uint32_t aCount)
+{
+  MOZ_ASSERT_UNREACHABLE("CachedChromeStream doesn't receive data");
+  return NS_ERROR_UNEXPECTED;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/parser/cache/CacheParser.h
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_parser_CacheParser_h
+#define mozilla_parser_CacheParser_h
+
+#include "nsIParser.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIContentSink.h"
+
+class nsIExpatSink;
+class nsXULPrototypeDocument;
+class nsXULPrototypeElement;
+class nsXULPrototypePI;
+class nsXULPrototypeScript;
+
+namespace mozilla {
+namespace parser {
+
+class ContextStack {
+    protected:
+        struct Entry {
+            nsXULPrototypeElement* mPrototype;
+            int32_t                mIndex;
+            Entry*                 mNext;
+        };
+
+        Entry* mTop;
+        int32_t mDepth;
+
+    public:
+        ContextStack();
+        ~ContextStack();
+
+        int32_t Depth() { return mDepth; }
+
+        nsresult Push(nsXULPrototypeElement* aPrototype);
+        nsresult Pop();
+        nsresult Peek(nsXULPrototypeElement** aPrototype, int32_t* aIndex);
+
+        nsresult SetTopIndex(int32_t aIndex);
+};
+
+class CacheParser final
+  : public nsIParser,
+    public nsIStreamListener
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CacheParser, nsIParser)
+
+  CacheParser();
+
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+
+  /* Start nsIParser */
+  /**
+   * No-op for backwards compat.
+   */
+  NS_IMETHOD_(void) SetContentSink(nsIContentSink* aSink) override;
+
+  /**
+   * Returns the tree op executor for backwards compat.
+   */
+  NS_IMETHOD_(nsIContentSink*) GetContentSink() override;
+
+  /**
+   * Always returns "view" for backwards compat.
+   */
+  NS_IMETHOD_(void) GetCommand(nsCString& aCommand) override;
+
+  /**
+   * No-op for backwards compat.
+   */
+  NS_IMETHOD_(void) SetCommand(const char* aCommand) override;
+
+  /**
+   * No-op for backwards compat.
+   */
+  NS_IMETHOD_(void) SetCommand(eParserCommands aParserCommand) override;
+
+  /**
+   *  Call this method once you've created a parser, and want to instruct it
+   *  about what charset to load
+   *
+   *  @param   aEncoding the charset of a document
+   *  @param   aCharsetSource the source of the charset
+   */
+  virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding,
+                                  int32_t aSource) override;
+
+  /**
+   * Get the channel associated with this parser
+   * @param aChannel out param that will contain the result
+   * @return NS_OK if successful or NS_NOT_AVAILABLE if not
+   */
+  NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
+
+  /**
+   * Return |this| for backwards compat.
+   */
+  NS_IMETHOD GetDTD(nsIDTD** aDTD) override;
+
+  /**
+   * Get the stream parser for this parser
+   */
+  virtual nsIStreamListener* GetStreamListener() override;
+
+  /**
+   * Don't call. For interface compat only.
+   */
+  NS_IMETHOD ContinueInterruptedParsing() override;
+
+  /**
+   * Blocks the parser.
+   */
+  NS_IMETHOD_(void) BlockParser() override;
+
+  /**
+   * Unblocks the parser.
+   */
+  NS_IMETHOD_(void) UnblockParser() override;
+
+  /**
+   * Asynchronously continues parsing.
+   */
+  NS_IMETHOD_(void) ContinueInterruptedParsingAsync() override;
+
+  /**
+   * Query whether the parser is enabled (i.e. not blocked) or not.
+   */
+  NS_IMETHOD_(bool) IsParserEnabled() override;
+
+  /**
+   * Query whether the parser thinks it's done with parsing.
+   */
+  NS_IMETHOD_(bool) IsComplete() override;
+
+  /**
+   * Set up request observer.
+   *
+   * @param   aURL used for View Source title
+   * @param   aListener a listener to forward notifications to
+   * @param   aKey the root context key (used for document.write)
+   * @param   aMode ignored (for interface compat only)
+   */
+  NS_IMETHOD Parse(nsIURI* aURL,
+                   nsIRequestObserver* aListener = nullptr,
+                   void* aKey = 0,
+                   nsDTDMode aMode = eDTDMode_autodetect) override;
+
+  /**
+   * document.write and document.close
+   *
+   * @param   aSourceBuffer the argument of document.write (empty for .close())
+   * @param   aKey a key unique to the script element that caused this call
+   * @param   aContentType "text/html" for HTML mode, else text/plain mode
+   * @param   aLastCall true if .close() false if .write()
+   * @param   aMode ignored (for interface compat only)
+   */
+  nsresult Parse(const nsAString& aSourceBuffer,
+                 void* aKey,
+                 const nsACString& aContentType,
+                 bool aLastCall,
+                 nsDTDMode aMode = eDTDMode_autodetect);
+
+  /**
+   * Stops the parser prematurely
+   */
+  NS_IMETHOD Terminate() override;
+
+  /**
+   * Don't call. For interface backwards compat only.
+   */
+  NS_IMETHOD ParseFragment(const nsAString& aSourceBuffer,
+                           nsTArray<nsString>& aTagStack) override;
+
+  /**
+   * Don't call. For interface compat only.
+   */
+  NS_IMETHOD BuildModel() override;
+
+  /**
+   * Don't call. For interface compat only.
+   */
+  NS_IMETHOD CancelParsingEvents() override;
+
+  /**
+   * Don't call. For interface compat only.
+   */
+  virtual void Reset() override;
+
+  /**
+   * True if the insertion point (per HTML5) is defined.
+   */
+  virtual bool IsInsertionPointDefined() override;
+
+  /**
+   * Call immediately before starting to evaluate a parser-inserted script or
+   * in general when the spec says to define an insertion point.
+   */
+  virtual void PushDefinedInsertionPoint() override;
+
+  /**
+   * Call immediately after having evaluated a parser-inserted script or
+   * generally want to restore to the state before the last
+   * PushDefinedInsertionPoint call.
+   */
+  virtual void PopDefinedInsertionPoint() override;
+
+  /**
+   * Marks the HTML5 parser as not a script-created parser: Prepares the
+   * parser to be able to read a stream.
+   *
+   * @param aCommand the parser command (Yeah, this is bad API design. Let's
+   * make this better when retiring nsIParser)
+   */
+  virtual void MarkAsNotScriptCreated(const char* aCommand) override;
+
+  /**
+   * True if this is a script-created HTML5 parser.
+   */
+  virtual bool IsScriptCreated() override;
+
+  /* End nsIParser  */
+
+private:
+  virtual ~CacheParser();
+
+protected:
+  nsresult HandleStartElement(nsXULPrototypeElement* aProto);
+  nsresult HandleEndElement(nsXULPrototypeElement* aProto);
+  nsresult CreateAndInsertPI(const nsXULPrototypePI* aProtoPI);
+  void PrepareToWalk();
+  void ResumeWalk();
+  nsresult ExecuteScript(nsXULPrototypeScript* aScript);
+
+protected:
+  nsCOMPtr<nsIExpatSink> mSink;
+  // !!! proably don't need this
+  nsCOMPtr<nsIContentSink> mOriginalSink;
+  ContextStack mContextStack;
+  /**
+   * The current prototype that we are walking to construct the
+   * content model.
+   */
+  RefPtr<nsXULPrototypeDocument> mCurrentPrototype;
+};
+
+} // namespace parser
+} // namespace mozilla
+
+
+#endif // mozilla_parser_CacheParser_h
\ No newline at end of file
copy from parser/moz.build
copy to parser/cache/moz.build
--- a/parser/moz.build
+++ b/parser/cache/moz.build
@@ -1,15 +1,24 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
-    BUG_COMPONENT = ('Core', 'HTML: Parser')
+    BUG_COMPONENT = ('Core', 'XML')
 
-DIRS += ['expat', 'xml', 'htmlparser', 'html']
-
-EXPORTS += [
-    'nsCharsetSource.h',
+EXPORTS.mozilla.parser += [
+    'CacheParser.h',
 ]
 
+
+UNIFIED_SOURCES += [
+    'CacheParser.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+    '/dom/xbl',
+    '/dom/xul',
+]
--- a/parser/moz.build
+++ b/parser/moz.build
@@ -2,14 +2,14 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'HTML: Parser')
 
-DIRS += ['expat', 'xml', 'htmlparser', 'html']
+DIRS += ['expat', 'cache', 'xml', 'htmlparser', 'html']
 
 EXPORTS += [
     'nsCharsetSource.h',
 ]
 
--- a/toolkit/content/editMenuCommands.inc.xul
+++ b/toolkit/content/editMenuCommands.inc.xul
@@ -1,13 +1,50 @@
-<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"
+<script type="application/javascript"
 #ifdef BROWSER_XHTML
 xmlns="http://www.w3.org/1999/xhtml"
 #endif
-/>
+><![CDATA[
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// update menu items that rely on focus or on the current selection
+function goUpdateGlobalEditMenuItems() {
+  // Don't bother updating the edit commands if they aren't visible in any way
+  // (i.e. the Edit menu isn't open, nor is the context menu open, nor have the
+  // cut, copy, and paste buttons been added to the toolbars) for performance.
+  // This only works in applications/on platforms that set the gEditUIVisible
+  // flag, so we check to see if that flag is defined before using it.
+  if (typeof gEditUIVisible != "undefined" && !gEditUIVisible)
+    return;
+
+  goUpdateCommand("cmd_undo");
+  goUpdateCommand("cmd_redo");
+  goUpdateCommand("cmd_cut");
+  goUpdateCommand("cmd_copy");
+  goUpdateCommand("cmd_paste");
+  goUpdateCommand("cmd_selectAll");
+  goUpdateCommand("cmd_delete");
+  goUpdateCommand("cmd_switchTextDirection");
+}
+
+// update menu items that relate to undo/redo
+function goUpdateUndoEditMenuItems() {
+  goUpdateCommand("cmd_undo");
+  goUpdateCommand("cmd_redo");
+}
+
+// update menu items that depend on clipboard contents
+function goUpdatePasteMenuItems() {
+  goUpdateCommand("cmd_paste");
+}
+]]></script>
 <commandset id="editMenuCommands">
   <commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
               oncommandupdate="goUpdateGlobalEditMenuItems()"/>
   <commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
               oncommandupdate="goUpdateUndoEditMenuItems()"/>
   <commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
               oncommandupdate="goUpdatePasteMenuItems()"/>
   <command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>