Home | History | Annotate | Download | only in loader
      1 /*
      2  * Copyright (C) 2008 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 
     28 #if ENABLE(VIDEO)
     29 #include "MediaDocument.h"
     30 
     31 #include "DocumentLoader.h"
     32 #include "Element.h"
     33 #include "Event.h"
     34 #include "EventNames.h"
     35 #include "Frame.h"
     36 #include "FrameLoader.h"
     37 #include "FrameLoaderClient.h"
     38 #include "HTMLEmbedElement.h"
     39 #include "HTMLNames.h"
     40 #include "HTMLVideoElement.h"
     41 #include "KeyboardEvent.h"
     42 #include "MainResourceLoader.h"
     43 #include "NodeList.h"
     44 #include "Page.h"
     45 #include "SegmentedString.h"
     46 #include "Settings.h"
     47 #include "Text.h"
     48 #include "XMLTokenizer.h"
     49 
     50 namespace WebCore {
     51 
     52 using namespace HTMLNames;
     53 
     54 class MediaTokenizer : public Tokenizer {
     55 public:
     56     MediaTokenizer(Document* doc) : m_doc(doc), m_mediaElement(0) {}
     57 
     58 private:
     59     virtual void write(const SegmentedString&, bool appendData);
     60     virtual void stopParsing();
     61     virtual void finish();
     62     virtual bool isWaitingForScripts() const;
     63 
     64     virtual bool wantsRawData() const { return true; }
     65     virtual bool writeRawData(const char* data, int len);
     66 
     67     void createDocumentStructure();
     68 
     69     Document* m_doc;
     70     HTMLMediaElement* m_mediaElement;
     71 };
     72 
     73 void MediaTokenizer::write(const SegmentedString&, bool)
     74 {
     75     ASSERT_NOT_REACHED();
     76 }
     77 
     78 void MediaTokenizer::createDocumentStructure()
     79 {
     80     ExceptionCode ec;
     81     RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false);
     82     m_doc->appendChild(rootElement, ec);
     83 
     84     RefPtr<Element> body = m_doc->createElement(bodyTag, false);
     85     body->setAttribute(styleAttr, "background-color: rgb(38,38,38);");
     86 
     87     rootElement->appendChild(body, ec);
     88 
     89     RefPtr<Element> mediaElement = m_doc->createElement(videoTag, false);
     90 
     91     m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get());
     92     m_mediaElement->setAttribute(controlsAttr, "");
     93     m_mediaElement->setAttribute(autoplayAttr, "");
     94     m_mediaElement->setAttribute(styleAttr, "margin: auto; position: absolute; top: 0; right: 0; bottom: 0; left: 0;");
     95 
     96     m_mediaElement->setAttribute(nameAttr, "media");
     97     m_mediaElement->setSrc(m_doc->url());
     98 
     99     body->appendChild(mediaElement, ec);
    100 
    101     Frame* frame = m_doc->frame();
    102     if (!frame)
    103         return;
    104 
    105     frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false);
    106 }
    107 
    108 bool MediaTokenizer::writeRawData(const char*, int)
    109 {
    110     ASSERT(!m_mediaElement);
    111     if (m_mediaElement)
    112         return false;
    113 
    114     createDocumentStructure();
    115     finish();
    116     return false;
    117 }
    118 
    119 void MediaTokenizer::stopParsing()
    120 {
    121     Tokenizer::stopParsing();
    122 }
    123 
    124 void MediaTokenizer::finish()
    125 {
    126     if (!m_parserStopped)
    127         m_doc->finishedParsing();
    128 }
    129 
    130 bool MediaTokenizer::isWaitingForScripts() const
    131 {
    132     // A media document is never waiting for scripts
    133     return false;
    134 }
    135 
    136 MediaDocument::MediaDocument(Frame* frame)
    137     : HTMLDocument(frame)
    138     , m_replaceMediaElementTimer(this, &MediaDocument::replaceMediaElementTimerFired)
    139 {
    140     setParseMode(Compat);
    141 }
    142 
    143 MediaDocument::~MediaDocument()
    144 {
    145     ASSERT(!m_replaceMediaElementTimer.isActive());
    146 }
    147 
    148 Tokenizer* MediaDocument::createTokenizer()
    149 {
    150     return new MediaTokenizer(this);
    151 }
    152 
    153 void MediaDocument::defaultEventHandler(Event* event)
    154 {
    155     // Match the default Quicktime plugin behavior to allow
    156     // clicking and double-clicking to pause and play the media.
    157     Node* targetNode = event->target()->toNode();
    158     if (targetNode && targetNode->hasTagName(videoTag)) {
    159         HTMLVideoElement* video = static_cast<HTMLVideoElement*>(targetNode);
    160         if (event->type() == eventNames().clickEvent) {
    161             if (!video->canPlay()) {
    162                 video->pause(event->fromUserGesture());
    163                 event->setDefaultHandled();
    164             }
    165         } else if (event->type() == eventNames().dblclickEvent) {
    166             if (video->canPlay()) {
    167                 video->play(event->fromUserGesture());
    168                 event->setDefaultHandled();
    169             }
    170         }
    171     }
    172 
    173     if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent()) {
    174         HTMLVideoElement* video = 0;
    175         if (targetNode) {
    176             if (targetNode->hasTagName(videoTag))
    177                 video = static_cast<HTMLVideoElement*>(targetNode);
    178             else {
    179                 RefPtr<NodeList> nodeList = targetNode->getElementsByTagName("video");
    180                 if (nodeList.get()->length() > 0)
    181                     video = static_cast<HTMLVideoElement*>(nodeList.get()->item(0));
    182             }
    183         }
    184         if (video) {
    185             KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event);
    186             if (keyboardEvent->keyIdentifier() == "U+0020") { // space
    187                 if (video->paused()) {
    188                     if (video->canPlay())
    189                         video->play(event->fromUserGesture());
    190                 } else
    191                     video->pause(event->fromUserGesture());
    192                 event->setDefaultHandled();
    193             }
    194         }
    195     }
    196 }
    197 
    198 void MediaDocument::mediaElementSawUnsupportedTracks()
    199 {
    200     // The HTMLMediaElement was told it has something that the underlying
    201     // MediaPlayer cannot handle so we should switch from <video> to <embed>
    202     // and let the plugin handle this. Don't do it immediately as this
    203     // function may be called directly from a media engine callback, and
    204     // replaceChild will destroy the element, media player, and media engine.
    205     m_replaceMediaElementTimer.startOneShot(0);
    206 }
    207 
    208 void MediaDocument::replaceMediaElementTimerFired(Timer<MediaDocument>*)
    209 {
    210     HTMLElement* htmlBody = body();
    211     if (!htmlBody)
    212         return;
    213 
    214     // Set body margin width and height to 0 as that is what a PluginDocument uses.
    215     htmlBody->setAttribute(marginwidthAttr, "0");
    216     htmlBody->setAttribute(marginheightAttr, "0");
    217 
    218     RefPtr<NodeList> nodeList = htmlBody->getElementsByTagName("video");
    219 
    220     if (nodeList.get()->length() > 0) {
    221         HTMLVideoElement* videoElement = static_cast<HTMLVideoElement*>(nodeList.get()->item(0));
    222 
    223         RefPtr<Element> element = Document::createElement(embedTag, false);
    224         HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(element.get());
    225 
    226         embedElement->setAttribute(widthAttr, "100%");
    227         embedElement->setAttribute(heightAttr, "100%");
    228         embedElement->setAttribute(nameAttr, "plugin");
    229         embedElement->setAttribute(srcAttr, url().string());
    230         embedElement->setAttribute(typeAttr, frame()->loader()->responseMIMEType());
    231 
    232         ExceptionCode ec;
    233         videoElement->parent()->replaceChild(embedElement, videoElement, ec);
    234     }
    235 }
    236 
    237 }
    238 #endif
    239