1/*
2 * Copyright (C) 2007-2018 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 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 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 "HTMLSourceElement.h"
28
29#include "Event.h"
30#include "EventNames.h"
31#include "HTMLNames.h"
32#include "HTMLPictureElement.h"
33#include "Logging.h"
34#include "MediaList.h"
35#include "MediaQueryParser.h"
36#include <wtf/IsoMallocInlines.h>
37
38#if ENABLE(VIDEO)
39#include "HTMLMediaElement.h"
40#endif
41
42namespace WebCore {
43
44WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLSourceElement);
45
46using namespace HTMLNames;
47
48inline HTMLSourceElement::HTMLSourceElement(const QualifiedName& tagName, Document& document)
49 : HTMLElement(tagName, document)
50 , ActiveDOMObject(document)
51 , m_errorEventTimer(*this, &HTMLSourceElement::errorEventTimerFired)
52{
53 LOG(Media, "HTMLSourceElement::HTMLSourceElement - %p", this);
54 ASSERT(hasTagName(sourceTag));
55}
56
57Ref<HTMLSourceElement> HTMLSourceElement::create(const QualifiedName& tagName, Document& document)
58{
59 auto sourceElement = adoptRef(*new HTMLSourceElement(tagName, document));
60 sourceElement->suspendIfNeeded();
61 return sourceElement;
62}
63
64Ref<HTMLSourceElement> HTMLSourceElement::create(Document& document)
65{
66 return create(sourceTag, document);
67}
68
69Node::InsertedIntoAncestorResult HTMLSourceElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
70{
71 HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
72 RefPtr<Element> parent = parentElement();
73 if (parent == &parentOfInsertedTree) {
74#if ENABLE(VIDEO)
75 if (is<HTMLMediaElement>(*parent))
76 downcast<HTMLMediaElement>(*parent).sourceWasAdded(*this);
77 else
78#endif
79 if (is<HTMLPictureElement>(*parent))
80 downcast<HTMLPictureElement>(*parent).sourcesChanged();
81 }
82 return InsertedIntoAncestorResult::Done;
83}
84
85void HTMLSourceElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
86{
87 HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
88 if (!parentNode() && is<Element>(oldParentOfRemovedTree)) {
89#if ENABLE(VIDEO)
90 if (is<HTMLMediaElement>(oldParentOfRemovedTree))
91 downcast<HTMLMediaElement>(oldParentOfRemovedTree).sourceWasRemoved(*this);
92 else
93#endif
94 if (is<HTMLPictureElement>(oldParentOfRemovedTree))
95 downcast<HTMLPictureElement>(oldParentOfRemovedTree).sourcesChanged();
96 }
97}
98
99void HTMLSourceElement::scheduleErrorEvent()
100{
101 LOG(Media, "HTMLSourceElement::scheduleErrorEvent - %p", this);
102 if (m_errorEventTimer.isActive())
103 return;
104
105 m_errorEventTimer.startOneShot(0_s);
106}
107
108void HTMLSourceElement::cancelPendingErrorEvent()
109{
110 LOG(Media, "HTMLSourceElement::cancelPendingErrorEvent - %p", this);
111 m_errorEventTimer.stop();
112}
113
114void HTMLSourceElement::errorEventTimerFired()
115{
116 LOG(Media, "HTMLSourceElement::errorEventTimerFired - %p", this);
117 dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::Yes));
118}
119
120bool HTMLSourceElement::isURLAttribute(const Attribute& attribute) const
121{
122 return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
123}
124
125const char* HTMLSourceElement::activeDOMObjectName() const
126{
127 return "HTMLSourceElement";
128}
129
130bool HTMLSourceElement::canSuspendForDocumentSuspension() const
131{
132 return true;
133}
134
135void HTMLSourceElement::suspend(ReasonForSuspension reason)
136{
137 // FIXME: Shouldn't this also stop the timer for PageWillBeSuspended?
138 if (reason == ReasonForSuspension::PageCache) {
139 m_shouldRescheduleErrorEventOnResume = m_errorEventTimer.isActive();
140 m_errorEventTimer.stop();
141 }
142}
143
144void HTMLSourceElement::resume()
145{
146 if (m_shouldRescheduleErrorEventOnResume) {
147 m_errorEventTimer.startOneShot(0_s);
148 m_shouldRescheduleErrorEventOnResume = false;
149 }
150}
151
152void HTMLSourceElement::stop()
153{
154 cancelPendingErrorEvent();
155}
156
157void HTMLSourceElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
158{
159 HTMLElement::parseAttribute(name, value);
160 if (name == srcsetAttr || name == sizesAttr || name == mediaAttr || name == typeAttr) {
161 if (name == mediaAttr)
162 m_cachedParsedMediaAttribute = WTF::nullopt;
163 auto parent = makeRefPtr(parentNode());
164 if (is<HTMLPictureElement>(parent))
165 downcast<HTMLPictureElement>(*parent).sourcesChanged();
166 }
167}
168
169const MediaQuerySet* HTMLSourceElement::parsedMediaAttribute(Document& document) const
170{
171 if (!m_cachedParsedMediaAttribute) {
172 RefPtr<const MediaQuerySet> parsedAttribute;
173 auto& value = attributeWithoutSynchronization(mediaAttr);
174 if (!value.isNull())
175 parsedAttribute = MediaQuerySet::create(value, MediaQueryParserContext(document));
176 m_cachedParsedMediaAttribute = WTFMove(parsedAttribute);
177 }
178 return m_cachedParsedMediaAttribute.value().get();
179}
180
181}
182