1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2007
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Chris Double <chris.double@double.co.nz>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 : #include "nsIDOMHTMLAudioElement.h"
39 : #include "nsIDOMHTMLSourceElement.h"
40 : #include "nsHTMLAudioElement.h"
41 : #include "nsGenericHTMLElement.h"
42 : #include "nsGkAtoms.h"
43 : #include "nsSize.h"
44 : #include "nsIFrame.h"
45 : #include "nsIDocument.h"
46 : #include "nsIDOMDocument.h"
47 : #include "nsDOMError.h"
48 : #include "nsNodeInfoManager.h"
49 : #include "plbase64.h"
50 : #include "nsNetUtil.h"
51 : #include "prmem.h"
52 : #include "nsNetUtil.h"
53 : #include "nsXPCOMStrings.h"
54 : #include "prlock.h"
55 : #include "nsThreadUtils.h"
56 :
57 : #include "nsIScriptSecurityManager.h"
58 : #include "nsIXPConnect.h"
59 : #include "jsapi.h"
60 : #include "jsfriendapi.h"
61 : #include "jstypedarray.h"
62 : #include "nsJSUtils.h"
63 :
64 : #include "nsITimer.h"
65 :
66 : #include "nsEventDispatcher.h"
67 : #include "nsIDOMProgressEvent.h"
68 : #include "nsContentUtils.h"
69 :
70 : using namespace mozilla::dom;
71 :
72 : nsGenericHTMLElement*
73 0 : NS_NewHTMLAudioElement(already_AddRefed<nsINodeInfo> aNodeInfo,
74 : FromParser aFromParser)
75 : {
76 : /*
77 : * nsHTMLAudioElement's will be created without a nsINodeInfo passed in
78 : * if someone says "var audio = new Audio();" in JavaScript, in a case like
79 : * that we request the nsINodeInfo from the document's nodeinfo list.
80 : */
81 0 : nsCOMPtr<nsINodeInfo> nodeInfo(aNodeInfo);
82 0 : if (!nodeInfo) {
83 : nsCOMPtr<nsIDocument> doc =
84 0 : do_QueryInterface(nsContentUtils::GetDocumentFromCaller());
85 0 : NS_ENSURE_TRUE(doc, nsnull);
86 :
87 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::audio, nsnull,
88 : kNameSpaceID_XHTML,
89 0 : nsIDOMNode::ELEMENT_NODE);
90 0 : NS_ENSURE_TRUE(nodeInfo, nsnull);
91 : }
92 :
93 0 : return new nsHTMLAudioElement(nodeInfo.forget());
94 : }
95 :
96 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLAudioElement, nsHTMLMediaElement)
97 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLAudioElement, nsHTMLMediaElement)
98 :
99 0 : DOMCI_NODE_DATA(HTMLAudioElement, nsHTMLAudioElement)
100 :
101 0 : NS_INTERFACE_TABLE_HEAD(nsHTMLAudioElement)
102 0 : NS_HTML_CONTENT_INTERFACE_TABLE3(nsHTMLAudioElement, nsIDOMHTMLMediaElement,
103 : nsIDOMHTMLAudioElement, nsIJSNativeInitializer)
104 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLAudioElement,
105 : nsHTMLMediaElement)
106 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLAudioElement)
107 :
108 0 : NS_IMPL_ELEMENT_CLONE(nsHTMLAudioElement)
109 :
110 :
111 0 : nsHTMLAudioElement::nsHTMLAudioElement(already_AddRefed<nsINodeInfo> aNodeInfo)
112 0 : : nsHTMLMediaElement(aNodeInfo)
113 : {
114 0 : }
115 :
116 0 : nsHTMLAudioElement::~nsHTMLAudioElement()
117 : {
118 0 : }
119 :
120 : NS_IMETHODIMP
121 0 : nsHTMLAudioElement::Initialize(nsISupports* aOwner, JSContext* aContext,
122 : JSObject *aObj, PRUint32 argc, jsval *argv)
123 : {
124 : // Audio elements created using "new Audio(...)" should have
125 : // 'preload' set to 'auto' (since the script must intend to
126 : // play the audio)
127 : nsresult rv = SetAttr(kNameSpaceID_None, nsGkAtoms::preload,
128 0 : NS_LITERAL_STRING("auto"), true);
129 0 : if (NS_FAILED(rv))
130 0 : return rv;
131 :
132 0 : if (argc <= 0) {
133 : // Nothing more to do here if we don't get any arguments.
134 0 : return NS_OK;
135 : }
136 :
137 : // The only (optional) argument is the url of the audio
138 0 : JSString* jsstr = JS_ValueToString(aContext, argv[0]);
139 0 : if (!jsstr)
140 0 : return NS_ERROR_FAILURE;
141 :
142 0 : nsDependentJSString str;
143 0 : if (!str.init(aContext, jsstr))
144 0 : return NS_ERROR_FAILURE;
145 :
146 0 : rv = SetAttr(kNameSpaceID_None, nsGkAtoms::src, str, true);
147 0 : if (NS_FAILED(rv))
148 0 : return rv;
149 :
150 : // We have been specified with a src URL. Begin a load.
151 0 : QueueSelectResourceTask();
152 :
153 0 : return NS_OK;
154 : }
155 :
156 : NS_IMETHODIMP
157 0 : nsHTMLAudioElement::MozSetup(PRUint32 aChannels, PRUint32 aRate)
158 : {
159 : // If there is already a src provided, don't setup another stream
160 0 : if (mDecoder) {
161 0 : return NS_ERROR_FAILURE;
162 : }
163 :
164 : // MozWriteAudio divides by mChannels, so validate now.
165 0 : if (0 == aChannels) {
166 0 : return NS_ERROR_FAILURE;
167 : }
168 :
169 0 : if (mAudioStream) {
170 0 : mAudioStream->Shutdown();
171 : }
172 :
173 0 : mAudioStream = nsAudioStream::AllocateStream();
174 0 : nsresult rv = mAudioStream->Init(aChannels, aRate,
175 0 : nsAudioStream::FORMAT_FLOAT32);
176 0 : if (NS_FAILED(rv)) {
177 0 : mAudioStream->Shutdown();
178 0 : mAudioStream = nsnull;
179 0 : return rv;
180 : }
181 :
182 0 : MetadataLoaded(aChannels, aRate);
183 0 : mAudioStream->SetVolume(mVolume);
184 0 : return NS_OK;
185 : }
186 :
187 : NS_IMETHODIMP
188 0 : nsHTMLAudioElement::MozWriteAudio(const jsval &aData, JSContext *aCx, PRUint32 *aRetVal)
189 : {
190 0 : if (!mAudioStream) {
191 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
192 : }
193 :
194 0 : if (JSVAL_IS_PRIMITIVE(aData)) {
195 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
196 : }
197 :
198 0 : JSObject *darray = JSVAL_TO_OBJECT(aData);
199 0 : JS::AutoValueRooter tsrc_tvr(aCx);
200 0 : JSObject *tsrc = NULL;
201 :
202 : // Allow either Float32Array or plain JS Array
203 0 : if (js::GetObjectClass(darray) == &js::TypedArray::fastClasses[js::TypedArray::TYPE_FLOAT32])
204 : {
205 0 : tsrc = js::TypedArray::getTypedArray(darray);
206 0 : } else if (JS_IsArrayObject(aCx, darray)) {
207 0 : JSObject *nobj = js_CreateTypedArrayWithArray(aCx, js::TypedArray::TYPE_FLOAT32, darray);
208 0 : if (!nobj) {
209 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
210 : }
211 0 : *tsrc_tvr.jsval_addr() = OBJECT_TO_JSVAL(nobj);
212 0 : tsrc = js::TypedArray::getTypedArray(nobj);
213 : } else {
214 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
215 : }
216 :
217 0 : PRUint32 dataLength = JS_GetTypedArrayLength(tsrc);
218 :
219 : // Make sure that we are going to write the correct amount of data based
220 : // on number of channels.
221 0 : if (dataLength % mChannels != 0) {
222 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
223 : }
224 :
225 : // Don't write more than can be written without blocking.
226 0 : PRUint32 writeLen = NS_MIN(mAudioStream->Available(), dataLength / mChannels);
227 :
228 0 : nsresult rv = mAudioStream->Write(JS_GetTypedArrayData(tsrc), writeLen);
229 0 : if (NS_FAILED(rv)) {
230 0 : return rv;
231 : }
232 :
233 : // Return the actual amount written.
234 0 : *aRetVal = writeLen * mChannels;
235 0 : return rv;
236 : }
237 :
238 : NS_IMETHODIMP
239 0 : nsHTMLAudioElement::MozCurrentSampleOffset(PRUint64 *aRetVal)
240 : {
241 0 : if (!mAudioStream) {
242 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
243 : }
244 :
245 0 : *aRetVal = mAudioStream->GetPositionInFrames() * mChannels;
246 0 : return NS_OK;
247 : }
248 :
249 :
250 0 : nsresult nsHTMLAudioElement::SetAcceptHeader(nsIHttpChannel* aChannel)
251 : {
252 : nsCAutoString value(
253 : #ifdef MOZ_WEBM
254 : "audio/webm,"
255 : #endif
256 : #ifdef MOZ_OGG
257 : "audio/ogg,"
258 : #endif
259 : #ifdef MOZ_WAVE
260 : "audio/wav,"
261 : #endif
262 : "audio/*;q=0.9,"
263 : #ifdef MOZ_OGG
264 : "application/ogg;q=0.7,"
265 : #endif
266 0 : "video/*;q=0.6,*/*;q=0.5");
267 :
268 0 : return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
269 : value,
270 0 : false);
271 : }
|