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