1/*
2 * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "core/html/HTMLSourceElement.h"
28
29#include "core/HTMLNames.h"
30#include "core/css/MediaList.h"
31#include "core/css/MediaQueryList.h"
32#include "core/css/MediaQueryMatcher.h"
33#include "core/dom/Document.h"
34#include "core/events/Event.h"
35#include "core/events/EventSender.h"
36#include "core/html/HTMLMediaElement.h"
37#include "core/html/HTMLPictureElement.h"
38#include "platform/Logging.h"
39
40namespace blink {
41
42using namespace HTMLNames;
43
44static SourceEventSender& sourceErrorEventSender()
45{
46    DEFINE_STATIC_LOCAL(SourceEventSender, sharedErrorEventSender, (EventTypeNames::error));
47    return sharedErrorEventSender;
48}
49
50class HTMLSourceElement::Listener FINAL : public MediaQueryListListener {
51public:
52    explicit Listener(HTMLSourceElement* element) : m_element(element) { }
53    virtual void notifyMediaQueryChanged() OVERRIDE
54    {
55        if (m_element)
56            m_element->notifyMediaQueryChanged();
57    }
58
59    void clearElement() { m_element = nullptr; }
60    virtual void trace(Visitor* visitor) OVERRIDE
61    {
62        visitor->trace(m_element);
63        MediaQueryListListener::trace(visitor);
64    }
65private:
66    RawPtrWillBeMember<HTMLSourceElement> m_element;
67};
68
69inline HTMLSourceElement::HTMLSourceElement(Document& document)
70    : HTMLElement(sourceTag, document)
71    , m_listener(adoptRefWillBeNoop(new Listener(this)))
72{
73    WTF_LOG(Media, "HTMLSourceElement::HTMLSourceElement - %p", this);
74}
75
76DEFINE_NODE_FACTORY(HTMLSourceElement)
77
78HTMLSourceElement::~HTMLSourceElement()
79{
80    sourceErrorEventSender().cancelEvent(this);
81#if !ENABLE(OILPAN)
82    m_listener->clearElement();
83#endif
84}
85
86Node::InsertionNotificationRequest HTMLSourceElement::insertedInto(ContainerNode* insertionPoint)
87{
88    HTMLElement::insertedInto(insertionPoint);
89    Element* parent = parentElement();
90    if (isHTMLMediaElement(parent))
91        toHTMLMediaElement(parent)->sourceWasAdded(this);
92    if (isHTMLPictureElement(parent))
93        toHTMLPictureElement(parent)->sourceOrMediaChanged();
94    return InsertionDone;
95}
96
97void HTMLSourceElement::removedFrom(ContainerNode* removalRoot)
98{
99    Element* parent = parentElement();
100    if (!parent && removalRoot->isElementNode())
101        parent = toElement(removalRoot);
102    if (isHTMLMediaElement(parent))
103        toHTMLMediaElement(parent)->sourceWasRemoved(this);
104    if (isHTMLPictureElement(parent))
105        toHTMLPictureElement(parent)->sourceOrMediaChanged();
106    HTMLElement::removedFrom(removalRoot);
107}
108
109void HTMLSourceElement::setSrc(const String& url)
110{
111    setAttribute(srcAttr, AtomicString(url));
112}
113
114const AtomicString& HTMLSourceElement::type() const
115{
116    return getAttribute(typeAttr);
117}
118
119void HTMLSourceElement::setType(const AtomicString& type)
120{
121    setAttribute(typeAttr, type);
122}
123
124void HTMLSourceElement::scheduleErrorEvent()
125{
126    WTF_LOG(Media, "HTMLSourceElement::scheduleErrorEvent - %p", this);
127    sourceErrorEventSender().dispatchEventSoon(this);
128}
129
130void HTMLSourceElement::cancelPendingErrorEvent()
131{
132    WTF_LOG(Media, "HTMLSourceElement::cancelPendingErrorEvent - %p", this);
133    sourceErrorEventSender().cancelEvent(this);
134}
135
136void HTMLSourceElement::dispatchPendingEvent(SourceEventSender* eventSender)
137{
138    ASSERT_UNUSED(eventSender, eventSender == &sourceErrorEventSender());
139    WTF_LOG(Media, "HTMLSourceElement::dispatchPendingEvent - %p", this);
140    dispatchEvent(Event::createCancelable(EventTypeNames::error));
141}
142
143bool HTMLSourceElement::mediaQueryMatches() const
144{
145    if (!m_mediaQueryList)
146        return true;
147
148    return m_mediaQueryList->matches();
149}
150
151bool HTMLSourceElement::isURLAttribute(const Attribute& attribute) const
152{
153    return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
154}
155
156void HTMLSourceElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
157{
158    HTMLElement::parseAttribute(name, value);
159    if (name == mediaAttr) {
160        if (m_mediaQueryList)
161            m_mediaQueryList->removeListener(m_listener);
162        RefPtrWillBeRawPtr<MediaQuerySet> set = MediaQuerySet::create(value);
163        m_mediaQueryList = MediaQueryList::create(&document(), &document().mediaQueryMatcher(), set.release());
164        m_mediaQueryList->addListener(m_listener);
165    }
166    if (name == srcsetAttr || name == sizesAttr || name == mediaAttr || name == typeAttr) {
167        Element* parent = parentElement();
168        if (isHTMLPictureElement(parent))
169            toHTMLPictureElement(parent)->sourceOrMediaChanged();
170    }
171}
172
173void HTMLSourceElement::notifyMediaQueryChanged()
174{
175    Element* parent = parentElement();
176    if (isHTMLPictureElement(parent))
177        toHTMLPictureElement(parent)->sourceOrMediaChanged();
178}
179
180void HTMLSourceElement::trace(Visitor* visitor)
181{
182    visitor->trace(m_mediaQueryList);
183    visitor->trace(m_listener);
184    HTMLElement::trace(visitor);
185}
186
187}
188