Element's renderer factory should return RenderPtrs.
[WebKit-https.git] / Source / WebCore / Modules / plugins / QuickTimePluginReplacement.cpp
1 /*
2  * Copyright (C) 2013 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 COMPUTER, 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 COMPUTER, 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(MEDIA_CONTROLS_SCRIPT)
29
30 #include "QuickTimePluginReplacement.h"
31
32 #include "Event.h"
33 #include "HTMLPlugInElement.h"
34 #include "HTMLVideoElement.h"
35 #include "JSDOMBinding.h"
36 #include "JSDOMGlobalObject.h"
37 #include "JSHTMLVideoElement.h"
38 #include "JSQuickTimePluginReplacement.h"
39 #include "Logging.h"
40 #include "MainFrame.h"
41 #include "Page.h"
42 #include "RenderElement.h"
43 #include "ScriptController.h"
44 #include "ScriptSourceCode.h"
45 #include "UserAgentScripts.h"
46 #include <JavaScriptCore/APICast.h>
47 #include <JavaScriptCore/JSBase.h>
48 #include <JavaScriptCore/JSCJSValueInlines.h>
49
50 using namespace JSC;
51
52 namespace WebCore {
53
54 static String quickTimePluginReplacementScript()
55 {
56     DEFINE_STATIC_LOCAL(String, script, (QuickTimePluginReplacementJavaScript, sizeof(QuickTimePluginReplacementJavaScript)));
57     return script;
58 }
59
60 void QuickTimePluginReplacement::registerPluginReplacement(PluginReplacementRegistrar registrar)
61 {
62     registrar(ReplacementPlugin(create, supportsMimeType, supportsFileExtension));
63 }
64
65 PassRefPtr<PluginReplacement> QuickTimePluginReplacement::create(HTMLPlugInElement& plugin, const Vector<String>& paramNames, const Vector<String>& paramValues)
66 {
67     return adoptRef(new QuickTimePluginReplacement(plugin, paramNames, paramValues));
68 }
69
70 bool QuickTimePluginReplacement::supportsMimeType(const String& mimeType)
71 {
72     static const char* types[] = {
73         "application/vnd.apple.mpegurl", "application/x-mpegurl", "audio/3gpp", "audio/3gpp2", "audio/aac", "audio/aiff",
74         "audio/amr", "audio/basic", "audio/mp3", "audio/mp4", "audio/mpeg", "audio/mpeg3", "audio/mpegurl", "audio/scpls",
75         "audio/wav", "audio/x-aac", "audio/x-aiff", "audio/x-caf", "audio/x-m4a", "audio/x-m4b", "audio/x-m4p",
76         "audio/x-m4r", "audio/x-mp3", "audio/x-mpeg", "audio/x-mpeg3", "audio/x-mpegurl", "audio/x-scpls", "audio/x-wav",
77         "video/3gpp", "video/3gpp2", "video/mp4", "video/quicktime", "video/x-m4v"
78     };
79     DEFINE_STATIC_LOCAL(HashSet<String>, typeHash, ());
80     if (!typeHash.size()) {
81         for (size_t i = 0; i < WTF_ARRAY_LENGTH(types); ++i)
82             typeHash.add(types[i]);
83     }
84
85     return typeHash.contains(mimeType);
86 }
87
88 bool QuickTimePluginReplacement::supportsFileExtension(const String& extension)
89 {
90     static const char* extensions[] = {
91         "3g2", "3gp", "3gp2", "3gpp", "aac", "adts", "aif", "aifc", "aiff", "AMR", "au", "bwf", "caf", "cdda", "m3u",
92         "m3u8", "m4a", "m4b", "m4p", "m4r", "m4v", "mov", "mp3", "mp3", "mp4", "mpeg", "mpg", "mqv", "pls", "qt",
93         "snd", "swa", "ts", "ulw", "wav"
94     };
95     DEFINE_STATIC_LOCAL(HashSet<String>, extensionHash, ());
96     if (!extensionHash.size()) {
97         for (size_t i = 0; i < WTF_ARRAY_LENGTH(extensions); ++i)
98             extensionHash.add(extensions[i]);
99     }
100
101     return extensionHash.contains(extension);
102 }
103
104 QuickTimePluginReplacement::QuickTimePluginReplacement(HTMLPlugInElement& plugin, const Vector<String>& paramNames, const Vector<String>& paramValues)
105     :PluginReplacement()
106     , m_parentElement(&plugin)
107     , m_names(paramNames)
108     , m_values(paramValues)
109     , m_scriptObject(nullptr)
110 {
111 }
112
113 QuickTimePluginReplacement::~QuickTimePluginReplacement()
114 {
115     m_parentElement = nullptr;
116     m_scriptObject = nullptr;
117     m_mediaElement = nullptr;
118 }
119
120 RenderPtr<RenderElement> QuickTimePluginReplacement::createElementRenderer(HTMLPlugInElement& plugin, PassRef<RenderStyle> style)
121 {
122     ASSERT_UNUSED(plugin, m_parentElement == &plugin);
123
124     if (m_mediaElement)
125         return m_mediaElement->createElementRenderer(std::move(style));
126
127     return nullptr;
128 }
129
130 DOMWrapperWorld& QuickTimePluginReplacement::isolatedWorld()
131 {
132     static DOMWrapperWorld& isolatedWorld = *DOMWrapperWorld::create(JSDOMWindow::commonVM()).leakRef();
133     return isolatedWorld;
134 }
135
136 bool QuickTimePluginReplacement::ensureReplacementScriptInjected()
137 {
138     Page* page = m_parentElement->document().page();
139     if (!page)
140         return false;
141     
142     DOMWrapperWorld& world = isolatedWorld();
143     ScriptController& scriptController = page->mainFrame().script();
144     JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
145     ExecState* exec = globalObject->globalExec();
146     
147     JSValue replacementFunction = globalObject->get(exec, Identifier(exec, "createPluginReplacement"));
148     if (replacementFunction.isFunction())
149         return true;
150     
151     scriptController.evaluateInWorld(ScriptSourceCode(quickTimePluginReplacementScript()), world);
152     if (exec->hadException()) {
153         LOG(Plugins, "%p - Exception when evaluating QuickTime plugin replacement script", this);
154         exec->clearException();
155         return false;
156     }
157     
158     return true;
159 }
160
161 bool QuickTimePluginReplacement::installReplacement(ShadowRoot* root)
162 {
163     Page* page = m_parentElement->document().page();
164
165     if (!ensureReplacementScriptInjected())
166         return false;
167
168     DOMWrapperWorld& world = isolatedWorld();
169     ScriptController& scriptController = page->mainFrame().script();
170     JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
171     ExecState* exec = globalObject->globalExec();
172     JSLockHolder lock(exec);
173     
174     // Lookup the "createPluginReplacement" function.
175     JSValue replacementFunction = globalObject->get(exec, Identifier(exec, "createPluginReplacement"));
176     if (replacementFunction.isUndefinedOrNull())
177         return false;
178     JSObject* replacementObject = replacementFunction.toObject(exec);
179     CallData callData;
180     CallType callType = replacementObject->methodTable()->getCallData(replacementObject, callData);
181     if (callType == CallTypeNone)
182         return false;
183
184     MarkedArgumentBuffer argList;
185     argList.append(toJS(exec, globalObject, root));
186     argList.append(toJS(exec, globalObject, m_parentElement));
187     argList.append(toJS(exec, globalObject, this));
188     argList.append(toJS<String>(exec, globalObject, m_names));
189     argList.append(toJS<String>(exec, globalObject, m_values));
190     JSValue replacement = call(exec, replacementObject, callType, callData, globalObject, argList);
191     if (exec->hadException()) {
192         exec->clearException();
193         return false;
194     }
195
196     // Get the <video> created to replace the plug-in.
197     JSValue value = replacement.get(exec, Identifier(exec, "video"));
198     if (!exec->hadException() && !value.isUndefinedOrNull())
199         m_mediaElement = toHTMLVideoElement(value);
200
201     if (!m_mediaElement) {
202         LOG(Plugins, "%p - Failed to find <video> element created by QuickTime plugin replacement script.", this);
203         exec->clearException();
204         return false;
205     }
206
207     // Get the scripting interface.
208     value = replacement.get(exec, Identifier(exec, "scriptObject"));
209     if (!exec->hadException() && !value.isUndefinedOrNull())
210         m_scriptObject = value.toObject(exec);
211
212     if (!m_scriptObject) {
213         LOG(Plugins, "%p - Failed to find script object created by QuickTime plugin replacement.", this);
214         exec->clearException();
215         return false;
216     }
217
218     return true;
219 }
220
221 unsigned long long QuickTimePluginReplacement::movieSize() const
222 {
223     if (m_mediaElement)
224         return m_mediaElement->fileSize();
225
226     return 0;
227 }
228
229 void QuickTimePluginReplacement::postEvent(const String& eventName)
230 {
231     Ref<HTMLPlugInElement> protect(*m_parentElement);
232     RefPtr<Event> event = Event::create(eventName, false, true);
233     m_parentElement->dispatchEvent(event.get());
234 }
235
236 }
237 #endif