• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
widgetNewParentMap()42 static WidgetToParentMap& widgetNewParentMap()
43 {
44     DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
45     return map;
46 }
47 
48 static unsigned s_updateSuspendCount = 0;
49 
UpdateSuspendScope()50 HTMLFrameOwnerElement::UpdateSuspendScope::UpdateSuspendScope()
51 {
52     ++s_updateSuspendCount;
53 }
54 
performDeferredWidgetTreeOperations()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 
~UpdateSuspendScope()73 HTMLFrameOwnerElement::UpdateSuspendScope::~UpdateSuspendScope()
74 {
75     ASSERT(s_updateSuspendCount > 0);
76     if (s_updateSuspendCount == 1)
77         performDeferredWidgetTreeOperations();
78     --s_updateSuspendCount;
79 }
80 
moveWidgetToParentSoon(Widget * child,FrameView * parent)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 
HTMLFrameOwnerElement(const QualifiedName & tagName,Document & document)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 
renderPart() const101 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 
setContentFrame(Frame & frame)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 
clearContentFrame()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 
disconnectContentFrame()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 
~HTMLFrameOwnerElement()147 HTMLFrameOwnerElement::~HTMLFrameOwnerElement()
148 {
149     if (m_contentFrame)
150         m_contentFrame->disconnectOwnerElement();
151 }
152 
contentDocument() const153 Document* HTMLFrameOwnerElement::contentDocument() const
154 {
155     return (m_contentFrame && m_contentFrame->isLocalFrame()) ? toLocalFrame(m_contentFrame)->document() : 0;
156 }
157 
contentWindow() const158 LocalDOMWindow* HTMLFrameOwnerElement::contentWindow() const
159 {
160     return m_contentFrame ? m_contentFrame->domWindow() : 0;
161 }
162 
setSandboxFlags(SandboxFlags flags)163 void HTMLFrameOwnerElement::setSandboxFlags(SandboxFlags flags)
164 {
165     m_sandboxFlags = flags;
166 }
167 
isKeyboardFocusable() const168 bool HTMLFrameOwnerElement::isKeyboardFocusable() const
169 {
170     return m_contentFrame && HTMLElement::isKeyboardFocusable();
171 }
172 
dispatchLoad()173 void HTMLFrameOwnerElement::dispatchLoad()
174 {
175     dispatchEvent(Event::create(EventTypeNames::load));
176 }
177 
getSVGDocument(ExceptionState & exceptionState) const178 Document* HTMLFrameOwnerElement::getSVGDocument(ExceptionState& exceptionState) const
179 {
180     Document* doc = contentDocument();
181     if (doc && doc->isSVGDocument())
182         return doc;
183     return 0;
184 }
185 
setWidget(PassRefPtr<Widget> widget)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 
ownedWidget() const215 Widget* HTMLFrameOwnerElement::ownedWidget() const
216 {
217     return m_widget.get();
218 }
219 
loadOrRedirectSubframe(const KURL & url,const AtomicString & frameName,bool lockBackForwardList)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