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/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();