Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  *
     19  */
     20 
     21 #include "config.h"
     22 #include "core/html/HTMLFrameOwnerElement.h"
     23 
     24 #include "bindings/v8/ExceptionMessages.h"
     25 #include "bindings/v8/ExceptionState.h"
     26 #include "core/accessibility/AXObjectCache.h"
     27 #include "core/dom/ExceptionCode.h"
     28 #include "core/events/Event.h"
     29 #include "core/frame/FrameView.h"
     30 #include "core/frame/LocalFrame.h"
     31 #include "core/loader/FrameLoader.h"
     32 #include "core/loader/FrameLoaderClient.h"
     33 #include "core/rendering/RenderLayer.h"
     34 #include "core/rendering/RenderPart.h"
     35 #include "core/rendering/compositing/RenderLayerCompositor.h"
     36 #include "platform/weborigin/SecurityOrigin.h"
     37 #include "platform/weborigin/SecurityPolicy.h"
     38 
     39 namespace WebCore {
     40 
     41 typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap;
     42 static WidgetToParentMap& widgetNewParentMap()
     43 {
     44     DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
     45     return map;
     46 }
     47 
     48 static unsigned s_updateSuspendCount = 0;
     49 
     50 HTMLFrameOwnerElement::UpdateSuspendScope::UpdateSuspendScope()
     51 {
     52     ++s_updateSuspendCount;
     53 }
     54 
     55 void HTMLFrameOwnerElement::UpdateSuspendScope::performDeferredWidgetTreeOperations()
     56 {
     57     WidgetToParentMap map;
     58     widgetNewParentMap().swap(map);
     59     WidgetToParentMap::iterator end = map.end();
     60     for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
     61         Widget* child = it->key.get();
     62         ScrollView* currentParent = toScrollView(child->parent());
     63         FrameView* newParent = it->value;
     64         if (newParent != currentParent) {
     65             if (currentParent)
     66                 currentParent->removeChild(child);
     67             if (newParent)
     68                 newParent->addChild(child);
     69         }
     70     }
     71 }
     72 
     73 HTMLFrameOwnerElement::UpdateSuspendScope::~UpdateSuspendScope()
     74 {
     75     ASSERT(s_updateSuspendCount > 0);
     76     if (s_updateSuspendCount == 1)
     77         performDeferredWidgetTreeOperations();
     78     --s_updateSuspendCount;
     79 }
     80 
     81 static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
     82 {
     83     if (!s_updateSuspendCount) {
     84         if (parent)
     85             parent->addChild(child);
     86         else if (toScrollView(child->parent()))
     87             toScrollView(child->parent())->removeChild(child);
     88         return;
     89     }
     90     widgetNewParentMap().set(child, parent);
     91 }
     92 
     93 HTMLFrameOwnerElement::HTMLFrameOwnerElement(const QualifiedName& tagName, Document& document)
     94     : HTMLElement(tagName, document)
     95     , m_contentFrame(0)
     96     , m_widget(nullptr)
     97     , m_sandboxFlags(SandboxNone)
     98 {
     99 }
    100 
    101 RenderPart* HTMLFrameOwnerElement::renderPart() const
    102 {
    103     // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
    104     // when using fallback content.
    105     if (!renderer() || !renderer()->isRenderPart())
    106         return 0;
    107     return toRenderPart(renderer());
    108 }
    109 
    110 void HTMLFrameOwnerElement::setContentFrame(Frame& frame)
    111 {
    112     // Make sure we will not end up with two frames referencing the same owner element.
    113     ASSERT(!m_contentFrame || m_contentFrame->owner() != this);
    114     // Disconnected frames should not be allowed to load.
    115     ASSERT(inDocument());
    116     m_contentFrame = &frame;
    117 
    118     for (ContainerNode* node = this; node; node = node->parentOrShadowHostNode())
    119         node->incrementConnectedSubframeCount();
    120 }
    121 
    122 void HTMLFrameOwnerElement::clearContentFrame()
    123 {
    124     if (!m_contentFrame)
    125         return;
    126 
    127     m_contentFrame = 0;
    128 
    129     for (ContainerNode* node = this; node; node = node->parentOrShadowHostNode())
    130         node->decrementConnectedSubframeCount();
    131 }
    132 
    133 void HTMLFrameOwnerElement::disconnectContentFrame()
    134 {
    135     // FIXME: Currently we don't do this in removedFrom because this causes an
    136     // unload event in the subframe which could execute script that could then
    137     // reach up into this document and then attempt to look back down. We should
    138     // see if this behavior is really needed as Gecko does not allow this.
    139     if (Frame* frame = contentFrame()) {
    140         RefPtr<Frame> protect(frame);
    141         if (frame->isLocalFrame())
    142             toLocalFrame(frame)->loader().frameDetached();
    143         frame->disconnectOwnerElement();
    144     }
    145 }
    146 
    147 HTMLFrameOwnerElement::~HTMLFrameOwnerElement()
    148 {
    149     if (m_contentFrame)
    150         m_contentFrame->disconnectOwnerElement();
    151 }
    152 
    153 Document* HTMLFrameOwnerElement::contentDocument() const
    154 {
    155     return (m_contentFrame && m_contentFrame->isLocalFrame()) ? toLocalFrame(m_contentFrame)->document() : 0;
    156 }
    157 
    158 LocalDOMWindow* HTMLFrameOwnerElement::contentWindow() const
    159 {
    160     return m_contentFrame ? m_contentFrame->domWindow() : 0;
    161 }
    162 
    163 void HTMLFrameOwnerElement::setSandboxFlags(SandboxFlags flags)
    164 {
    165     m_sandboxFlags = flags;
    166 }
    167 
    168 bool HTMLFrameOwnerElement::isKeyboardFocusable() const
    169 {
    170     return m_contentFrame && HTMLElement::isKeyboardFocusable();
    171 }
    172 
    173 void HTMLFrameOwnerElement::dispatchLoad()
    174 {
    175     dispatchEvent(Event::create(EventTypeNames::load));
    176 }
    177 
    178 Document* HTMLFrameOwnerElement::getSVGDocument(ExceptionState& exceptionState) const
    179 {
    180     Document* doc = contentDocument();
    181     if (doc && doc->isSVGDocument())
    182         return doc;
    183     return 0;
    184 }
    185 
    186 void HTMLFrameOwnerElement::setWidget(PassRefPtr<Widget> widget)
    187 {
    188     if (widget == m_widget)
    189         return;
    190 
    191     if (m_widget) {
    192         if (m_widget->parent())
    193             moveWidgetToParentSoon(m_widget.get(), 0);
    194         m_widget = nullptr;
    195     }
    196 
    197     m_widget = widget;
    198 
    199     RenderWidget* renderWidget = toRenderWidget(renderer());
    200     if (!renderWidget)
    201         return;
    202 
    203     if (m_widget) {
    204         renderWidget->updateOnWidgetChange();
    205 
    206         ASSERT(document().view() == renderWidget->frameView());
    207         ASSERT(renderWidget->frameView());
    208         moveWidgetToParentSoon(m_widget.get(), renderWidget->frameView());
    209     }
    210 
    211     if (AXObjectCache* cache = document().existingAXObjectCache())
    212         cache->childrenChanged(renderWidget);
    213 }
    214 
    215 Widget* HTMLFrameOwnerElement::ownedWidget() const
    216 {
    217     return m_widget.get();
    218 }
    219 
    220 bool HTMLFrameOwnerElement::loadOrRedirectSubframe(const KURL& url, const AtomicString& frameName, bool lockBackForwardList)
    221 {
    222     RefPtr<LocalFrame> parentFrame = document().frame();
    223     // FIXME(kenrb): The necessary semantics for RemoteFrames have not been worked out yet, but this will likely need some logic to handle them.
    224     if (contentFrame() && contentFrame()->isLocalFrame()) {
    225         toLocalFrame(contentFrame())->navigationScheduler().scheduleLocationChange(&document(), url.string(), Referrer(document().outgoingReferrer(), document().referrerPolicy()), lockBackForwardList);
    226         return true;
    227     }
    228 
    229     if (!document().securityOrigin()->canDisplay(url)) {
    230         FrameLoader::reportLocalLoadFailed(parentFrame.get(), url.string());
    231         return false;
    232     }
    233 
    234     if (!SubframeLoadingDisabler::canLoadFrame(*this))
    235         return false;
    236 
    237     String referrer = SecurityPolicy::generateReferrerHeader(document().referrerPolicy(), url, document().outgoingReferrer());
    238     RefPtr<LocalFrame> childFrame = parentFrame->loader().client()->createFrame(url, frameName, Referrer(referrer, document().referrerPolicy()), this);
    239 
    240     if (!childFrame)  {
    241         parentFrame->loader().checkCompleted();
    242         return false;
    243     }
    244 
    245     // All new frames will have m_isComplete set to true at this point due to synchronously loading
    246     // an empty document in FrameLoader::init(). But many frames will now be starting an
    247     // asynchronous load of url, so we set m_isComplete to false and then check if the load is
    248     // actually completed below. (Note that we set m_isComplete to false even for synchronous
    249     // loads, so that checkCompleted() below won't bail early.)
    250     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.
    251     childFrame->loader().started();
    252 
    253     FrameView* view = childFrame->view();
    254     RenderObject* renderObject = renderer();
    255     // We need to test the existence of renderObject and its widget-ness, as
    256     // failing to do so causes problems.
    257     if (renderObject && renderObject->isWidget() && view)
    258         setWidget(view);
    259 
    260     // Some loads are performed synchronously (e.g., about:blank and loads
    261     // cancelled by returning a null ResourceRequest from requestFromDelegate).
    262     // In these cases, the synchronous load would have finished
    263     // before we could connect the signals, so make sure to send the
    264     // completed() signal for the child by hand and mark the load as being
    265     // complete.
    266     // FIXME: In this case the LocalFrame will have finished loading before
    267     // it's being added to the child list. It would be a good idea to
    268     // create the child first, then invoke the loader separately.
    269     if (childFrame->loader().state() == FrameStateComplete && !childFrame->loader().policyDocumentLoader())
    270         childFrame->loader().checkCompleted();
    271     return true;
    272 }
    273 
    274 
    275 } // namespace WebCore
    276