HTMLMediaElement.cpp 160 KB
Newer Older
antti's avatar
antti committed
1
/*
2
 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
antti's avatar
antti committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#if ENABLE(VIDEO)
#include "HTMLMediaElement.h"

30 31
#include "ApplicationCacheHost.h"
#include "ApplicationCacheResource.h"
32
#include "Attribute.h"
33 34
#include "Chrome.h"
#include "ChromeClient.h"
35 36
#include "ClientRect.h"
#include "ClientRectList.h"
37
#include "ContentSecurityPolicy.h"
darin@apple.com's avatar
darin@apple.com committed
38
#include "ContentType.h"
39 40
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
41
#include "DiagnosticLoggingKeys.h"
42
#include "DocumentLoader.h"
43
#include "ElementShadow.h"
antti@apple.com's avatar
antti@apple.com committed
44
#include "Event.h"
antti's avatar
antti committed
45 46
#include "EventNames.h"
#include "ExceptionCode.h"
47
#include "ExceptionCodePlaceholder.h"
48 49
#include "Frame.h"
#include "FrameLoader.h"
50
#include "FrameLoaderClient.h"
51
#include "FrameView.h"
antti's avatar
antti committed
52 53 54
#include "HTMLDocument.h"
#include "HTMLNames.h"
#include "HTMLSourceElement.h"
adele's avatar
adele committed
55
#include "HTMLVideoElement.h"
56
#include "Language.h"
57
#include "Logging.h"
58
#include "MediaController.h"
59
#include "MediaControls.h"
60
#include "MediaDocument.h"
antti's avatar
antti committed
61
#include "MediaError.h"
62
#include "MediaFragmentURIParser.h"
63 64
#include "MediaKeyError.h"
#include "MediaKeyEvent.h"
antti's avatar
antti committed
65
#include "MediaList.h"
antti@apple.com's avatar
antti@apple.com committed
66
#include "MediaPlayer.h"
darin@apple.com's avatar
darin@apple.com committed
67
#include "MediaQueryEvaluator.h"
68
#include "MouseEvent.h"
69
#include "MIMETypeRegistry.h"
70
#include "NodeRenderingContext.h"
mitz@apple.com's avatar
mitz@apple.com committed
71
#include "Page.h"
72
#include "PageGroup.h"
antti's avatar
antti committed
73
#include "RenderVideo.h"
74
#include "RenderView.h"
75
#include "ScriptController.h"
76
#include "ScriptEventListener.h"
77
#include "SecurityOrigin.h"
78
#include "SecurityPolicy.h"
79
#include "Settings.h"
80
#include "ShadowRoot.h"
antti's avatar
antti committed
81
#include "TimeRanges.h"
82
#include "WebCoreMemoryInstrumentation.h"
darin@apple.com's avatar
darin@apple.com committed
83 84 85
#include <limits>
#include <wtf/CurrentTime.h>
#include <wtf/MathExtras.h>
86
#include <wtf/MemoryInstrumentationVector.h>
87
#include <wtf/NonCopyingSort.h>
88
#include <wtf/Uint8Array.h>
89
#include <wtf/text/CString.h>
darin@apple.com's avatar
darin@apple.com committed
90

91 92 93 94
#if USE(ACCELERATED_COMPOSITING)
#include "RenderLayerCompositor.h"
#endif

95
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
96
#include "RenderEmbeddedObject.h"
97 98
#include "Widget.h"
#endif
antti's avatar
antti committed
99

100
#if ENABLE(VIDEO_TRACK)
101
#include "CaptionUserPreferences.h"
102
#include "HTMLTrackElement.h"
103 104
#include "InbandTextTrack.h"
#include "InbandTextTrackPrivate.h"
105
#include "RuntimeEnabledFeatures.h"
106
#include "TextTrackCueList.h"
107
#include "TextTrackList.h"
108 109
#endif

110
#if ENABLE(WEB_AUDIO)
111
#include "AudioSourceProvider.h"
112 113 114
#include "MediaElementAudioSourceNode.h"
#endif

115 116 117 118
#if PLATFORM(MAC)
#include "DisplaySleepDisabler.h"
#endif

119 120 121 122 123
#if ENABLE(MEDIA_SOURCE)
#include "MediaSource.h"
#include "MediaSourceRegistry.h"
#endif

124 125 126 127
#if ENABLE(MEDIA_STREAM)
#include "MediaStreamRegistry.h"
#endif

128 129 130 131 132
#if ENABLE(ENCRYPTED_MEDIA_V2)
#include "MediaKeyNeededEvent.h"
#include "MediaKeys.h"
#endif

133 134 135 136
#if USE(PLATFORM_TEXT_TRACK_MENU)
#include "PlatformTextTrack.h"
#endif

antti's avatar
antti committed
137 138 139 140
using namespace std;

namespace WebCore {

141
#if !LOG_DISABLED
roger_fong@apple.com's avatar
 
roger_fong@apple.com committed
142
static String urlForLoggingMedia(const KURL& url)
143
{
144
    static const unsigned maximumURLLengthForLogging = 128;
145

146
    if (url.string().length() < maximumURLLengthForLogging)
147 148
        return url.string();
    return url.string().substring(0, maximumURLLengthForLogging) + "...";
149 150
}

151
static const char* boolString(bool val)
152 153 154 155 156 157 158 159 160 161 162
{
    return val ? "true" : "false";
}
#endif

#ifndef LOG_MEDIA_EVENTS
// Default to not logging events because so many are generated they can overwhelm the rest of 
// the logging.
#define LOG_MEDIA_EVENTS 0
#endif

163 164 165 166 167 168
#ifndef LOG_CACHED_TIME_WARNINGS
// Default to not logging warnings about excessive drift in the cached media time because it adds a
// fair amount of overhead and logging.
#define LOG_CACHED_TIME_WARNINGS 0
#endif

169 170
#if ENABLE(MEDIA_SOURCE)
// URL protocol used to signal that the media source API is being used.
171
static const char* mediaSourceBlobProtocol = "blob";
172 173
#endif

antti's avatar
antti committed
174
using namespace HTMLNames;
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
using namespace std;

typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap;
static DocumentElementSetMap& documentToElementSetMap()
{
    DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ());
    return map;
}

static void addElementToDocumentMap(HTMLMediaElement* element, Document* document)
{
    DocumentElementSetMap& map = documentToElementSetMap();
    HashSet<HTMLMediaElement*> set = map.take(document);
    set.add(element);
    map.add(document, set);
}

static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document)
{
    DocumentElementSetMap& map = documentToElementSetMap();
    HashSet<HTMLMediaElement*> set = map.take(document);
    set.remove(element);
    if (!set.isEmpty())
        map.add(document, set);
}
antti's avatar
antti committed
200

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
#if ENABLE(ENCRYPTED_MEDIA)
static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception)
{
    switch (exception) {
    case MediaPlayer::NoError:
        return 0;
    case MediaPlayer::InvalidPlayerState:
        return INVALID_STATE_ERR;
    case MediaPlayer::KeySystemNotSupported:
        return NOT_SUPPORTED_ERR;
    }

    ASSERT_NOT_REACHED();
    return INVALID_STATE_ERR;
}
#endif

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
#if ENABLE(VIDEO_TRACK)
class TrackDisplayUpdateScope {
public:
    TrackDisplayUpdateScope(HTMLMediaElement* mediaElement)
    {
        m_mediaElement = mediaElement;
        m_mediaElement->beginIgnoringTrackDisplayUpdateRequests();
    }
    ~TrackDisplayUpdateScope()
    {
        ASSERT(m_mediaElement);
        m_mediaElement->endIgnoringTrackDisplayUpdateRequests();
    }
    
private:
    HTMLMediaElement* m_mediaElement;
};
#endif

237
HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document, bool createdByParser)
238
    : HTMLElement(tagName, document)
239
    , ActiveDOMObject(document)
antti's avatar
antti committed
240 241
    , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
    , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
242
    , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
243
    , m_playedTimeRanges()
244
    , m_asyncEventQueue(GenericEventQueue::create(this))
245
    , m_playbackRate(1.0f)
antti's avatar
antti committed
246
    , m_defaultPlaybackRate(1.0f)
247
    , m_webkitPreservesPitch(true)
248 249
    , m_networkState(NETWORK_EMPTY)
    , m_readyState(HAVE_NOTHING)
250
    , m_readyStateMaximum(HAVE_NOTHING)
adele@apple.com's avatar
adele@apple.com committed
251
    , m_volume(1.0f)
252
    , m_lastSeekTime(0)
antti's avatar
antti committed
253
    , m_previousProgressTime(numeric_limits<double>::max())
254 255 256
    , m_lastTimeUpdateEventWallTime(0)
    , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
    , m_loadState(WaitingForSource)
257 258 259
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    , m_proxyWidget(0)
#endif
260
    , m_restrictions(RequireUserGestureForFullscreenRestriction | RequirePageConsentToLoadMediaRestriction)
261
    , m_preload(MediaPlayer::Auto)
262
    , m_displayMode(Unknown)
263
    , m_processingMediaPlayerCallback(0)
264
    , m_cachedTime(MediaPlayer::invalidTime())
265 266
    , m_cachedTimeWallClockUpdateTime(0)
    , m_minimumWallClockTimeToCacheMediaTime(0)
267 268
    , m_fragmentStartTime(MediaPlayer::invalidTime())
    , m_fragmentEndTime(MediaPlayer::invalidTime())
269
    , m_pendingActionFlags(0)
270
    , m_playing(false)
271
    , m_isWaitingUntilMediaCanStart(false)
272
    , m_shouldDelayLoadEvent(false)
273 274 275 276 277 278 279 280 281
    , m_haveFiredLoadedData(false)
    , m_inActiveDocument(true)
    , m_autoplaying(true)
    , m_muted(false)
    , m_paused(true)
    , m_seeking(false)
    , m_sentStalledEvent(false)
    , m_sentEndEvent(false)
    , m_pausedInternal(false)
282
    , m_sendProgressEvents(true)
283
    , m_isFullscreen(false)
284
    , m_closedCaptionsVisible(false)
285 286 287
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    , m_needWidgetUpdate(false)
#endif
288
    , m_dispatchingCanPlayEvent(false)
289
    , m_loadInitiatedByUserGesture(false)
290
    , m_completelyLoaded(false)
291
    , m_havePreparedToPlay(false)
292
    , m_parsingInProgress(createdByParser)
293
#if ENABLE(VIDEO_TRACK)
294
    , m_tracksAreReady(true)
eric.carlson@apple.com's avatar
eric.carlson@apple.com committed
295
    , m_haveVisibleTextTrack(false)
296
    , m_processingPreferenceChange(false)
297
    , m_lastTextTrackUpdateTime(-1)
298
    , m_textTracks(0)
299
    , m_ignoreTrackDisplayUpdate(0)
300
#endif
301 302 303
#if ENABLE(WEB_AUDIO)
    , m_audioSourceNode(0)
#endif
antti's avatar
antti committed
304
{
305
    LOG(Media, "HTMLMediaElement::HTMLMediaElement");
306
    document->registerForMediaVolumeCallbacks(this);
307
    document->registerForPrivateBrowsingStateChangedCallbacks(this);
308

309
    if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture()) {
310
        addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
311 312
        addBehaviorRestriction(RequireUserGestureForLoadRestriction);
    }
313

314
    setHasCustomStyleCallbacks();
315
    addElementToDocumentMap(this, document);
316 317

#if ENABLE(VIDEO_TRACK)
318
    document->registerForCaptionPreferencesChangedCallbacks(this);
319
#endif
antti's avatar
antti committed
320 321 322 323
}

HTMLMediaElement::~HTMLMediaElement()
{
324
    LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
325 326
    if (m_isWaitingUntilMediaCanStart)
        document()->removeMediaCanStartListener(this);
327
    setShouldDelayLoadEvent(false);
mitz@apple.com's avatar
mitz@apple.com committed
328
    document()->unregisterForMediaVolumeCallbacks(this);
329
    document()->unregisterForPrivateBrowsingStateChangedCallbacks(this);
330
#if ENABLE(VIDEO_TRACK)
331
    document()->unregisterForCaptionPreferencesChangedCallbacks(this);
332 333 334 335 336 337
    if (m_textTracks)
        m_textTracks->clearOwner();
    if (m_textTracks) {
        for (unsigned i = 0; i < m_textTracks->length(); ++i)
            m_textTracks->item(i)->clearClient();
    }
338
#endif
339 340 341 342

    if (m_mediaController)
        m_mediaController->removeMediaElement(this);

343 344 345 346
#if ENABLE(MEDIA_SOURCE)
    setSourceState(MediaSource::closedKeyword());
#endif

347 348 349 350
#if ENABLE(ENCRYPTED_MEDIA_V2)
    setMediaKeys(0);
#endif

351
    removeElementFromDocumentMap(this, document());
antti's avatar
antti committed
352 353
}

354
void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument)
355
{
356 357 358
    if (m_isWaitingUntilMediaCanStart) {
        if (oldDocument)
            oldDocument->removeMediaCanStartListener(this);
359
        document()->addMediaCanStartListener(this);
360 361 362 363 364 365 366 367 368 369 370 371 372
    }

    if (m_shouldDelayLoadEvent) {
        if (oldDocument)
            oldDocument->decrementLoadEventDelayCount();
        document()->incrementLoadEventDelayCount();
    }

    if (oldDocument) {
        oldDocument->unregisterForMediaVolumeCallbacks(this);
        removeElementFromDocumentMap(this, oldDocument);
    }

373
    document()->registerForMediaVolumeCallbacks(this);
374
    addElementToDocumentMap(this, document());
375 376

    HTMLElement::didMoveToNewDocument(oldDocument);
377 378
}

379 380 381 382 383
bool HTMLMediaElement::hasCustomFocusLogic() const
{
    return true;
}

384 385
bool HTMLMediaElement::supportsFocus() const
{
386 387 388
    if (ownerDocument()->isMediaDocument())
        return false;

389 390 391 392
    // If no controls specified, we should still be able to focus the element if it has tabIndex.
    return controls() ||  HTMLElement::supportsFocus();
}

393 394 395 396 397
bool HTMLMediaElement::isMouseFocusable() const
{
    return false;
}

398
void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
antti's avatar
antti committed
399
{
400
    if (name == srcAttr) {
401
        // Trigger a reload, as long as the 'src' attribute is present.
402
        if (!value.isNull()) {
403 404
            clearMediaPlayer(LoadMediaResource);
            scheduleDelayedAction(LoadMediaResource);
405
        }
406
    } else if (name == controlsAttr)
407
        configureMediaControls();
408
#if PLATFORM(MAC)
409
    else if (name == loopAttr)
410 411
        updateDisableSleep();
#endif
412 413
    else if (name == preloadAttr) {
        if (equalIgnoringCase(value, "none"))
414
            m_preload = MediaPlayer::None;
415
        else if (equalIgnoringCase(value, "metadata"))
416 417 418 419 420 421 422 423 424 425 426
            m_preload = MediaPlayer::MetaData;
        else {
            // The spec does not define an "invalid value default" but "auto" is suggested as the
            // "missing value default", so use it for everything except "none" and "metadata"
            m_preload = MediaPlayer::Auto;
        }

        // The attribute must be ignored if the autoplay attribute is present
        if (!autoplay() && m_player)
            m_player->setPreload(m_preload);

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
    } else if (name == mediagroupAttr)
        setMediaGroup(value);
    else if (name == onabortAttr)
        setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, name, value));
    else if (name == onbeforeloadAttr)
        setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, name, value));
    else if (name == oncanplayAttr)
        setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, name, value));
    else if (name == oncanplaythroughAttr)
        setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, name, value));
    else if (name == ondurationchangeAttr)
        setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, name, value));
    else if (name == onemptiedAttr)
        setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, name, value));
    else if (name == onendedAttr)
        setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, name, value));
    else if (name == onerrorAttr)
        setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, name, value));
    else if (name == onloadeddataAttr)
        setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, name, value));
    else if (name == onloadedmetadataAttr)
        setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, name, value));
    else if (name == onloadstartAttr)
        setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, name, value));
    else if (name == onpauseAttr)
        setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, name, value));
    else if (name == onplayAttr)
        setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, name, value));
    else if (name == onplayingAttr)
        setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, name, value));
    else if (name == onprogressAttr)
        setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, name, value));
    else if (name == onratechangeAttr)
        setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, name, value));
    else if (name == onseekedAttr)
        setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, name, value));
    else if (name == onseekingAttr)
        setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, name, value));
    else if (name == onstalledAttr)
        setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, name, value));
    else if (name == onsuspendAttr)
        setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, name, value));
    else if (name == ontimeupdateAttr)
        setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, name, value));
    else if (name == onvolumechangeAttr)
        setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, name, value));
    else if (name == onwaitingAttr)
        setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, name, value));
    else if (name == onwebkitbeginfullscreenAttr)
        setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, name, value));
    else if (name == onwebkitendfullscreenAttr)
        setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, name, value));
479
    else
480
        HTMLElement::parseAttribute(name, value);
481 482
}

483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
void HTMLMediaElement::finishParsingChildren()
{
    HTMLElement::finishParsingChildren();
    m_parsingInProgress = false;

#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    document()->updateStyleIfNeeded();
    createMediaPlayerProxy();
#endif
    
#if ENABLE(VIDEO_TRACK)
    if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
        return;
    
    for (Node* node = firstChild(); node; node = node->nextSibling()) {
        if (node->hasTagName(trackTag)) {
499
            scheduleDelayedAction(LoadTextTrackResource);
500 501 502 503 504 505
            break;
        }
    }
#endif
}

506
bool HTMLMediaElement::rendererIsNeeded(const NodeRenderingContext& context)
adele@apple.com's avatar
adele@apple.com committed
507
{
508
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
509
    UNUSED_PARAM(context);
510 511 512 513 514 515
    Frame* frame = document()->frame();
    if (!frame)
        return false;

    return true;
#else
516
    return controls() ? HTMLElement::rendererIsNeeded(context) : false;
517
#endif
adele@apple.com's avatar
adele@apple.com committed
518 519 520 521
}

RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
{
522
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
523 524
    // Setup the renderer if we already have a proxy widget.
    RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this);
525
    if (m_proxyWidget) {
526
        mediaRenderer->setWidget(m_proxyWidget);
527

528
        if (Frame* frame = document()->frame())
529
            frame->loader()->client()->showMediaPlayerProxyPlugin(m_proxyWidget.get());
530
    }
531
    return mediaRenderer;
532
#else
adele@apple.com's avatar
adele@apple.com committed
533
    return new (arena) RenderMedia(this);
534
#endif
adele@apple.com's avatar
adele@apple.com committed
535
}
536 537 538

bool HTMLMediaElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
{
539 540
    if (!hasMediaControls())
        return false;
541 542 543 544
    // <media> doesn't allow its content, including shadow subtree, to
    // be rendered. So this should return false for most of the children.
    // One exception is a shadow tree built for rendering controls which should be visible.
    // So we let them go here by comparing its subtree root with one of the controls.
545 546
    return (mediaControls()->treeScope() == childContext.node()->treeScope()
            && childContext.isOnUpperEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext));
547 548
}

549
Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode* insertionPoint)
antti's avatar
antti committed
550
{
551 552 553
    LOG(Media, "HTMLMediaElement::insertedInto");
    HTMLElement::insertedInto(insertionPoint);
    if (insertionPoint->inDocument() && !getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
554
        scheduleDelayedAction(LoadMediaResource);
555
    configureMediaControls();
556
    return InsertionDone;
antti's avatar
antti committed
557 558
}

559
void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint)
antti's avatar
antti committed
560
{
561 562 563 564 565 566 567 568 569 570
    if (insertionPoint->inDocument()) {
        LOG(Media, "HTMLMediaElement::removedFromDocument");
        configureMediaControls();
        if (m_networkState > NETWORK_EMPTY)
            pause();
        if (m_isFullscreen)
            exitFullscreen();
    }

    HTMLElement::removedFrom(insertionPoint);
antti's avatar
antti committed
571 572
}

adele@apple.com's avatar
adele@apple.com committed
573 574 575 576
void HTMLMediaElement::attach()
{
    ASSERT(!attached());

577 578 579 580
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    m_needWidgetUpdate = true;
#endif

adele@apple.com's avatar
adele@apple.com committed
581 582 583 584
    HTMLElement::attach();

    if (renderer())
        renderer()->updateFromElement();
585 586
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    else if (m_proxyWidget) {
587
        if (Frame* frame = document()->frame())
588
            frame->loader()->client()->hideMediaPlayerProxyPlugin(m_proxyWidget.get());
589 590
    }
#endif
adele@apple.com's avatar
adele@apple.com committed
591 592
}

antti@apple.com's avatar
antti@apple.com committed
593
void HTMLMediaElement::didRecalcStyle(StyleChange)
594 595 596 597 598
{
    if (renderer())
        renderer()->updateFromElement();
}

599
void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
antti's avatar
antti committed
600
{
601
    LOG(Media, "HTMLMediaElement::scheduleLoad");
602

603
    if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaResource)) {
604
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
605
        createMediaPlayerProxy();
606
#endif
607 608
        
        prepareForLoad();
609
        m_pendingActionFlags |= LoadMediaResource;
610
    }
611

612
#if ENABLE(VIDEO_TRACK)
613 614 615 616 617 618 619
    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (actionType & LoadTextTrackResource))
        m_pendingActionFlags |= LoadTextTrackResource;
#endif

#if USE(PLATFORM_TEXT_TRACK_MENU)
    if (actionType & TextTrackChangesNotification)
        m_pendingActionFlags |= TextTrackChangesNotification;
620 621 622 623
#endif

    if (!m_loadTimer.isActive())
        m_loadTimer.startOneShot(0);
624 625 626 627 628
}

void HTMLMediaElement::scheduleNextSourceChild()
{
    // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
629
    m_pendingActionFlags |= LoadMediaResource;
antti's avatar
antti committed
630 631 632
    m_loadTimer.startOneShot(0);
}

633
void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
antti's avatar
antti committed
634
{
635 636 637
#if LOG_MEDIA_EVENTS
    LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
#endif
638 639
    RefPtr<Event> event = Event::create(eventName, false, true);
    event->setTarget(this);
640

641
    m_asyncEventQueue->enqueueEvent(event.release());
antti's avatar
antti committed
642 643
}

644
void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
antti's avatar
antti committed
645
{
646 647
    RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.

648
#if ENABLE(VIDEO_TRACK)
649
    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & LoadTextTrackResource))
650
        configureTextTracks();
651 652
#endif

653
    if (m_pendingActionFlags & LoadMediaResource) {
654 655 656 657 658 659
        if (m_loadState == LoadingFromSourceElement)
            loadNextSourceChild();
        else
            loadInternal();
    }

660 661 662 663 664 665
#if USE(PLATFORM_TEXT_TRACK_MENU)
    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & TextTrackChangesNotification))
        notifyMediaPlayerOfTextTrackChanges();
#endif

    m_pendingActionFlags = 0;
antti's avatar
antti committed
666 667 668 669 670 671 672
}

PassRefPtr<MediaError> HTMLMediaElement::error() const 
{
    return m_error;
}

673
void HTMLMediaElement::setSrc(const String& url)
antti's avatar
antti committed
674 675 676 677 678 679 680 681 682
{
    setAttribute(srcAttr, url);
}

HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
{
    return m_networkState;
}

683
String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const KURL& url) const
684
{
685
    MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType), keySystem, url, this);
686 687 688 689 690 691
    String canPlay;

    // 4.8.10.3
    switch (support)
    {
        case MediaPlayer::IsNotSupported:
692
            canPlay = emptyString();
693 694
            break;
        case MediaPlayer::MayBeSupported:
695
            canPlay = ASCIILiteral("maybe");
696 697
            break;
        case MediaPlayer::IsSupported:
698
            canPlay = ASCIILiteral("probably");
699 700 701
            break;
    }
    
702
    LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.elidedString().utf8().data(), canPlay.utf8().data());
703

704 705 706
    return canPlay;
}

707
void HTMLMediaElement::load()
antti's avatar
antti committed
708
{
709 710
    RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
    
711
    LOG(Media, "HTMLMediaElement::load()");
712
    
713
    if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture())
714 715 716 717 718 719 720
        return;
    
    m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
    if (m_loadInitiatedByUserGesture)
        removeBehaviorsRestrictionsAfterFirstUserGesture();
    prepareForLoad();
    loadInternal();
721
    prepareToPlay();
722 723
}

724
void HTMLMediaElement::prepareForLoad()
725
{
726 727
    LOG(Media, "HTMLMediaElement::prepareForLoad");

728
    // Perform the cleanup required for the resource load algorithm to run.
729
    stopPeriodicTimers();
730
    m_loadTimer.stop();
731
    m_sentEndEvent = false;
732 733
    m_sentStalledEvent = false;
    m_haveFiredLoadedData = false;
734
    m_completelyLoaded = false;
735
    m_havePreparedToPlay = false;
736
    m_displayMode = Unknown;
737

738
    // 1 - Abort any already-running instance of the resource selection algorithm for this element.
739
    m_loadState = WaitingForSource;
740 741
    m_currentSourceNode = 0;

742
    // 2 - If there are any tasks from the media element's media element event task source in 
743
    // one of the task queues, then remove those tasks.
744
    cancelPendingEventsAndCallbacks();
745

746 747 748 749
    // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
    // a task to fire a simple event named abort at the media element.
    if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
        scheduleEvent(eventNames().abortEvent);
750

751 752 753 754
#if ENABLE(MEDIA_SOURCE)
    setSourceState(MediaSource::closedKeyword());
#endif

755
#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
756
    createMediaPlayer();
757
#else
758 759 760 761
    if (m_player)
        m_player->cancelLoad();
    else
        createMediaPlayerProxy();
762 763 764
#endif

    // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
765 766 767
    if (m_networkState != NETWORK_EMPTY) {
        m_networkState = NETWORK_EMPTY;
        m_readyState = HAVE_NOTHING;
768
        m_readyStateMaximum = HAVE_NOTHING;
769
        refreshCachedTime();
antti@apple.com's avatar
antti@apple.com committed
770
        m_paused = true;
antti@apple.com's avatar
antti@apple.com committed
771
        m_seeking = false;
772
        invalidateCachedTime();
773
        scheduleEvent(eventNames().emptiedEvent);
774
        updateMediaController();
775
#if ENABLE(VIDEO_TRACK)
776 777
        if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
            updateActiveTextTrackCues(0);
778
#endif
antti's avatar
antti committed
779
    }
780

781 782 783 784 785 786 787
    // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
    setPlaybackRate(defaultPlaybackRate());

    // 6 - Set the error attribute to null and the autoplaying flag to true.
    m_error = 0;
    m_autoplaying = true;

788 789 790 791 792 793 794 795 796 797
    // 7 - Invoke the media element's resource selection algorithm.

    // 8 - Note: Playback of any previously playing media resource for this element stops.

    // The resource selection algorithm
    // 1 - Set the networkState to NETWORK_NO_SOURCE
    m_networkState = NETWORK_NO_SOURCE;

    // 2 - Asynchronously await a stable state.

798 799
    m_playedTimeRanges = TimeRanges::create();
    m_lastSeekTime = 0;
800 801 802 803 804

    // The spec doesn't say to block the load event until we actually run the asynchronous section
    // algorithm, but do it now because we won't start that until after the timer fires and the 
    // event may have already fired by then.
    setShouldDelayLoadEvent(true);
805 806

    configureMediaControls();
807 808 809 810
}

void HTMLMediaElement::loadInternal()
{
811 812 813
    // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
    // us catch those bugs more quickly without needing all the branches to align to actually
    // trigger the event.
814
    ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
815

816 817
    // If we can't start a load right away, start it later.
    Page* page = document()->page();
818
    if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) {
819
        setShouldDelayLoadEvent(false);
820 821
        if (m_isWaitingUntilMediaCanStart)
            return;
822
        document()->addMediaCanStartListener(this);
823 824 825
        m_isWaitingUntilMediaCanStart = true;
        return;
    }
826 827 828 829
    
    // Once the page has allowed an element to load media, it is free to load at will. This allows a 
    // playlist that starts in a foreground tab to continue automatically if the tab is subsequently 
    // put in the the background.
830
    removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
831

832 833 834
#if ENABLE(VIDEO_TRACK)
    // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
    // disabled state when the element's resource selection algorithm last started".
835 836 837 838 839
    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
        m_textTracksWhenResourceSelectionBegan.clear();
        if (m_textTracks) {
            for (unsigned i = 0; i < m_textTracks->length(); ++i) {
                TextTrack* track = m_textTracks->item(i);
840
                if (track->mode() != TextTrack::disabledKeyword())
841 842
                    m_textTracksWhenResourceSelectionBegan.append(track);
            }
843 844 845 846
        }
    }
#endif

847 848 849 850 851
    selectMediaResource();
}

void HTMLMediaElement::selectMediaResource()
{
852 853
    LOG(Media, "HTMLMediaElement::selectMediaResource");

854 855
    enum Mode { attribute, children };

856 857
    // 3 - If the media element has a src attribute, then let mode be attribute.
    Mode mode = attribute;
858
    if (!fastHasAttribute(srcAttr)) {
859 860 861 862 863
        Node* node;
        for (node = firstChild(); node; node = node->nextSibling()) {
            if (node->hasTagName(sourceTag))
                break;
        }
864

865 866 867 868 869
        // Otherwise, if the media element does not have a src attribute but has a source 
        // element child, then let mode be children and let candidate be the first such 
        // source element child in tree order.
        if (node) {
            mode = children;
870
            m_nextChildNodeToConsider = node;
871 872 873 874 875
            m_currentSourceNode = 0;
        } else {
            // Otherwise the media element has neither a src attribute nor a source element 
            // child: set the networkState to NETWORK_EMPTY, and abort these steps; the 
            // synchronous section ends.
876
            m_loadState = WaitingForSource;
877
            setShouldDelayLoadEvent(false);
878
            m_networkState = NETWORK_EMPTY;
879 880

            LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
881 882
            return;
        }
883 884
    }

885 886 887
    // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event), 
    // and set its networkState to NETWORK_LOADING.
    setShouldDelayLoadEvent(true);
888 889
    m_networkState = NETWORK_LOADING;

890
    // 5 - Queue a task to fire a simple event named loadstart at the media element.
eric.carlson@apple.com's avatar