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