Implement <iframe srcdoc>
https://bugs.webkit.org/show_bug.cgi?id=82991

Reviewed by Sam Weinig.

Source/WebCore: 

This patch implements the <iframe srcdoc> feature. This feature allows
authors to easily supply the contents of an iframe without needing to
round-trip to the server. Using srcdoc is more convenient than using
data URLs because we set a bunch of properties of the child document
sensibly. For example, the child inherits the base URL of the parent
and the child uses standards mode by default.

This feature is specified in
<http://www.whatwg.org/specs/web-apps/current-work/#attr-iframe-srcdoc>.
Although the feature has been in the spec for a while, I'm not aware of
any other implementations in major browsers. The srcdoc feature works
especially well with the sandbox and seamless attributes. We already
implement sandbox and will likely implement seamless soon.

The srcdoc feature was announced on the webkit-dev mailing list on March 30:
https://lists.webkit.org/pipermail/webkit-dev/2012-March/020161.html

This patch approaches the implementation using SubstituteData, which is
a mechanism previously used for error messages and the like. Using
SubstituteData has the advantage of not needing to modify the loading
or history pipelines at all, making the integration of srcdoc with the
rest of WebCore quite smooth.

This patch encodes the contents of the srcdoc attribute to and from
UTF-8 when round-tripping the contents through the loader. In a future
patch, I plan to experiment with whether using UTF-16 (or perhaps
another encoding) can improve performance. There might also be a way to
avoid the memcpy entirely, but these optimizations are best left to
followup patches as this patch focuses on the observable behavior of
the feature.

Tests: fast/frames/srcdoc/reloading-a-srcdoc-document-loads-it-again.html
       fast/frames/srcdoc/setting-src-does-nothing.html
       fast/frames/srcdoc/setting-srcdoc-reloads-document.html
       fast/frames/srcdoc/srcdoc-beats-src-dom.html
       fast/frames/srcdoc/srcdoc-beats-src.html
       fast/frames/srcdoc/srcdoc-can-be-in-qurks-mode.html
       fast/frames/srcdoc/srcdoc-can-navigate.html
       fast/frames/srcdoc/srcdoc-defaults-to-standards-mode.html
       fast/frames/srcdoc/srcdoc-loads-content.html
       fast/frames/srcdoc/srcdoc-urls.html
       http/tests/security/srcdoc-can-access-parent.html
       http/tests/security/srcdoc-in-sandbox-cannot-access-parent.html
       http/tests/security/srcdoc-inherits-referrer-for-forms.html
       http/tests/security/srcdoc-inherits-referrer.html

* dom/Document.cpp:
(WebCore::Document::Document):
(WebCore::Document::initSecurityContext):
    - srcdoc documents need to inherit their security contexts from
      their parents. We pick this initialization point to inherit the
      base URL and to set the "srcdoc Document" bit so that everything
      gets initialized atomically and from precisely the same owner
      frame.
* dom/Document.h:
(Document):
(WebCore::Document::isSrcdocDocument):
    - This bit of state is present in the HTML5 spec and is referred to
      by a number of different requirements in the spec.
* html/HTMLAttributeNames.in:
* html/HTMLFrameElementBase.cpp:
(WebCore::HTMLFrameElementBase::parseAttribute):
(WebCore::HTMLFrameElementBase::location):
    - These functions implement the requirement that the srcdoc
      attribute takes precedence over the src attribute and triggers a
      load of a document with the URL about:srcdoc.
* html/HTMLIFrameElement.idl:
    - Expose the srcdoc property as a reflection of the DOM attribute.
* html/parser/HTMLTreeBuilder.cpp:
(WebCore::HTMLTreeBuilder::defaultForInitial):
    - This tweak allows srcdoc documents to use standards mode by
      default, saving authors from having to include a doctype in each
      srcdoc document.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::defaultSubstituteDataForURL):
    - This function transfers the contents of the srcdoc attribute into
      the loading pipeline. If the client supplies other
      SubstituteData, that takes precendence over our "default"
      SubstituteData.
(WebCore::FrameLoader::outgoingReferrer):
    - This function implements the requirement from the HTML5 spec that
      srcdoc documents inherit their referrer from their parent
      document. A recursive implementation might have been more
      aesthetic but the iterative implementation seemed like a better
      choice.
(WebCore::FrameLoader::shouldTreatURLAsSrcdocDocument):
    - An about:srcdoc URL only has special meaning when loaded in an
      iframe with a srcdoc attribute. Otherwise, it's just a normal
      "about" URL, meaning a blank page.
(WebCore::FrameLoader::urlSelected):
(WebCore::FrameLoader::submitForm):
(WebCore::FrameLoader::loadFrameRequest):
(WebCore::FrameLoader::load):
(WebCore::FrameLoader::loadWithNavigationAction):
(WebCore::FrameLoader::reloadWithOverrideEncoding):
(WebCore::FrameLoader::reload):
(WebCore::FrameLoader::loadResourceSynchronously):
    - Update these call sites to call defaultSubstituteDataForURL and
      outgoingReferrer consistently.
* loader/FrameLoader.h:
(FrameLoader):

LayoutTests: 

Add a number of tests for <iframe srcdoc>. These tests cover all the
requirements I could find in the HTML5 specification by grepping for
the term "srcdoc".

* fast/frames/srcdoc/reloading-a-srcdoc-document-loads-it-again-expected.txt: Added.
* fast/frames/srcdoc/reloading-a-srcdoc-document-loads-it-again.html: Added.
* fast/frames/srcdoc/setting-src-does-nothing-expected.txt: Added.
* fast/frames/srcdoc/setting-src-does-nothing.html: Added.
* fast/frames/srcdoc/setting-srcdoc-reloads-document-expected.txt: Added.
* fast/frames/srcdoc/setting-srcdoc-reloads-document.html: Added.
* fast/frames/srcdoc/srcdoc-beats-src-dom-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-beats-src-dom.html: Added.
* fast/frames/srcdoc/srcdoc-beats-src-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-beats-src.html: Added.
* fast/frames/srcdoc/srcdoc-can-be-in-qurks-mode-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-can-be-in-qurks-mode.html: Added.
* fast/frames/srcdoc/srcdoc-can-navigate-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-can-navigate.html: Added.
* fast/frames/srcdoc/srcdoc-defaults-to-standards-mode-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-defaults-to-standards-mode.html: Added.
* fast/frames/srcdoc/srcdoc-loads-content-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-loads-content.html: Added.
* fast/frames/srcdoc/srcdoc-urls-expected.txt: Added.
* fast/frames/srcdoc/srcdoc-urls.html: Added.
* http/tests/security/srcdoc-can-access-parent-expected.txt: Added.
* http/tests/security/srcdoc-can-access-parent.html: Added.
* http/tests/security/srcdoc-in-sandbox-cannot-access-parent-expected.txt: Added.
* http/tests/security/srcdoc-in-sandbox-cannot-access-parent.html: Added.
* http/tests/security/srcdoc-inherits-referrer-expected.txt: Added.
* http/tests/security/srcdoc-inherits-referrer-for-forms-expected.txt: Added.
* http/tests/security/srcdoc-inherits-referrer-for-forms.html: Added.
* http/tests/security/srcdoc-inherits-referrer.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@113143 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 30c12b9..9d60d56 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,112 @@
+2012-04-03  Adam Barth  <abarth@webkit.org>
+
+        Implement <iframe srcdoc>
+        https://bugs.webkit.org/show_bug.cgi?id=82991
+
+        Reviewed by Sam Weinig.
+
+        This patch implements the <iframe srcdoc> feature. This feature allows
+        authors to easily supply the contents of an iframe without needing to
+        round-trip to the server. Using srcdoc is more convenient than using
+        data URLs because we set a bunch of properties of the child document
+        sensibly. For example, the child inherits the base URL of the parent
+        and the child uses standards mode by default.
+
+        This feature is specified in
+        <http://www.whatwg.org/specs/web-apps/current-work/#attr-iframe-srcdoc>.
+        Although the feature has been in the spec for a while, I'm not aware of
+        any other implementations in major browsers. The srcdoc feature works
+        especially well with the sandbox and seamless attributes. We already
+        implement sandbox and will likely implement seamless soon.
+
+        The srcdoc feature was announced on the webkit-dev mailing list on March 30:
+        https://lists.webkit.org/pipermail/webkit-dev/2012-March/020161.html
+
+        This patch approaches the implementation using SubstituteData, which is
+        a mechanism previously used for error messages and the like. Using
+        SubstituteData has the advantage of not needing to modify the loading
+        or history pipelines at all, making the integration of srcdoc with the
+        rest of WebCore quite smooth.
+
+        This patch encodes the contents of the srcdoc attribute to and from
+        UTF-8 when round-tripping the contents through the loader. In a future
+        patch, I plan to experiment with whether using UTF-16 (or perhaps
+        another encoding) can improve performance. There might also be a way to
+        avoid the memcpy entirely, but these optimizations are best left to
+        followup patches as this patch focuses on the observable behavior of
+        the feature.
+
+        Tests: fast/frames/srcdoc/reloading-a-srcdoc-document-loads-it-again.html
+               fast/frames/srcdoc/setting-src-does-nothing.html
+               fast/frames/srcdoc/setting-srcdoc-reloads-document.html
+               fast/frames/srcdoc/srcdoc-beats-src-dom.html
+               fast/frames/srcdoc/srcdoc-beats-src.html
+               fast/frames/srcdoc/srcdoc-can-be-in-qurks-mode.html
+               fast/frames/srcdoc/srcdoc-can-navigate.html
+               fast/frames/srcdoc/srcdoc-defaults-to-standards-mode.html
+               fast/frames/srcdoc/srcdoc-loads-content.html
+               fast/frames/srcdoc/srcdoc-urls.html
+               http/tests/security/srcdoc-can-access-parent.html
+               http/tests/security/srcdoc-in-sandbox-cannot-access-parent.html
+               http/tests/security/srcdoc-inherits-referrer-for-forms.html
+               http/tests/security/srcdoc-inherits-referrer.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        (WebCore::Document::initSecurityContext):
+            - srcdoc documents need to inherit their security contexts from
+              their parents. We pick this initialization point to inherit the
+              base URL and to set the "srcdoc Document" bit so that everything
+              gets initialized atomically and from precisely the same owner
+              frame.
+        * dom/Document.h:
+        (Document):
+        (WebCore::Document::isSrcdocDocument):
+            - This bit of state is present in the HTML5 spec and is referred to
+              by a number of different requirements in the spec.
+        * html/HTMLAttributeNames.in:
+        * html/HTMLFrameElementBase.cpp:
+        (WebCore::HTMLFrameElementBase::parseAttribute):
+        (WebCore::HTMLFrameElementBase::location):
+            - These functions implement the requirement that the srcdoc
+              attribute takes precedence over the src attribute and triggers a
+              load of a document with the URL about:srcdoc.
+        * html/HTMLIFrameElement.idl:
+            - Expose the srcdoc property as a reflection of the DOM attribute.
+        * html/parser/HTMLTreeBuilder.cpp:
+        (WebCore::HTMLTreeBuilder::defaultForInitial):
+            - This tweak allows srcdoc documents to use standards mode by
+              default, saving authors from having to include a doctype in each
+              srcdoc document.
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::defaultSubstituteDataForURL):
+            - This function transfers the contents of the srcdoc attribute into
+              the loading pipeline. If the client supplies other
+              SubstituteData, that takes precendence over our "default"
+              SubstituteData.
+        (WebCore::FrameLoader::outgoingReferrer):
+            - This function implements the requirement from the HTML5 spec that
+              srcdoc documents inherit their referrer from their parent
+              document. A recursive implementation might have been more
+              aesthetic but the iterative implementation seemed like a better
+              choice.
+        (WebCore::FrameLoader::shouldTreatURLAsSrcdocDocument):
+            - An about:srcdoc URL only has special meaning when loaded in an
+              iframe with a srcdoc attribute. Otherwise, it's just a normal
+              "about" URL, meaning a blank page.
+        (WebCore::FrameLoader::urlSelected):
+        (WebCore::FrameLoader::submitForm):
+        (WebCore::FrameLoader::loadFrameRequest):
+        (WebCore::FrameLoader::load):
+        (WebCore::FrameLoader::loadWithNavigationAction):
+        (WebCore::FrameLoader::reloadWithOverrideEncoding):
+        (WebCore::FrameLoader::reload):
+        (WebCore::FrameLoader::loadResourceSynchronously):
+            - Update these call sites to call defaultSubstituteDataForURL and
+              outgoingReferrer consistently.
+        * loader/FrameLoader.h:
+        (FrameLoader):
+
 2012-03-29  Geoffrey Garen  <ggaren@apple.com>
 
         First step toward incremental Weak<T> finalization
diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp
index 67ed9231..efba134a 100644
--- a/Source/WebCore/dom/Document.cpp
+++ b/Source/WebCore/dom/Document.cpp
@@ -458,6 +458,7 @@
     , m_isHTML(isHTML)
     , m_isViewSource(false)
     , m_sawElementsInKnownNamespaces(false)
+    , m_isSrcdocDocument(false)
     , m_eventQueue(DocumentEventQueue::create(this))
     , m_weakReference(DocumentWeakReference::create(this))
     , m_idAttributeName(idAttr)
@@ -4831,6 +4832,21 @@
         return;
     }
 
+    if (m_frame->loader()->shouldTreatURLAsSrcdocDocument(url())) {
+        m_isSrcdocDocument = true;
+        setBaseURLOverride(ownerFrame->document()->baseURL());
+    }
+
+    if (isSandboxed(SandboxOrigin)) {
+        // If we're supposed to inherit our security origin from our owner,
+        // but we're also sandboxed, the only thing we inherit is the ability
+        // to load local resources. This lets about:blank iframes in file://
+        // URL documents load images and other resources from the file system.
+        if (ownerFrame->document()->securityOrigin()->canLoadLocalResources())
+            securityOrigin()->grantLoadLocalResources();
+        return;
+    }
+
     m_cookieURL = ownerFrame->document()->cookieURL();
     // We alias the SecurityOrigins to match Firefox, see Bug 15313
     // https://bugs.webkit.org/show_bug.cgi?id=15313
diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h
index d255e97..5ca373f 100644
--- a/Source/WebCore/dom/Document.h
+++ b/Source/WebCore/dom/Document.h
@@ -442,7 +442,9 @@
     virtual bool isPluginDocument() const { return false; }
     virtual bool isMediaDocument() const { return false; }
     virtual bool isFrameSet() const { return false; }
-    
+
+    bool isSrcdocDocument() const { return m_isSrcdocDocument; }
+
     PassRefPtr<CSSValuePool> cssValuePool() const;
     
     CSSStyleSelector* styleSelectorIfExists() const { return m_styleSelector.get(); }
@@ -1426,6 +1428,7 @@
 
     bool m_isViewSource;
     bool m_sawElementsInKnownNamespaces;
+    bool m_isSrcdocDocument;
 
     RefPtr<DocumentEventQueue> m_eventQueue;
 
diff --git a/Source/WebCore/html/HTMLAttributeNames.in b/Source/WebCore/html/HTMLAttributeNames.in
index 151df7f..3c730bb 100644
--- a/Source/WebCore/html/HTMLAttributeNames.in
+++ b/Source/WebCore/html/HTMLAttributeNames.in
@@ -291,6 +291,7 @@
 x-webkit-grammar
 spellcheck
 src
+srcdoc
 srclang
 standby
 start
diff --git a/Source/WebCore/html/HTMLFrameElementBase.cpp b/Source/WebCore/html/HTMLFrameElementBase.cpp
index f439a39..861c267 100644
--- a/Source/WebCore/html/HTMLFrameElementBase.cpp
+++ b/Source/WebCore/html/HTMLFrameElementBase.cpp
@@ -104,7 +104,9 @@
 
 void HTMLFrameElementBase::parseAttribute(Attribute* attr)
 {
-    if (attr->name() == srcAttr)
+    if (attr->name() == srcdocAttr)
+        setLocation("about:srcdoc");
+    else if (attr->name() == srcAttr && !fastHasAttribute(srcdocAttr))
         setLocation(stripLeadingAndTrailingHTMLSpaces(attr->value()));
     else if (isIdAttributeName(attr->name())) {
         // Important to call through to base for the id attribute so the hasID bit gets set.
@@ -182,6 +184,8 @@
 
 KURL HTMLFrameElementBase::location() const
 {
+    if (fastHasAttribute(srcdocAttr))
+        return KURL(ParsedURLString, "about:srcdoc");
     return document()->completeURL(getAttribute(srcAttr));
 }
 
diff --git a/Source/WebCore/html/HTMLIFrameElement.idl b/Source/WebCore/html/HTMLIFrameElement.idl
index 25f9cb1..809074c 100644
--- a/Source/WebCore/html/HTMLIFrameElement.idl
+++ b/Source/WebCore/html/HTMLIFrameElement.idl
@@ -31,6 +31,7 @@
         attribute [Reflect] DOMString sandbox;
         attribute [Reflect] DOMString scrolling;
         attribute [Reflect, URL] DOMString src;
+        attribute [Reflect] DOMString srcdoc;
         attribute [Reflect] DOMString width;
 
         // Introduced in DOM Level 2:
diff --git a/Source/WebCore/html/parser/HTMLTreeBuilder.cpp b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp
index 797e583..8062ebd 100644
--- a/Source/WebCore/html/parser/HTMLTreeBuilder.cpp
+++ b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp
@@ -2529,7 +2529,7 @@
 void HTMLTreeBuilder::defaultForInitial()
 {
     notImplemented();
-    if (!m_fragmentContext.fragment())
+    if (!m_fragmentContext.fragment() && !m_document->isSrcdocDocument())
         m_document->setCompatibilityMode(Document::QuirksMode);
     // FIXME: parse error
     setInsertionMode(BeforeHTMLMode);
diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp
index dc32c71..eea438a 100644
--- a/Source/WebCore/loader/FrameLoader.cpp
+++ b/Source/WebCore/loader/FrameLoader.cpp
@@ -279,7 +279,7 @@
     if (shouldSendReferrer == NeverSendReferrer)
         m_suppressOpenerInNewFrame = true;
     if (frameRequest.resourceRequest().httpReferrer().isEmpty())
-        frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
+        frameRequest.resourceRequest().setHTTPReferrer(outgoingReferrer());
     addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
 
     loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0, shouldSendReferrer);
@@ -345,7 +345,7 @@
     }
 
     submission->data()->generateFiles(m_frame->document());
-    submission->setReferrer(m_outgoingReferrer);
+    submission->setReferrer(outgoingReferrer());
     submission->setOrigin(outgoingOrigin());
 
     targetFrame->navigationScheduler()->scheduleFormSubmission(submission);
@@ -887,7 +887,16 @@
 
 String FrameLoader::outgoingReferrer() const
 {
-    return m_outgoingReferrer;
+    // See http://www.whatwg.org/specs/web-apps/current-work/#fetching-resources
+    // for why we walk the parent chain for srcdoc documents.
+    Frame* frame = m_frame;
+    while (frame->document()->isSrcdocDocument()) {
+        frame = frame->tree()->parent();
+        // Srcdoc documents cannot be top-level documents, by definition,
+        // because they need to be contained in iframes with the srcdoc.
+        ASSERT(frame);
+    }
+    return frame->loader()->m_outgoingReferrer;
 }
 
 String FrameLoader::outgoingOrigin() const
@@ -1151,7 +1160,7 @@
 
     String argsReferrer = request.resourceRequest().httpReferrer();
     if (argsReferrer.isEmpty())
-        argsReferrer = m_outgoingReferrer;
+        argsReferrer = outgoingReferrer();
 
     String referrer = SecurityPolicy::generateReferrerHeader(m_frame->document()->referrerPolicy(), url, argsReferrer);
     if (shouldSendReferrer == NeverSendReferrer)
@@ -1251,9 +1260,19 @@
     }
 }
 
+SubstituteData FrameLoader::defaultSubstituteDataForURL(const KURL& url)
+{
+    if (!shouldTreatURLAsSrcdocDocument(url))
+        return SubstituteData();
+    String srcdoc = m_frame->ownerElement()->fastGetAttribute(srcdocAttr);
+    ASSERT(!srcdoc.isNull());
+    CString encodedSrcdoc = srcdoc.utf8();
+    return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), "text/html", "UTF-8", KURL());
+}
+
 void FrameLoader::load(const ResourceRequest& request, bool lockHistory)
 {
-    load(request, SubstituteData(), lockHistory);
+    load(request, defaultSubstituteDataForURL(request.url()), lockHistory);
 }
 
 void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)
@@ -1287,7 +1306,7 @@
 
 void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState)
 {
-    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
+    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
     if (lockHistory && m_documentLoader)
         loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
 
@@ -1457,7 +1476,7 @@
 
     request.setCachePolicy(ReturnCacheDataElseLoad);
 
-    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
+    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
     setPolicyDocumentLoader(loader.get());
 
     loader->setOverrideEncoding(encoding);
@@ -1484,7 +1503,7 @@
     
     // Create a new document loader for the reload, this will become m_documentLoader eventually,
     // but first it has to be the "policy" document loader, and then the "provisional" document loader.
-    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
+    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url()));
 
     ResourceRequest& request = loader->request();
 
@@ -2550,7 +2569,7 @@
 unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
 {
     ASSERT(m_frame->document());
-    String referrer = SecurityPolicy::generateReferrerHeader(m_frame->document()->referrerPolicy(), request.url(), m_outgoingReferrer);
+    String referrer = SecurityPolicy::generateReferrerHeader(m_frame->document()->referrerPolicy(), request.url(), outgoingReferrer());
     
     ResourceRequest initialRequest = request;
     initialRequest.setTimeoutInterval(10);
@@ -2931,6 +2950,18 @@
     return url == history()->currentItem()->url() || url == history()->currentItem()->originalURL();
 }
 
+bool FrameLoader::shouldTreatURLAsSrcdocDocument(const KURL& url) const
+{
+    if (!equalIgnoringCase(url.string(), "about:srcdoc"))
+        return false;
+    HTMLFrameOwnerElement* ownerElement = m_frame->ownerElement();
+    if (!ownerElement)
+        return false;
+    if (!ownerElement->hasTagName(iframeTag))
+        return false;
+    return ownerElement->fastHasAttribute(srcdocAttr);
+}
+
 void FrameLoader::checkDidPerformFirstNavigation()
 {
     Page* page = m_frame->page();
diff --git a/Source/WebCore/loader/FrameLoader.h b/Source/WebCore/loader/FrameLoader.h
index 3f93104..474b1d8 100644
--- a/Source/WebCore/loader/FrameLoader.h
+++ b/Source/WebCore/loader/FrameLoader.h
@@ -168,6 +168,8 @@
     void didChangeTitle(DocumentLoader*);
     void didChangeIcons(IconType);
 
+    bool shouldTreatURLAsSrcdocDocument(const KURL&) const;
+
     FrameLoadType loadType() const;
 
     CachePolicy subresourceCachePolicy() const;
@@ -307,6 +309,8 @@
     void transitionToCommitted(PassRefPtr<CachedPage>);
     void frameLoadCompleted();
 
+    SubstituteData defaultSubstituteDataForURL(const KURL&);
+
     static void callContinueLoadAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue);
     static void callContinueLoadAfterNewWindowPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, bool shouldContinue);
     static void callContinueFragmentScrollAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue);
diff --git a/Source/WebCore/page/SecurityOrigin.cpp b/Source/WebCore/page/SecurityOrigin.cpp
index 3f2f093..f5a8f92 100644
--- a/Source/WebCore/page/SecurityOrigin.cpp
+++ b/Source/WebCore/page/SecurityOrigin.cpp
@@ -366,12 +366,15 @@
 
 void SecurityOrigin::grantLoadLocalResources()
 {
-    // This function exists only to support backwards compatibility with older
-    // versions of WebKit. Granting privileges to some, but not all, documents
-    // in a SecurityOrigin is a security hazard because the documents without
-    // the privilege can obtain the privilege by injecting script into the
-    // documents that have been granted the privilege.
-    ASSERT(SecurityPolicy::allowSubstituteDataAccessToLocal());
+    // Granting privileges to some, but not all, documents in a SecurityOrigin
+    // is a security hazard because the documents without the privilege can
+    // obtain the privilege by injecting script into the documents that have
+    // been granted the privilege.
+    //
+    // To be backwards compatible with older versions of WebKit, we also use
+    // this function to grant the ability to load local resources to documents
+    // loaded with SubstituteData.
+    ASSERT(isUnique() || SecurityPolicy::allowSubstituteDataAccessToLocal());
     m_canLoadLocalResources = true;
 }