1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jungshik Shin <jshin@mailaps.org>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or 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 :
39 : #include "MediaDocument.h"
40 : #include "nsGkAtoms.h"
41 : #include "nsRect.h"
42 : #include "nsPresContext.h"
43 : #include "nsIPresShell.h"
44 : #include "nsIScrollable.h"
45 : #include "nsIViewManager.h"
46 : #include "nsITextToSubURI.h"
47 : #include "nsIURL.h"
48 : #include "nsIContentViewer.h"
49 : #include "nsIMarkupDocumentViewer.h"
50 : #include "nsIDocShell.h"
51 : #include "nsIParser.h" // kCharsetFrom* macro definition
52 : #include "nsNodeInfoManager.h"
53 : #include "nsContentUtils.h"
54 :
55 : namespace mozilla {
56 : namespace dom {
57 :
58 0 : MediaDocumentStreamListener::MediaDocumentStreamListener(MediaDocument *aDocument)
59 : {
60 0 : mDocument = aDocument;
61 0 : }
62 :
63 0 : MediaDocumentStreamListener::~MediaDocumentStreamListener()
64 : {
65 0 : }
66 :
67 :
68 0 : NS_IMPL_THREADSAFE_ISUPPORTS2(MediaDocumentStreamListener,
69 : nsIRequestObserver,
70 : nsIStreamListener)
71 :
72 :
73 : void
74 0 : MediaDocumentStreamListener::SetStreamListener(nsIStreamListener *aListener)
75 : {
76 0 : mNextStream = aListener;
77 0 : }
78 :
79 : NS_IMETHODIMP
80 0 : MediaDocumentStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
81 : {
82 0 : NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
83 :
84 0 : mDocument->StartLayout();
85 :
86 0 : if (mNextStream) {
87 0 : return mNextStream->OnStartRequest(request, ctxt);
88 : }
89 :
90 0 : return NS_BINDING_ABORTED;
91 : }
92 :
93 : NS_IMETHODIMP
94 0 : MediaDocumentStreamListener::OnStopRequest(nsIRequest* request,
95 : nsISupports *ctxt,
96 : nsresult status)
97 : {
98 0 : nsresult rv = NS_OK;
99 0 : if (mNextStream) {
100 0 : rv = mNextStream->OnStopRequest(request, ctxt, status);
101 : }
102 :
103 : // No more need for our document so clear our reference and prevent leaks
104 0 : mDocument = nsnull;
105 :
106 0 : return rv;
107 : }
108 :
109 : NS_IMETHODIMP
110 0 : MediaDocumentStreamListener::OnDataAvailable(nsIRequest* request,
111 : nsISupports *ctxt,
112 : nsIInputStream *inStr,
113 : PRUint32 sourceOffset,
114 : PRUint32 count)
115 : {
116 0 : if (mNextStream) {
117 0 : return mNextStream->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
118 : }
119 :
120 0 : return NS_OK;
121 : }
122 :
123 : // default format names for MediaDocument.
124 : const char* const MediaDocument::sFormatNames[4] =
125 : {
126 : "MediaTitleWithNoInfo", // eWithNoInfo
127 : "MediaTitleWithFile", // eWithFile
128 : "", // eWithDim
129 : "" // eWithDimAndFile
130 : };
131 :
132 0 : MediaDocument::MediaDocument()
133 0 : : mDocumentElementInserted(false)
134 : {
135 0 : }
136 0 : MediaDocument::~MediaDocument()
137 : {
138 0 : }
139 :
140 : nsresult
141 0 : MediaDocument::Init()
142 : {
143 0 : nsresult rv = nsHTMLDocument::Init();
144 0 : NS_ENSURE_SUCCESS(rv, rv);
145 :
146 : // Create a bundle for the localization
147 : nsCOMPtr<nsIStringBundleService> stringService =
148 0 : mozilla::services::GetStringBundleService();
149 0 : if (stringService) {
150 0 : stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI,
151 0 : getter_AddRefs(mStringBundle));
152 : }
153 :
154 0 : mIsSyntheticDocument = true;
155 :
156 0 : return NS_OK;
157 : }
158 :
159 : nsresult
160 0 : MediaDocument::StartDocumentLoad(const char* aCommand,
161 : nsIChannel* aChannel,
162 : nsILoadGroup* aLoadGroup,
163 : nsISupports* aContainer,
164 : nsIStreamListener** aDocListener,
165 : bool aReset,
166 : nsIContentSink* aSink)
167 : {
168 : nsresult rv = nsDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup,
169 : aContainer, aDocListener, aReset,
170 0 : aSink);
171 0 : if (NS_FAILED(rv)) {
172 0 : return rv;
173 : }
174 :
175 : // We try to set the charset of the current document to that of the
176 : // 'genuine' (as opposed to an intervening 'chrome') parent document
177 : // that may be in a different window/tab. Even if we fail here,
178 : // we just return NS_OK because another attempt is made in
179 : // |UpdateTitleAndCharset| and the worst thing possible is a mangled
180 : // filename in the titlebar and the file picker.
181 :
182 : // When this document is opened in the window/tab of the referring
183 : // document (by a simple link-clicking), |prevDocCharacterSet| contains
184 : // the charset of the referring document. On the other hand, if the
185 : // document is opened in a new window, it is |defaultCharacterSet| of |muCV|
186 : // where the charset of our interest is stored. In case of openining
187 : // in a new tab, we get the charset from the docShell. Note that we
188 : // exclude UTF-8 as 'invalid' because UTF-8 is likely to be the charset
189 : // of a chrome document that has nothing to do with the actual content
190 : // whose charset we want to know. Even if "the actual content" is indeed
191 : // in UTF-8, we don't lose anything because the default empty value is
192 : // considered synonymous with UTF-8.
193 :
194 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
195 :
196 : // not being able to set the charset is not critical.
197 0 : NS_ENSURE_TRUE(docShell, NS_OK);
198 :
199 0 : nsCAutoString charset;
200 :
201 0 : nsCOMPtr<nsIAtom> csAtom;
202 0 : docShell->GetParentCharset(getter_AddRefs(csAtom));
203 0 : if (csAtom) { // opening in a new tab
204 0 : csAtom->ToUTF8String(charset);
205 : }
206 :
207 0 : if (charset.IsEmpty() || charset.Equals("UTF-8")) {
208 0 : nsCOMPtr<nsIContentViewer> cv;
209 0 : docShell->GetContentViewer(getter_AddRefs(cv));
210 :
211 : // not being able to set the charset is not critical.
212 0 : NS_ENSURE_TRUE(cv, NS_OK);
213 0 : nsCOMPtr<nsIMarkupDocumentViewer> muCV = do_QueryInterface(cv);
214 0 : if (muCV) {
215 0 : muCV->GetPrevDocCharacterSet(charset); // opening in the same window/tab
216 0 : if (charset.Equals("UTF-8") || charset.IsEmpty()) {
217 0 : muCV->GetDefaultCharacterSet(charset); // opening in a new window
218 : }
219 : }
220 : }
221 :
222 0 : if (!charset.IsEmpty() && !charset.Equals("UTF-8")) {
223 0 : SetDocumentCharacterSet(charset);
224 0 : mCharacterSetSource = kCharsetFromUserDefault;
225 : }
226 :
227 0 : return NS_OK;
228 : }
229 :
230 : nsresult
231 0 : MediaDocument::CreateSyntheticDocument()
232 : {
233 : // Synthesize an empty html document
234 : nsresult rv;
235 :
236 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
237 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nsnull,
238 : kNameSpaceID_XHTML,
239 0 : nsIDOMNode::ELEMENT_NODE);
240 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
241 :
242 0 : nsRefPtr<nsGenericHTMLElement> root = NS_NewHTMLHtmlElement(nodeInfo.forget());
243 0 : NS_ENSURE_TRUE(root, NS_ERROR_OUT_OF_MEMORY);
244 :
245 0 : NS_ASSERTION(GetChildCount() == 0, "Shouldn't have any kids");
246 0 : rv = AppendChildTo(root, false);
247 0 : NS_ENSURE_SUCCESS(rv, rv);
248 :
249 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head, nsnull,
250 : kNameSpaceID_XHTML,
251 0 : nsIDOMNode::ELEMENT_NODE);
252 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
253 :
254 : // Create a <head> so our title has somewhere to live
255 0 : nsRefPtr<nsGenericHTMLElement> head = NS_NewHTMLHeadElement(nodeInfo.forget());
256 0 : NS_ENSURE_TRUE(head, NS_ERROR_OUT_OF_MEMORY);
257 :
258 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::meta, nsnull,
259 : kNameSpaceID_XHTML,
260 0 : nsIDOMNode::ELEMENT_NODE);
261 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
262 :
263 0 : nsRefPtr<nsGenericHTMLElement> metaContent = NS_NewHTMLMetaElement(nodeInfo.forget());
264 0 : NS_ENSURE_TRUE(metaContent, NS_ERROR_OUT_OF_MEMORY);
265 : metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
266 0 : NS_LITERAL_STRING("viewport"),
267 0 : true);
268 :
269 : metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
270 0 : NS_LITERAL_STRING("width=device-width; height=device-height;"),
271 0 : true);
272 0 : head->AppendChildTo(metaContent, false);
273 :
274 0 : root->AppendChildTo(head, false);
275 :
276 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::body, nsnull,
277 : kNameSpaceID_XHTML,
278 0 : nsIDOMNode::ELEMENT_NODE);
279 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
280 :
281 0 : nsRefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo.forget());
282 0 : NS_ENSURE_TRUE(body, NS_ERROR_OUT_OF_MEMORY);
283 :
284 0 : root->AppendChildTo(body, false);
285 :
286 0 : return NS_OK;
287 : }
288 :
289 : nsresult
290 0 : MediaDocument::StartLayout()
291 : {
292 0 : mMayStartLayout = true;
293 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
294 : // Don't mess with the presshell if someone has already handled
295 : // its initial reflow.
296 0 : if (shell && !shell->DidInitialReflow()) {
297 0 : nsRect visibleArea = shell->GetPresContext()->GetVisibleArea();
298 0 : nsresult rv = shell->InitialReflow(visibleArea.width, visibleArea.height);
299 0 : NS_ENSURE_SUCCESS(rv, rv);
300 : }
301 :
302 0 : return NS_OK;
303 : }
304 :
305 : void
306 0 : MediaDocument::GetFileName(nsAString& aResult)
307 : {
308 0 : aResult.Truncate();
309 :
310 0 : nsCOMPtr<nsIURL> url = do_QueryInterface(mDocumentURI);
311 0 : if (!url)
312 : return;
313 :
314 0 : nsCAutoString fileName;
315 0 : url->GetFileName(fileName);
316 0 : if (fileName.IsEmpty())
317 : return;
318 :
319 0 : nsCAutoString docCharset;
320 : // Now that the charset is set in |StartDocumentLoad| to the charset of
321 : // the document viewer instead of a bogus value ("ISO-8859-1" set in
322 : // |nsDocument|'s ctor), the priority is given to the current charset.
323 : // This is necessary to deal with a media document being opened in a new
324 : // window or a new tab, in which case |originCharset| of |nsIURI| is not
325 : // reliable.
326 0 : if (mCharacterSetSource != kCharsetUninitialized) {
327 0 : docCharset = mCharacterSet;
328 : } else {
329 : // resort to |originCharset|
330 0 : url->GetOriginCharset(docCharset);
331 0 : SetDocumentCharacterSet(docCharset);
332 : }
333 :
334 : nsresult rv;
335 : nsCOMPtr<nsITextToSubURI> textToSubURI =
336 0 : do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
337 0 : if (NS_SUCCEEDED(rv)) {
338 : // UnEscapeURIForUI always succeeds
339 0 : textToSubURI->UnEscapeURIForUI(docCharset, fileName, aResult);
340 : } else {
341 0 : CopyUTF8toUTF16(fileName, aResult);
342 : }
343 : }
344 :
345 : nsresult
346 0 : MediaDocument::LinkStylesheet(const nsAString& aStylesheet)
347 : {
348 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
349 : nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::link, nsnull,
350 : kNameSpaceID_XHTML,
351 0 : nsIDOMNode::ELEMENT_NODE);
352 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
353 :
354 0 : nsRefPtr<nsGenericHTMLElement> link = NS_NewHTMLLinkElement(nodeInfo.forget());
355 0 : NS_ENSURE_TRUE(link, NS_ERROR_OUT_OF_MEMORY);
356 :
357 : link->SetAttr(kNameSpaceID_None, nsGkAtoms::rel,
358 0 : NS_LITERAL_STRING("stylesheet"), true);
359 :
360 0 : link->SetAttr(kNameSpaceID_None, nsGkAtoms::href, aStylesheet, true);
361 :
362 0 : Element* head = GetHeadElement();
363 0 : return head->AppendChildTo(link, false);
364 : }
365 :
366 : void
367 0 : MediaDocument::UpdateTitleAndCharset(const nsACString& aTypeStr,
368 : const char* const* aFormatNames,
369 : PRInt32 aWidth, PRInt32 aHeight,
370 : const nsAString& aStatus)
371 : {
372 0 : nsXPIDLString fileStr;
373 0 : GetFileName(fileStr);
374 :
375 0 : NS_ConvertASCIItoUTF16 typeStr(aTypeStr);
376 0 : nsXPIDLString title;
377 :
378 0 : if (mStringBundle) {
379 : // if we got a valid size (not all media have a size)
380 0 : if (aWidth != 0 && aHeight != 0) {
381 0 : nsAutoString widthStr;
382 0 : nsAutoString heightStr;
383 0 : widthStr.AppendInt(aWidth);
384 0 : heightStr.AppendInt(aHeight);
385 : // If we got a filename, display it
386 0 : if (!fileStr.IsEmpty()) {
387 0 : const PRUnichar *formatStrings[4] = {fileStr.get(), typeStr.get(),
388 0 : widthStr.get(), heightStr.get()};
389 0 : NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithDimAndFile]);
390 0 : mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 4,
391 0 : getter_Copies(title));
392 : }
393 : else {
394 0 : const PRUnichar *formatStrings[3] = {typeStr.get(), widthStr.get(),
395 0 : heightStr.get()};
396 0 : NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithDim]);
397 0 : mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 3,
398 0 : getter_Copies(title));
399 0 : }
400 : }
401 : else {
402 : // If we got a filename, display it
403 0 : if (!fileStr.IsEmpty()) {
404 0 : const PRUnichar *formatStrings[2] = {fileStr.get(), typeStr.get()};
405 0 : NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithFile]);
406 0 : mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2,
407 0 : getter_Copies(title));
408 : }
409 : else {
410 0 : const PRUnichar *formatStrings[1] = {typeStr.get()};
411 0 : NS_ConvertASCIItoUTF16 fmtName(aFormatNames[eWithNoInfo]);
412 0 : mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 1,
413 0 : getter_Copies(title));
414 : }
415 : }
416 : }
417 :
418 : // set it on the document
419 0 : if (aStatus.IsEmpty()) {
420 0 : SetTitle(title);
421 : }
422 : else {
423 0 : nsXPIDLString titleWithStatus;
424 0 : const nsPromiseFlatString& status = PromiseFlatString(aStatus);
425 0 : const PRUnichar *formatStrings[2] = {title.get(), status.get()};
426 0 : NS_NAMED_LITERAL_STRING(fmtName, "TitleWithStatus");
427 0 : mStringBundle->FormatStringFromName(fmtName.get(), formatStrings, 2,
428 0 : getter_Copies(titleWithStatus));
429 0 : SetTitle(titleWithStatus);
430 : }
431 0 : }
432 :
433 : void
434 0 : MediaDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject)
435 : {
436 0 : nsHTMLDocument::SetScriptGlobalObject(aGlobalObject);
437 0 : if (!mDocumentElementInserted && aGlobalObject) {
438 0 : mDocumentElementInserted = true;
439 : nsContentUtils::AddScriptRunner(
440 0 : new nsDocElementCreatedNotificationRunner(this));
441 : }
442 0 : }
443 :
444 : } // namespace dom
445 : } // namespace mozilla
|