1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 *           (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5 * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25#include "core/html/HTMLEmbedElement.h"
26
27#include "CSSPropertyNames.h"
28#include "HTMLNames.h"
29#include "core/dom/Attribute.h"
30#include "core/dom/shadow/ShadowRoot.h"
31#include "core/html/HTMLImageLoader.h"
32#include "core/html/HTMLObjectElement.h"
33#include "core/html/PluginDocument.h"
34#include "core/html/parser/HTMLParserIdioms.h"
35#include "core/rendering/RenderEmbeddedObject.h"
36#include "core/rendering/RenderWidget.h"
37
38namespace WebCore {
39
40using namespace HTMLNames;
41
42inline HTMLEmbedElement::HTMLEmbedElement(Document& document, bool createdByParser)
43    : HTMLPlugInElement(embedTag, document, createdByParser, ShouldPreferPlugInsForImages)
44{
45    ScriptWrappable::init(this);
46}
47
48PassRefPtr<HTMLEmbedElement> HTMLEmbedElement::create(Document& document, bool createdByParser)
49{
50    RefPtr<HTMLEmbedElement> element = adoptRef(new HTMLEmbedElement(document, createdByParser));
51    element->ensureUserAgentShadowRoot();
52    return element.release();
53}
54
55static inline RenderWidget* findWidgetRenderer(const Node* n)
56{
57    if (!n->renderer())
58        do
59            n = n->parentNode();
60        while (n && !n->hasTagName(objectTag));
61
62    if (n && n->renderer() && n->renderer()->isWidget())
63        return toRenderWidget(n->renderer());
64
65    return 0;
66}
67
68RenderWidget* HTMLEmbedElement::existingRenderWidget() const
69{
70    return findWidgetRenderer(this);
71}
72
73bool HTMLEmbedElement::isPresentationAttribute(const QualifiedName& name) const
74{
75    if (name == hiddenAttr)
76        return true;
77    return HTMLPlugInElement::isPresentationAttribute(name);
78}
79
80void HTMLEmbedElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
81{
82    if (name == hiddenAttr) {
83        if (equalIgnoringCase(value, "yes") || equalIgnoringCase(value, "true")) {
84            addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, 0, CSSPrimitiveValue::CSS_PX);
85            addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, 0, CSSPrimitiveValue::CSS_PX);
86        }
87    } else {
88        HTMLPlugInElement::collectStyleForPresentationAttribute(name, value, style);
89    }
90}
91
92void HTMLEmbedElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
93{
94    if (name == typeAttr) {
95        m_serviceType = value.string().lower();
96        size_t pos = m_serviceType.find(";");
97        if (pos != kNotFound)
98            m_serviceType = m_serviceType.left(pos);
99    } else if (name == codeAttr) {
100        m_url = stripLeadingAndTrailingHTMLSpaces(value);
101    } else if (name == srcAttr) {
102        m_url = stripLeadingAndTrailingHTMLSpaces(value);
103        if (renderer() && isImageType()) {
104            if (!m_imageLoader)
105                m_imageLoader = adoptPtr(new HTMLImageLoader(this));
106            m_imageLoader->updateFromElementIgnoringPreviousError();
107        }
108    } else {
109        HTMLPlugInElement::parseAttribute(name, value);
110    }
111}
112
113void HTMLEmbedElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues)
114{
115    if (!hasAttributes())
116        return;
117
118    for (unsigned i = 0; i < attributeCount(); ++i) {
119        const Attribute* attribute = attributeItem(i);
120        paramNames.append(attribute->localName().string());
121        paramValues.append(attribute->value().string());
122    }
123}
124
125// FIXME: This should be unified with HTMLObjectElement::updateWidget and
126// moved down into HTMLPluginElement.cpp
127void HTMLEmbedElement::updateWidgetInternal()
128{
129    ASSERT(!renderEmbeddedObject()->showsUnavailablePluginIndicator());
130    ASSERT(needsWidgetUpdate());
131    setNeedsWidgetUpdate(false);
132
133    if (m_url.isEmpty() && m_serviceType.isEmpty())
134        return;
135
136    // Note these pass m_url and m_serviceType to allow better code sharing with
137    // <object> which modifies url and serviceType before calling these.
138    if (!allowedToLoadFrameURL(m_url))
139        return;
140
141    // FIXME: These should be joined into a PluginParameters class.
142    Vector<String> paramNames;
143    Vector<String> paramValues;
144    parametersForPlugin(paramNames, paramValues);
145
146    RefPtr<HTMLEmbedElement> protect(this); // Loading the plugin might remove us from the document.
147    bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(m_url);
148    if (!beforeLoadAllowedLoad) {
149        if (document().isPluginDocument()) {
150            // Plugins inside plugin documents load differently than other plugins. By the time
151            // we are here in a plugin document, the load of the plugin (which is the plugin document's
152            // main resource) has already started. We need to explicitly cancel the main resource load here.
153            toPluginDocument(document()).cancelManualPluginLoad();
154        }
155        return;
156    }
157    if (!renderer()) // Do not load the plugin if beforeload removed this element or its renderer.
158        return;
159
160    // FIXME: beforeLoad could have detached the renderer!  Just like in the <object> case above.
161    requestObject(m_url, m_serviceType, paramNames, paramValues);
162}
163
164bool HTMLEmbedElement::rendererIsNeeded(const RenderStyle& style)
165{
166    if (isImageType())
167        return HTMLPlugInElement::rendererIsNeeded(style);
168
169    Frame* frame = document().frame();
170    if (!frame)
171        return false;
172
173    // If my parent is an <object> and is not set to use fallback content, I
174    // should be ignored and not get a renderer.
175    ContainerNode* p = parentNode();
176    if (p && p->hasTagName(objectTag)) {
177        ASSERT(p->renderer());
178        if (!toHTMLObjectElement(p)->useFallbackContent()) {
179            ASSERT(!p->renderer()->isEmbeddedObject());
180            return false;
181        }
182    }
183    return HTMLPlugInElement::rendererIsNeeded(style);
184}
185
186bool HTMLEmbedElement::isURLAttribute(const Attribute& attribute) const
187{
188    return attribute.name() == srcAttr || HTMLPlugInElement::isURLAttribute(attribute);
189}
190
191const AtomicString HTMLEmbedElement::imageSourceURL() const
192{
193    return getAttribute(srcAttr);
194}
195
196void HTMLEmbedElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
197{
198    HTMLPlugInElement::addSubresourceAttributeURLs(urls);
199
200    addSubresourceURL(urls, document().completeURL(getAttribute(srcAttr)));
201}
202
203bool HTMLEmbedElement::isInteractiveContent() const
204{
205    return true;
206}
207
208bool HTMLEmbedElement::isExposed() const
209{
210    // http://www.whatwg.org/specs/web-apps/current-work/#exposed
211    for (Node* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
212        if (ancestor->hasTagName(objectTag) && toHTMLObjectElement(ancestor)->isExposed())
213            return false;
214    }
215    return true;
216}
217
218}
219