Make IFRAME_SEAMLESS child documents inherit styles from their parent iframe element
https://bugs.webkit.org/show_bug.cgi?id=85940

Reviewed by Ojan Vafai.

Source/WebCore:

The HTML5 <iframe seamless> spec says:
In a CSS-supporting user agent: the user agent must, for the purpose of CSS property
inheritance only, treat the root element of the active document of the iframe
element's nested browsing context as being a child of the iframe element.
(Thus inherited properties on the root element of the document in the
iframe will inherit the computed values of those properties on the iframe
element instead of taking their initial values.)

Initially I implemented this support to the letter of the spec. However, doing so I learned
that WebKit has a RenderStyle for the Document Node, not just the root element of the document.
In this RenderStyle on the Document, we add a bunch of per-document styles from settings
including designMode.

This change makes StyleResolver::styleForDocument inherit style from the parent iframe's
style, before applying any of these per-document styles.  This may or may not be correct
depending on what behavior we want for rtl-ordering, page-zoom, locale, design mode, etc.
For now, we continue to treat the iframe's document as independent in these regards, and
the settings on that document override those inherited from the iframe.

Also, intially when making this work, I added redirects in recalcStyle and scheduleStyleRecalc
from the child document to the parent document in the case of seamless (since the parent
document effectively manages the style resolve and layout of the child in seamless mode).
However, I was not able to find a test which depended on this code change, so in this final patch
I have removed both of these modifications and replaced them with FIXMEs.  Based on discussions
with Ojan and James Robinson, I believe both of those changes may eventually be wanted.

This change basically does 3 things:
1.  Makes StyleResolver::styleForDocument inherit from the parent iframe.
2.  Makes any recalcStyle calls on the iframe propogate down into the child document (HTMLIFrameElement::didRecalcStyle).
3.  Makes Document::recalcStyle aware of the fact that the Document's style *can* change
    for reasons other than recalcStyle(Force).

I'm open to more testing suggestions, if reviewers have settings on the Document's style
that you want to make sure we inherit from the parent iframe, or don't inherit, etc.
I view this as a complete solution to this aspect of the current <iframe seamless> spec,
but likely not the last code we will write for this aspect of the seamless feature. :)

Tested by fast/frames/seamlesss/seamless-css-cascade.html and seamless-designMode.html

* css/StyleResolver.cpp:
(WebCore::StyleResolver::collectMatchingRulesForList):
* dom/Document.cpp:
(WebCore::Document::scheduleStyleRecalc):
(WebCore::Document::recalcStyle):
* html/HTMLIFrameElement.cpp:
(WebCore::HTMLIFrameElement::HTMLIFrameElement):
(WebCore::HTMLIFrameElement::didRecalcStyle):
(WebCore):
* html/HTMLIFrameElement.h:
(HTMLIFrameElement):

LayoutTests:

This single pass is deceptive.  seamless-designMode exists
to make sure that we do not regress application of Document-level
styles in the child document.

* fast/frames/seamless/seamless-css-cascade-expected.txt:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@116694 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index a694d08..71b39a6 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,16 @@
+2012-05-10  Eric Seidel  <eric@webkit.org>
+
+        Make IFRAME_SEAMLESS child documents inherit styles from their parent iframe element
+        https://bugs.webkit.org/show_bug.cgi?id=85940
+
+        Reviewed by Ojan Vafai.
+
+        This single pass is deceptive.  seamless-designMode exists
+        to make sure that we do not regress application of Document-level
+        styles in the child document.
+
+        * fast/frames/seamless/seamless-css-cascade-expected.txt:
+
 2012-05-10  Julien Chaffraix  <jchaffraix@webkit.org>
 
         Crash in computedCSSPadding* functions due to RenderImage::imageDimensionsChanged called during attachment
diff --git a/LayoutTests/fast/frames/seamless/seamless-css-cascade-expected.txt b/LayoutTests/fast/frames/seamless/seamless-css-cascade-expected.txt
index daf769a..1fce406 100644
--- a/LayoutTests/fast/frames/seamless/seamless-css-cascade-expected.txt
+++ b/LayoutTests/fast/frames/seamless/seamless-css-cascade-expected.txt
@@ -2,7 +2,7 @@
 PASS window.getComputedStyle(one).color is "rgb(255, 255, 0)"
 PASS window.getComputedStyle(two).color is "rgb(128, 0, 128)"
 PASS window.getComputedStyle(three).color is "rgb(255, 255, 255)"
-FAIL window.getComputedStyle(rootElement).color should be rgb(255, 165, 0). Was rgb(0, 0, 0).
-FAIL window.getComputedStyle(rootElement).color should be rgb(1, 2, 3). Was rgb(0, 0, 0).
+PASS window.getComputedStyle(rootElement).color is "rgb(255, 165, 0)"
+PASS window.getComputedStyle(rootElement).color is "rgb(1, 2, 3)"
 PASS window.getComputedStyle(one).color is "rgb(3, 2, 1)"
 
diff --git a/LayoutTests/fast/frames/seamless/seamless-inherited-document-style-expected.txt b/LayoutTests/fast/frames/seamless/seamless-inherited-document-style-expected.txt
new file mode 100644
index 0000000..fddcb87
--- /dev/null
+++ b/LayoutTests/fast/frames/seamless/seamless-inherited-document-style-expected.txt
@@ -0,0 +1,8 @@
+Test that seamless iframes inherit styles from their parent iframe instead of using StyleResolver::styleForDocument defaults.
+PASS window.getComputedStyle(rootElement)['-webkit-rtl-ordering'] is "visual"
+FAIL window.getComputedStyle(rootElement)['-webkit-user-modify'] should be read-write. Was read-only.
+PASS window.getComputedStyle(rootElement)['-webkit-locale'] is "en_US"
+PASS window.getComputedStyle(rootElement)['writing-mode'] is "lr"
+PASS window.getComputedStyle(rootElement)['direction'] is "rtl"
+PASS window.getComputedStyle(rootElement)['font'] is "normal normal normal 18px/normal Ahem"
+
diff --git a/LayoutTests/fast/frames/seamless/seamless-inherited-document-style.html b/LayoutTests/fast/frames/seamless/seamless-inherited-document-style.html
new file mode 100644
index 0000000..8bfb508
--- /dev/null
+++ b/LayoutTests/fast/frames/seamless/seamless-inherited-document-style.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="../../js/resources/js-test-pre.js"></script>
+<style>
+iframe {
+    -webkit-rtl-ordering: visual;
+    -webkit-user-modify: read-write;
+    -webkit-locale: 'en_US';
+    writing-mode: lr;
+    direction: rtl;
+    font: normal normal normal 18px/normal Ahem;
+}
+</style>
+<iframe id="iframe" seamless srcdoc=""></iframe>
+<script>
+debug("Test that seamless iframes inherit styles from their parent iframe instead of using StyleResolver::styleForDocument defaults.")
+window.onload = function () {
+    window.iframe = document.getElementById("iframe");
+    window.rootElement = iframe.contentDocument.documentElement;
+    shouldBeEqualToString("window.getComputedStyle(rootElement)['-webkit-rtl-ordering']", "visual");
+    shouldBeEqualToString("window.getComputedStyle(rootElement)['-webkit-user-modify']", "read-write");
+    shouldBeEqualToString("window.getComputedStyle(rootElement)['-webkit-locale']", "en_US");
+    shouldBeEqualToString("window.getComputedStyle(rootElement)['writing-mode']", "lr");
+    shouldBeEqualToString("window.getComputedStyle(rootElement)['direction']", "rtl");
+    shouldBeEqualToString("window.getComputedStyle(rootElement)['font']", "normal normal normal 18px/normal Ahem");
+}
+</script>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 2f99f1e..d0a5aec 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,61 @@
+2012-05-10  Eric Seidel  <eric@webkit.org>
+
+        Make IFRAME_SEAMLESS child documents inherit styles from their parent iframe element
+        https://bugs.webkit.org/show_bug.cgi?id=85940
+
+        Reviewed by Ojan Vafai.
+
+        The HTML5 <iframe seamless> spec says:
+        In a CSS-supporting user agent: the user agent must, for the purpose of CSS property
+        inheritance only, treat the root element of the active document of the iframe
+        element's nested browsing context as being a child of the iframe element.
+        (Thus inherited properties on the root element of the document in the
+        iframe will inherit the computed values of those properties on the iframe
+        element instead of taking their initial values.)
+
+        Initially I implemented this support to the letter of the spec. However, doing so I learned
+        that WebKit has a RenderStyle for the Document Node, not just the root element of the document.
+        In this RenderStyle on the Document, we add a bunch of per-document styles from settings
+        including designMode.
+
+        This change makes StyleResolver::styleForDocument inherit style from the parent iframe's
+        style, before applying any of these per-document styles.  This may or may not be correct
+        depending on what behavior we want for rtl-ordering, page-zoom, locale, design mode, etc.
+        For now, we continue to treat the iframe's document as independent in these regards, and
+        the settings on that document override those inherited from the iframe.
+
+        Also, intially when making this work, I added redirects in recalcStyle and scheduleStyleRecalc
+        from the child document to the parent document in the case of seamless (since the parent
+        document effectively manages the style resolve and layout of the child in seamless mode).
+        However, I was not able to find a test which depended on this code change, so in this final patch
+        I have removed both of these modifications and replaced them with FIXMEs.  Based on discussions
+        with Ojan and James Robinson, I believe both of those changes may eventually be wanted.
+
+        This change basically does 3 things:
+        1.  Makes StyleResolver::styleForDocument inherit from the parent iframe.
+        2.  Makes any recalcStyle calls on the iframe propogate down into the child document (HTMLIFrameElement::didRecalcStyle).
+        3.  Makes Document::recalcStyle aware of the fact that the Document's style *can* change
+            for reasons other than recalcStyle(Force).
+
+        I'm open to more testing suggestions, if reviewers have settings on the Document's style
+        that you want to make sure we inherit from the parent iframe, or don't inherit, etc.
+        I view this as a complete solution to this aspect of the current <iframe seamless> spec,
+        but likely not the last code we will write for this aspect of the seamless feature. :)
+
+        Tested by fast/frames/seamlesss/seamless-css-cascade.html and seamless-designMode.html
+
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::collectMatchingRulesForList):
+        * dom/Document.cpp:
+        (WebCore::Document::scheduleStyleRecalc):
+        (WebCore::Document::recalcStyle):
+        * html/HTMLIFrameElement.cpp:
+        (WebCore::HTMLIFrameElement::HTMLIFrameElement):
+        (WebCore::HTMLIFrameElement::didRecalcStyle):
+        (WebCore):
+        * html/HTMLIFrameElement.h:
+        (HTMLIFrameElement):
+
 2012-05-10  Julien Chaffraix  <jchaffraix@webkit.org>
 
         Crash in computedCSSPadding* functions due to RenderImage::imageDimensionsChanged called during attachment
diff --git a/Source/WebCore/css/StyleResolver.cpp b/Source/WebCore/css/StyleResolver.cpp
index 3c2b63a..24a180f 100644
--- a/Source/WebCore/css/StyleResolver.cpp
+++ b/Source/WebCore/css/StyleResolver.cpp
@@ -1527,13 +1527,28 @@
 {
     Frame* frame = document->frame();
 
+    // HTML5 states that seamless iframes should replace default CSS values
+    // with values inherited from the containing iframe element. However,
+    // some values (such as the case of designMode = "on") still need to
+    // be set by this "document style".
     RefPtr<RenderStyle> documentStyle = RenderStyle::create();
+    bool seamlessWithParent = document->shouldDisplaySeamlesslyWithParent();
+    if (seamlessWithParent) {
+        RenderStyle* iframeStyle = document->seamlessParentIFrame()->renderStyle();
+        if (iframeStyle)
+            documentStyle->inheritFrom(iframeStyle);
+    }
+
+    // FIXME: It's not clear which values below we want to override in the seamless case!
     documentStyle->setDisplay(BLOCK);
-    documentStyle->setRTLOrdering(document->visuallyOrdered() ? VisualOrder : LogicalOrder);
-    documentStyle->setZoom(frame && !document->printing() ? frame->pageZoomFactor() : 1);
-    documentStyle->setPageScaleTransform(frame ? frame->frameScaleFactor() : 1);
+    if (!seamlessWithParent) {
+        documentStyle->setRTLOrdering(document->visuallyOrdered() ? VisualOrder : LogicalOrder);
+        documentStyle->setZoom(frame && !document->printing() ? frame->pageZoomFactor() : 1);
+        documentStyle->setPageScaleTransform(frame ? frame->frameScaleFactor() : 1);
+        documentStyle->setLocale(document->contentLanguage());
+    }
+    // FIXME: This overrides any -webkit-user-modify inherited from the parent iframe.
     documentStyle->setUserModify(document->inDesignMode() ? READ_WRITE : READ_ONLY);
-    documentStyle->setLocale(document->contentLanguage());
 
     Element* docElement = document->documentElement();
     RenderObject* docElementRenderer = docElement ? docElement->renderer() : 0;
@@ -1562,6 +1577,10 @@
         }
     }
 
+    // Seamless iframes want to inherit their font from their parent iframe, so early return before setting the font.
+    if (seamlessWithParent)
+        return documentStyle.release();
+
     FontDescription fontDescription;
     fontDescription.setUsePrinterFont(document->printing());
     fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle->locale()));
diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp
index d6e42ef..f467af3 100644
--- a/Source/WebCore/dom/Document.cpp
+++ b/Source/WebCore/dom/Document.cpp
@@ -1651,6 +1651,9 @@
 
 void Document::scheduleStyleRecalc()
 {
+    // FIXME: In the seamless case, we should likely schedule a style recalc
+    // on our parent and instead return early here.
+
     if (m_styleRecalcTimer.isActive() || inPageCache())
         return;
 
@@ -1704,7 +1707,10 @@
     
     if (m_inStyleRecalc)
         return; // Guard against re-entrancy. -dwh
-    
+
+    // FIXME: In the seamless case, we may wish to exit early in the child after recalcing our parent chain.
+    // I have not yet found a test which requires such.
+
     if (m_hasDirtyStyleResolver)
         updateActiveStylesheets(RecalcStyleImmediately);
 
@@ -1730,7 +1736,8 @@
     if (m_pendingStyleRecalcShouldForce)
         change = Force;
 
-    if (change == Force) {
+    // Recalculating the root style (on the document) is not needed in the common case.
+    if ((change == Force) || (shouldDisplaySeamlesslyWithParent() && (change >= Inherit))) {
         // style selector may set this again during recalc
         m_hasNodesWithPlaceholderStyle = false;
         
diff --git a/Source/WebCore/html/HTMLIFrameElement.cpp b/Source/WebCore/html/HTMLIFrameElement.cpp
index 04529c8..c7e157e 100644
--- a/Source/WebCore/html/HTMLIFrameElement.cpp
+++ b/Source/WebCore/html/HTMLIFrameElement.cpp
@@ -41,6 +41,7 @@
     : HTMLFrameElementBase(tagName, document)
 {
     ASSERT(hasTagName(iframeTag));
+    setHasCustomWillOrDidRecalcStyle();
 }
 
 PassRefPtr<HTMLIFrameElement> HTMLIFrameElement::create(const QualifiedName& tagName, Document* document)
@@ -120,6 +121,15 @@
     return contentDocument() && contentDocument()->mayDisplaySeamlessWithParent() && hasAttribute(seamlessAttr);
 }
 
+void HTMLIFrameElement::didRecalcStyle(StyleChange styleChange)
+{
+    if (!shouldDisplaySeamlessly())
+        return;
+    Document* childDocument = contentDocument();
+    if (styleChange >= Inherit || childDocument->childNeedsStyleRecalc() || childDocument->needsStyleRecalc())
+        contentDocument()->recalcStyle(styleChange);
+}
+
 #if ENABLE(MICRODATA)
 String HTMLIFrameElement::itemValueText() const
 {
diff --git a/Source/WebCore/html/HTMLIFrameElement.h b/Source/WebCore/html/HTMLIFrameElement.h
index 5d55359..ff889b0 100644
--- a/Source/WebCore/html/HTMLIFrameElement.h
+++ b/Source/WebCore/html/HTMLIFrameElement.h
@@ -43,10 +43,12 @@
 
     virtual InsertionNotificationRequest insertedInto(Node*) OVERRIDE;
     virtual void removedFrom(Node*) OVERRIDE;
-    
+
     virtual bool rendererIsNeeded(const NodeRenderingContext&);
     virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
-       
+
+    virtual void didRecalcStyle(StyleChange) OVERRIDE;
+
 #if ENABLE(MICRODATA)
     virtual String itemValueText() const OVERRIDE;
     virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE;