Backed out changeset 51d87c2129d2 (bug 1865372) for causing RunWatchdog crashes in...
[gecko.git] / dom / html / MediaDocument.cpp
blob40662e6d4cd1351315e9c7430e0dc4bbee322cfc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MediaDocument.h"
8 #include "nsGkAtoms.h"
9 #include "nsRect.h"
10 #include "nsPresContext.h"
11 #include "nsViewManager.h"
12 #include "nsITextToSubURI.h"
13 #include "nsIURL.h"
14 #include "nsIDocShell.h"
15 #include "nsCharsetSource.h" // kCharsetFrom* macro definition
16 #include "nsNodeInfoManager.h"
17 #include "nsContentUtils.h"
18 #include "nsDocElementCreatedNotificationRunner.h"
19 #include "mozilla/Encoding.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/Components.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsIPrincipal.h"
24 #include "nsIMultiPartChannel.h"
25 #include "nsProxyRelease.h"
27 namespace mozilla::dom {
29 MediaDocumentStreamListener::MediaDocumentStreamListener(
30 MediaDocument* aDocument)
31 : mDocument(aDocument) {}
33 MediaDocumentStreamListener::~MediaDocumentStreamListener() {
34 if (mDocument && !NS_IsMainThread()) {
35 nsCOMPtr<nsIEventTarget> mainTarget(do_GetMainThread());
36 NS_ProxyRelease("MediaDocumentStreamListener::mDocument", mainTarget,
37 mDocument.forget());
41 NS_IMPL_ISUPPORTS(MediaDocumentStreamListener, nsIRequestObserver,
42 nsIStreamListener, nsIThreadRetargetableStreamListener)
44 NS_IMETHODIMP
45 MediaDocumentStreamListener::OnStartRequest(nsIRequest* request) {
46 NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
48 mDocument->StartLayout();
50 if (mNextStream) {
51 return mNextStream->OnStartRequest(request);
54 return NS_ERROR_PARSED_DATA_CACHED;
57 NS_IMETHODIMP
58 MediaDocumentStreamListener::OnStopRequest(nsIRequest* request,
59 nsresult status) {
60 nsresult rv = NS_OK;
61 if (mNextStream) {
62 rv = mNextStream->OnStopRequest(request, status);
65 // Don't release mDocument here if we're in the middle of a multipart
66 // response.
67 bool lastPart = true;
68 nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(request));
69 if (mpchan) {
70 mpchan->GetIsLastPart(&lastPart);
73 if (lastPart) {
74 mDocument = nullptr;
76 return rv;
79 NS_IMETHODIMP
80 MediaDocumentStreamListener::OnDataAvailable(nsIRequest* request,
81 nsIInputStream* inStr,
82 uint64_t sourceOffset,
83 uint32_t count) {
84 if (mNextStream) {
85 return mNextStream->OnDataAvailable(request, inStr, sourceOffset, count);
88 return NS_OK;
91 NS_IMETHODIMP
92 MediaDocumentStreamListener::OnDataFinished(nsresult aStatus) {
93 if (!mNextStream) {
94 return NS_ERROR_FAILURE;
96 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
97 do_QueryInterface(mNextStream);
98 if (retargetable) {
99 return retargetable->OnDataFinished(aStatus);
102 return NS_OK;
105 NS_IMETHODIMP
106 MediaDocumentStreamListener::CheckListenerChain() {
107 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
108 do_QueryInterface(mNextStream);
109 if (retargetable) {
110 return retargetable->CheckListenerChain();
112 return NS_ERROR_NO_INTERFACE;
115 // default format names for MediaDocument.
116 const char* const MediaDocument::sFormatNames[4] = {
117 "MediaTitleWithNoInfo", // eWithNoInfo
118 "MediaTitleWithFile", // eWithFile
119 "", // eWithDim
120 "" // eWithDimAndFile
123 MediaDocument::MediaDocument() : mDidInitialDocumentSetup(false) {
124 mCompatMode = eCompatibility_FullStandards;
126 MediaDocument::~MediaDocument() = default;
128 nsresult MediaDocument::Init(nsIPrincipal* aPrincipal,
129 nsIPrincipal* aPartitionedPrincipal) {
130 nsresult rv = nsHTMLDocument::Init(aPrincipal, aPartitionedPrincipal);
131 NS_ENSURE_SUCCESS(rv, rv);
133 mIsSyntheticDocument = true;
135 return NS_OK;
138 nsresult MediaDocument::StartDocumentLoad(
139 const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup,
140 nsISupports* aContainer, nsIStreamListener** aDocListener, bool aReset) {
141 nsresult rv = Document::StartDocumentLoad(aCommand, aChannel, aLoadGroup,
142 aContainer, aDocListener, aReset);
143 if (NS_FAILED(rv)) {
144 return rv;
147 // We try to set the charset of the current document to that of the
148 // 'genuine' (as opposed to an intervening 'chrome') parent document
149 // that may be in a different window/tab. Even if we fail here,
150 // we just return NS_OK because another attempt is made in
151 // |UpdateTitleAndCharset| and the worst thing possible is a mangled
152 // filename in the titlebar and the file picker.
154 // Note that we
155 // exclude UTF-8 as 'invalid' because UTF-8 is likely to be the charset
156 // of a chrome document that has nothing to do with the actual content
157 // whose charset we want to know. Even if "the actual content" is indeed
158 // in UTF-8, we don't lose anything because the default empty value is
159 // considered synonymous with UTF-8.
161 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
163 // not being able to set the charset is not critical.
164 NS_ENSURE_TRUE(docShell, NS_OK);
166 const Encoding* encoding;
167 int32_t source;
168 nsCOMPtr<nsIPrincipal> principal;
169 // opening in a new tab
170 docShell->GetParentCharset(encoding, &source, getter_AddRefs(principal));
172 if (encoding && encoding != UTF_8_ENCODING &&
173 NodePrincipal()->Equals(principal)) {
174 SetDocumentCharacterSetSource(source);
175 SetDocumentCharacterSet(WrapNotNull(encoding));
178 return NS_OK;
181 void MediaDocument::InitialSetupDone() {
182 MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_LOADING,
183 "Bad readyState: we should still be doing our initial load");
184 mDidInitialDocumentSetup = true;
185 nsContentUtils::AddScriptRunner(
186 new nsDocElementCreatedNotificationRunner(this));
187 SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
190 nsresult MediaDocument::CreateSyntheticDocument() {
191 MOZ_ASSERT(!InitialSetupHasBeenDone());
193 // Synthesize an empty html document
195 RefPtr<mozilla::dom::NodeInfo> nodeInfo;
196 nodeInfo = mNodeInfoManager->GetNodeInfo(
197 nsGkAtoms::html, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
199 RefPtr<nsGenericHTMLElement> root = NS_NewHTMLHtmlElement(nodeInfo.forget());
200 NS_ENSURE_TRUE(root, NS_ERROR_OUT_OF_MEMORY);
202 NS_ASSERTION(GetChildCount() == 0, "Shouldn't have any kids");
203 ErrorResult rv;
204 AppendChildTo(root, false, rv);
205 if (rv.Failed()) {
206 return rv.StealNSResult();
209 nodeInfo = mNodeInfoManager->GetNodeInfo(
210 nsGkAtoms::head, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
212 // Create a <head> so our title has somewhere to live
213 RefPtr<nsGenericHTMLElement> head = NS_NewHTMLHeadElement(nodeInfo.forget());
214 NS_ENSURE_TRUE(head, NS_ERROR_OUT_OF_MEMORY);
216 nodeInfo = mNodeInfoManager->GetNodeInfo(
217 nsGkAtoms::meta, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
219 RefPtr<nsGenericHTMLElement> metaContent =
220 NS_NewHTMLMetaElement(nodeInfo.forget());
221 NS_ENSURE_TRUE(metaContent, NS_ERROR_OUT_OF_MEMORY);
222 metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::name, u"viewport"_ns,
223 true);
225 metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
226 u"width=device-width; height=device-height;"_ns, true);
227 head->AppendChildTo(metaContent, false, IgnoreErrors());
229 root->AppendChildTo(head, false, IgnoreErrors());
231 nodeInfo = mNodeInfoManager->GetNodeInfo(
232 nsGkAtoms::body, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
234 RefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo.forget());
235 NS_ENSURE_TRUE(body, NS_ERROR_OUT_OF_MEMORY);
237 root->AppendChildTo(body, false, IgnoreErrors());
239 return NS_OK;
242 nsresult MediaDocument::StartLayout() {
243 mMayStartLayout = true;
244 RefPtr<PresShell> presShell = GetPresShell();
245 // Don't mess with the presshell if someone has already handled
246 // its initial reflow.
247 if (presShell && !presShell->DidInitialize()) {
248 nsresult rv = presShell->Initialize();
249 NS_ENSURE_SUCCESS(rv, rv);
252 return NS_OK;
255 void MediaDocument::GetFileName(nsAString& aResult, nsIChannel* aChannel) {
256 aResult.Truncate();
258 if (aChannel) {
259 aChannel->GetContentDispositionFilename(aResult);
260 if (!aResult.IsEmpty()) return;
263 nsCOMPtr<nsIURL> url = do_QueryInterface(mDocumentURI);
264 if (!url) return;
266 nsAutoCString fileName;
267 url->GetFileName(fileName);
268 if (fileName.IsEmpty()) return;
270 // Now that the charset is set in |StartDocumentLoad| to the charset of
271 // the document viewer instead of a bogus value ("windows-1252" set in
272 // |Document|'s ctor), the priority is given to the current charset.
273 // This is necessary to deal with a media document being opened in a new
274 // window or a new tab.
275 if (mCharacterSetSource == kCharsetUninitialized) {
276 // resort to UTF-8
277 SetDocumentCharacterSet(UTF_8_ENCODING);
280 nsresult rv;
281 nsCOMPtr<nsITextToSubURI> textToSubURI =
282 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
283 if (NS_SUCCEEDED(rv)) {
284 // UnEscapeURIForUI always succeeds
285 textToSubURI->UnEscapeURIForUI(fileName, aResult);
286 } else {
287 CopyUTF8toUTF16(fileName, aResult);
291 nsresult MediaDocument::LinkStylesheet(const nsAString& aStylesheet) {
292 RefPtr<mozilla::dom::NodeInfo> nodeInfo;
293 nodeInfo = mNodeInfoManager->GetNodeInfo(
294 nsGkAtoms::link, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
296 RefPtr<nsGenericHTMLElement> link = NS_NewHTMLLinkElement(nodeInfo.forget());
297 NS_ENSURE_TRUE(link, NS_ERROR_OUT_OF_MEMORY);
299 link->SetAttr(kNameSpaceID_None, nsGkAtoms::rel, u"stylesheet"_ns, true);
301 link->SetAttr(kNameSpaceID_None, nsGkAtoms::href, aStylesheet, true);
303 ErrorResult rv;
304 Element* head = GetHeadElement();
305 head->AppendChildTo(link, false, rv);
306 return rv.StealNSResult();
309 nsresult MediaDocument::LinkScript(const nsAString& aScript) {
310 RefPtr<mozilla::dom::NodeInfo> nodeInfo;
311 nodeInfo = mNodeInfoManager->GetNodeInfo(
312 nsGkAtoms::script, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
314 RefPtr<nsGenericHTMLElement> script =
315 NS_NewHTMLScriptElement(nodeInfo.forget());
316 NS_ENSURE_TRUE(script, NS_ERROR_OUT_OF_MEMORY);
318 script->SetAttr(kNameSpaceID_None, nsGkAtoms::type, u"text/javascript"_ns,
319 true);
321 script->SetAttr(kNameSpaceID_None, nsGkAtoms::src, aScript, true);
323 ErrorResult rv;
324 Element* head = GetHeadElement();
325 head->AppendChildTo(script, false, rv);
326 return rv.StealNSResult();
329 void MediaDocument::FormatStringFromName(const char* aName,
330 const nsTArray<nsString>& aParams,
331 nsAString& aResult) {
332 bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() && !AllowsL10n();
333 if (!spoofLocale) {
334 if (!mStringBundle) {
335 nsCOMPtr<nsIStringBundleService> stringService =
336 mozilla::components::StringBundle::Service();
337 if (stringService) {
338 stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI,
339 getter_AddRefs(mStringBundle));
342 if (mStringBundle) {
343 mStringBundle->FormatStringFromName(aName, aParams, aResult);
345 } else {
346 if (!mStringBundleEnglish) {
347 nsCOMPtr<nsIStringBundleService> stringService =
348 mozilla::components::StringBundle::Service();
349 if (stringService) {
350 stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI_en_US,
351 getter_AddRefs(mStringBundleEnglish));
354 if (mStringBundleEnglish) {
355 mStringBundleEnglish->FormatStringFromName(aName, aParams, aResult);
360 void MediaDocument::UpdateTitleAndCharset(const nsACString& aTypeStr,
361 nsIChannel* aChannel,
362 const char* const* aFormatNames,
363 int32_t aWidth, int32_t aHeight,
364 const nsAString& aStatus) {
365 nsAutoString fileStr;
366 GetFileName(fileStr, aChannel);
368 NS_ConvertASCIItoUTF16 typeStr(aTypeStr);
369 nsAutoString title;
371 // if we got a valid size (not all media have a size)
372 if (aWidth != 0 && aHeight != 0) {
373 nsAutoString widthStr;
374 nsAutoString heightStr;
375 widthStr.AppendInt(aWidth);
376 heightStr.AppendInt(aHeight);
377 // If we got a filename, display it
378 if (!fileStr.IsEmpty()) {
379 AutoTArray<nsString, 4> formatStrings = {fileStr, typeStr, widthStr,
380 heightStr};
381 FormatStringFromName(aFormatNames[eWithDimAndFile], formatStrings, title);
382 } else {
383 AutoTArray<nsString, 3> formatStrings = {typeStr, widthStr, heightStr};
384 FormatStringFromName(aFormatNames[eWithDim], formatStrings, title);
386 } else {
387 // If we got a filename, display it
388 if (!fileStr.IsEmpty()) {
389 AutoTArray<nsString, 2> formatStrings = {fileStr, typeStr};
390 FormatStringFromName(aFormatNames[eWithFile], formatStrings, title);
391 } else {
392 AutoTArray<nsString, 1> formatStrings = {typeStr};
393 FormatStringFromName(aFormatNames[eWithNoInfo], formatStrings, title);
397 // set it on the document
398 if (aStatus.IsEmpty()) {
399 IgnoredErrorResult ignored;
400 SetTitle(title, ignored);
401 } else {
402 nsAutoString titleWithStatus;
403 AutoTArray<nsString, 2> formatStrings;
404 formatStrings.AppendElement(title);
405 formatStrings.AppendElement(aStatus);
406 FormatStringFromName("TitleWithStatus", formatStrings, titleWithStatus);
407 SetTitle(titleWithStatus, IgnoreErrors());
411 } // namespace mozilla::dom