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