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#include "core/html/MediaDocument.h"
29
30#include "HTMLNames.h"
31#include "bindings/v8/ExceptionStatePlaceholder.h"
32#include "core/dom/NodeTraversal.h"
33#include "core/dom/RawDataDocumentParser.h"
34#include "core/events/KeyboardEvent.h"
35#include "core/events/ThreadLocalEventNames.h"
36#include "core/html/HTMLBodyElement.h"
37#include "core/html/HTMLHeadElement.h"
38#include "core/html/HTMLHtmlElement.h"
39#include "core/html/HTMLMetaElement.h"
40#include "core/html/HTMLSourceElement.h"
41#include "core/html/HTMLVideoElement.h"
42#include "core/loader/DocumentLoader.h"
43#include "core/loader/FrameLoader.h"
44#include "core/frame/Frame.h"
45#include "platform/KeyboardCodes.h"
46
47namespace WebCore {
48
49using namespace HTMLNames;
50
51// FIXME: Share more code with PluginDocumentParser.
52class MediaDocumentParser : public RawDataDocumentParser {
53public:
54    static PassRefPtr<MediaDocumentParser> create(MediaDocument* document)
55    {
56        return adoptRef(new MediaDocumentParser(document));
57    }
58
59private:
60    explicit MediaDocumentParser(Document* document)
61        : RawDataDocumentParser(document)
62        , m_didBuildDocumentStructure(false)
63    {
64    }
65
66    virtual void appendBytes(const char*, size_t) OVERRIDE;
67
68    void createDocumentStructure();
69
70    bool m_didBuildDocumentStructure;
71};
72
73void MediaDocumentParser::createDocumentStructure()
74{
75    ASSERT(document());
76    RefPtr<HTMLHtmlElement> rootElement = HTMLHtmlElement::create(*document());
77    rootElement->insertedByParser();
78    document()->appendChild(rootElement);
79
80    if (document()->frame())
81        document()->frame()->loader().dispatchDocumentElementAvailable();
82
83    RefPtr<HTMLHeadElement> head = HTMLHeadElement::create(*document());
84    RefPtr<HTMLMetaElement> meta = HTMLMetaElement::create(*document());
85    meta->setAttribute(nameAttr, "viewport");
86    meta->setAttribute(contentAttr, "width=device-width");
87    head->appendChild(meta.release());
88
89    RefPtr<HTMLVideoElement> media = HTMLVideoElement::create(*document());
90    media->setAttribute(controlsAttr, "");
91    media->setAttribute(autoplayAttr, "");
92    media->setAttribute(nameAttr, "media");
93
94    RefPtr<HTMLSourceElement> source = HTMLSourceElement::create(*document());
95    source->setSrc(document()->url());
96
97    if (DocumentLoader* loader = document()->loader())
98        source->setType(loader->responseMIMEType());
99
100    media->appendChild(source.release());
101
102    RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(*document());
103    body->appendChild(media.release());
104
105    rootElement->appendChild(head.release());
106    rootElement->appendChild(body.release());
107
108    m_didBuildDocumentStructure = true;
109}
110
111void MediaDocumentParser::appendBytes(const char*, size_t)
112{
113    if (m_didBuildDocumentStructure)
114        return;
115
116    createDocumentStructure();
117    finish();
118}
119
120MediaDocument::MediaDocument(const DocumentInit& initializer)
121    : HTMLDocument(initializer, MediaDocumentClass)
122{
123    setCompatibilityMode(QuirksMode);
124    lockCompatibilityMode();
125}
126
127PassRefPtr<DocumentParser> MediaDocument::createParser()
128{
129    return MediaDocumentParser::create(this);
130}
131
132static inline HTMLVideoElement* descendentVideoElement(Node* root)
133{
134    ASSERT(root);
135
136    for (Node* node = root; node; node = NodeTraversal::next(*node, root)) {
137        if (isHTMLVideoElement(node))
138            return toHTMLVideoElement(node);
139    }
140
141    return 0;
142}
143
144void MediaDocument::defaultEventHandler(Event* event)
145{
146    Node* targetNode = event->target()->toNode();
147    if (!targetNode)
148        return;
149
150    if (event->type() == EventTypeNames::keydown && event->isKeyboardEvent()) {
151        HTMLVideoElement* video = descendentVideoElement(targetNode);
152        if (!video)
153            return;
154
155        KeyboardEvent* keyboardEvent = toKeyboardEvent(event);
156        if (keyboardEvent->keyIdentifier() == "U+0020" || keyboardEvent->keyCode() == VKEY_MEDIA_PLAY_PAUSE) {
157            // space or media key (play/pause)
158            if (video->paused()) {
159                if (video->canPlay())
160                    video->play();
161            } else
162                video->pause();
163            event->setDefaultHandled();
164        }
165    }
166}
167
168}
169