Home | History | Annotate | Download | only in plugins
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/plugins/PluginOcclusionSupport.h"
     33 
     34 #include "core/HTMLNames.h"
     35 #include "core/dom/Element.h"
     36 #include "core/frame/FrameView.h"
     37 #include "core/frame/LocalFrame.h"
     38 #include "core/html/HTMLElement.h"
     39 #include "core/html/HTMLFrameOwnerElement.h"
     40 #include "core/rendering/RenderBox.h"
     41 #include "core/rendering/RenderObject.h"
     42 #include "platform/Widget.h"
     43 #include "wtf/HashSet.h"
     44 
     45 // This file provides a utility function to support rendering certain elements above plugins.
     46 
     47 namespace WebCore {
     48 
     49 static void getObjectStack(const RenderObject* ro, Vector<const RenderObject*>* roStack)
     50 {
     51     roStack->clear();
     52     while (ro) {
     53         roStack->append(ro);
     54         ro = ro->parent();
     55     }
     56 }
     57 
     58 // Returns true if stack1 is at or above stack2
     59 static bool iframeIsAbovePlugin(const Vector<const RenderObject*>& iframeZstack, const Vector<const RenderObject*>& pluginZstack)
     60 {
     61     for (size_t i = 0; i < iframeZstack.size() && i < pluginZstack.size(); i++) {
     62         // The root is at the end of these stacks. We want to iterate
     63         // root-downwards so we index backwards from the end.
     64         const RenderObject* ro1 = iframeZstack[iframeZstack.size() - 1 - i];
     65         const RenderObject* ro2 = pluginZstack[pluginZstack.size() - 1 - i];
     66 
     67         if (ro1 != ro2) {
     68             // When we find nodes in the stack that are not the same, then
     69             // we've found the nodes just below the lowest comment ancestor.
     70             // Determine which should be on top.
     71 
     72             // See if z-index determines an order.
     73             if (ro1->style() && ro2->style()) {
     74                 int z1 = ro1->style()->zIndex();
     75                 int z2 = ro2->style()->zIndex();
     76                 if (z1 > z2)
     77                     return true;
     78                 if (z1 < z2)
     79                     return false;
     80             }
     81 
     82             // If the plugin does not have an explicit z-index it stacks behind the iframe.
     83             // This is for maintaining compatibility with IE.
     84             if (ro2->style()->position() == StaticPosition) {
     85                 // The 0'th elements of these RenderObject arrays represent the plugin node and
     86                 // the iframe.
     87                 const RenderObject* pluginRenderObject = pluginZstack[0];
     88                 const RenderObject* iframeRenderObject = iframeZstack[0];
     89 
     90                 if (pluginRenderObject->style() && iframeRenderObject->style()) {
     91                     if (pluginRenderObject->style()->zIndex() > iframeRenderObject->style()->zIndex())
     92                         return false;
     93                 }
     94                 return true;
     95             }
     96 
     97             // Inspect the document order. Later order means higher stacking.
     98             const RenderObject* parent = ro1->parent();
     99             if (!parent)
    100                 return false;
    101             ASSERT(parent == ro2->parent());
    102 
    103             for (const RenderObject* ro = parent->slowFirstChild(); ro; ro = ro->nextSibling()) {
    104                 if (ro == ro1)
    105                     return false;
    106                 if (ro == ro2)
    107                     return true;
    108             }
    109             ASSERT(false); // We should have seen ro1 and ro2 by now.
    110             return false;
    111         }
    112     }
    113     return true;
    114 }
    115 
    116 static bool intersectsRect(const RenderObject* renderer, const IntRect& rect)
    117 {
    118     return renderer->absoluteBoundingBoxRectIgnoringTransforms().intersects(rect)
    119         && (!renderer->style() || renderer->style()->visibility() == VISIBLE);
    120 }
    121 
    122 static void addToOcclusions(const RenderBox* renderer, Vector<IntRect>& occlusions)
    123 {
    124     IntPoint point = roundedIntPoint(renderer->localToAbsolute());
    125     IntSize size(renderer->width(), renderer->height());
    126     occlusions.append(IntRect(point, size));
    127 }
    128 
    129 static void addTreeToOcclusions(const RenderObject* renderer, const IntRect& frameRect, Vector<IntRect>& occlusions)
    130 {
    131     if (!renderer)
    132         return;
    133     if (renderer->isBox() && intersectsRect(renderer, frameRect))
    134         addToOcclusions(toRenderBox(renderer), occlusions);
    135     for (RenderObject* child = renderer->slowFirstChild(); child; child = child->nextSibling())
    136         addTreeToOcclusions(child, frameRect, occlusions);
    137 }
    138 
    139 static const Element* topLayerAncestor(const Element* element)
    140 {
    141     while (element && !element->isInTopLayer())
    142         element = element->parentOrShadowHostElement();
    143     return element;
    144 }
    145 
    146 // Return a set of rectangles that should not be overdrawn by the
    147 // plugin ("cutouts"). This helps implement the "iframe shim"
    148 // technique of overlaying a windowed plugin with content from the
    149 // page. In a nutshell, iframe elements should occlude plugins when
    150 // they occur higher in the stacking order.
    151 void getPluginOcclusions(Element* element, Widget* parentWidget, const IntRect& frameRect, Vector<IntRect>& occlusions)
    152 {
    153     RenderObject* pluginNode = element->renderer();
    154     ASSERT(pluginNode);
    155     if (!pluginNode->style())
    156         return;
    157     Vector<const RenderObject*> pluginZstack;
    158     Vector<const RenderObject*> iframeZstack;
    159     getObjectStack(pluginNode, &pluginZstack);
    160 
    161     if (!parentWidget->isFrameView())
    162         return;
    163 
    164     FrameView* parentFrameView = toFrameView(parentWidget);
    165 
    166     // Occlusions by iframes.
    167     const HashSet<RefPtr<Widget> >* children = parentFrameView->children();
    168     for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != children->end(); ++it) {
    169         // We only care about FrameView's because iframes show up as FrameViews.
    170         if (!(*it)->isFrameView())
    171             continue;
    172 
    173         const FrameView* frameView = toFrameView((*it).get());
    174         // Check to make sure we can get both the element and the RenderObject
    175         // for this FrameView, if we can't just move on to the next object.
    176         // FIXME: Plugin occlusion by remote frames is probably broken.
    177         HTMLElement* element = frameView->frame().deprecatedLocalOwner();
    178         if (!element || !element->renderer())
    179             continue;
    180 
    181         RenderObject* iframeRenderer = element->renderer();
    182 
    183         if (isHTMLIFrameElement(*element) && intersectsRect(iframeRenderer, frameRect)) {
    184             getObjectStack(iframeRenderer, &iframeZstack);
    185             if (iframeIsAbovePlugin(iframeZstack, pluginZstack))
    186                 addToOcclusions(toRenderBox(iframeRenderer), occlusions);
    187         }
    188     }
    189 
    190     // Occlusions by top layer elements.
    191     // FIXME: There's no handling yet for the interaction between top layer and
    192     // iframes. For example, a plugin in the top layer will be occluded by an
    193     // iframe. And a plugin inside an iframe in the top layer won't be respected
    194     // as being in the top layer.
    195     const Element* ancestor = topLayerAncestor(element);
    196     Document* document = parentFrameView->frame().document();
    197     const WillBeHeapVector<RefPtrWillBeMember<Element> >& elements = document->topLayerElements();
    198     size_t start = ancestor ? elements.find(ancestor) + 1 : 0;
    199     for (size_t i = start; i < elements.size(); ++i)
    200         addTreeToOcclusions(elements[i]->renderer(), frameRect, occlusions);
    201 }
    202 
    203 } // namespace WebCore
    204