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/. */
6
7 #include "MediaDocument.h"
8 #include "nsGkAtoms.h"
9 #include "nsNodeInfoManager.h"
10 #include "nsContentCreatorFunctions.h"
11 #include "mozilla/dom/HTMLMediaElement.h"
12 #include "DocumentInlines.h"
13 #include "nsContentUtils.h"
14 #include "mozilla/dom/Element.h"
15
16 namespace mozilla::dom {
17
18 class VideoDocument final : public MediaDocument {
19 public:
MediaDocumentKind() const20 enum MediaDocumentKind MediaDocumentKind() const override {
21 return MediaDocumentKind::Video;
22 }
23
24 virtual nsresult StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
25 nsILoadGroup* aLoadGroup,
26 nsISupports* aContainer,
27 nsIStreamListener** aDocListener,
28 bool aReset = true,
29 nsIContentSink* aSink = nullptr) override;
30 virtual void SetScriptGlobalObject(
31 nsIScriptGlobalObject* aScriptGlobalObject) override;
32
Destroy()33 virtual void Destroy() override {
34 if (mStreamListener) {
35 mStreamListener->DropDocumentRef();
36 }
37 MediaDocument::Destroy();
38 }
39
40 nsresult StartLayout() override;
41
42 protected:
43 nsresult CreateVideoElement();
44 // Sets document <title> to reflect the file name and description.
45 void UpdateTitle(nsIChannel* aChannel);
46
47 RefPtr<MediaDocumentStreamListener> mStreamListener;
48 };
49
StartDocumentLoad(const char * aCommand,nsIChannel * aChannel,nsILoadGroup * aLoadGroup,nsISupports * aContainer,nsIStreamListener ** aDocListener,bool aReset,nsIContentSink * aSink)50 nsresult VideoDocument::StartDocumentLoad(const char* aCommand,
51 nsIChannel* aChannel,
52 nsILoadGroup* aLoadGroup,
53 nsISupports* aContainer,
54 nsIStreamListener** aDocListener,
55 bool aReset, nsIContentSink* aSink) {
56 nsresult rv = MediaDocument::StartDocumentLoad(
57 aCommand, aChannel, aLoadGroup, aContainer, aDocListener, aReset, aSink);
58 NS_ENSURE_SUCCESS(rv, rv);
59
60 mStreamListener = new MediaDocumentStreamListener(this);
61 NS_ADDREF(*aDocListener = mStreamListener);
62 return rv;
63 }
64
StartLayout()65 nsresult VideoDocument::StartLayout() {
66 // Create video element, and begin loading the media resource. Note we
67 // delay creating the video element until now (we're called from
68 // MediaDocumentStreamListener::OnStartRequest) as the PresShell is likely
69 // to have been created by now, so the MediaDecoder will be able to tell
70 // what kind of compositor we have, so the video element knows whether
71 // it can create a hardware accelerated video decoder or not.
72 nsresult rv = CreateVideoElement();
73 NS_ENSURE_SUCCESS(rv, rv);
74
75 rv = MediaDocument::StartLayout();
76 NS_ENSURE_SUCCESS(rv, rv);
77
78 return NS_OK;
79 }
80
SetScriptGlobalObject(nsIScriptGlobalObject * aScriptGlobalObject)81 void VideoDocument::SetScriptGlobalObject(
82 nsIScriptGlobalObject* aScriptGlobalObject) {
83 // Set the script global object on the superclass before doing
84 // anything that might require it....
85 MediaDocument::SetScriptGlobalObject(aScriptGlobalObject);
86
87 if (aScriptGlobalObject && !InitialSetupHasBeenDone()) {
88 DebugOnly<nsresult> rv = CreateSyntheticDocument();
89 NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic video document");
90
91 if (!nsContentUtils::IsChildOfSameType(this)) {
92 LinkStylesheet(nsLiteralString(
93 u"resource://content-accessible/TopLevelVideoDocument.css"));
94 LinkStylesheet(nsLiteralString(
95 u"chrome://global/skin/media/TopLevelVideoDocument.css"));
96 LinkScript(u"chrome://global/content/TopLevelVideoDocument.js"_ns);
97 }
98 InitialSetupDone();
99 }
100 }
101
CreateVideoElement()102 nsresult VideoDocument::CreateVideoElement() {
103 RefPtr<Element> body = GetBodyElement();
104 if (!body) {
105 NS_WARNING("no body on video document!");
106 return NS_ERROR_FAILURE;
107 }
108
109 // make content
110 RefPtr<mozilla::dom::NodeInfo> nodeInfo;
111 nodeInfo = mNodeInfoManager->GetNodeInfo(
112 nsGkAtoms::video, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
113
114 RefPtr<HTMLMediaElement> element = static_cast<HTMLMediaElement*>(
115 NS_NewHTMLVideoElement(nodeInfo.forget(), NOT_FROM_PARSER));
116 if (!element) return NS_ERROR_OUT_OF_MEMORY;
117 element->SetAutoplay(true, IgnoreErrors());
118 element->SetControls(true, IgnoreErrors());
119 element->LoadWithChannel(mChannel,
120 getter_AddRefs(mStreamListener->mNextStream));
121 UpdateTitle(mChannel);
122
123 if (nsContentUtils::IsChildOfSameType(this)) {
124 // Video documents that aren't toplevel should fill their frames and
125 // not have margins
126 element->SetAttr(
127 kNameSpaceID_None, nsGkAtoms::style,
128 nsLiteralString(
129 u"position:absolute; top:0; left:0; width:100%; height:100%"),
130 true);
131 }
132
133 ErrorResult rv;
134 body->AppendChildTo(element, false, rv);
135 return rv.StealNSResult();
136 }
137
UpdateTitle(nsIChannel * aChannel)138 void VideoDocument::UpdateTitle(nsIChannel* aChannel) {
139 if (!aChannel) return;
140
141 nsAutoString fileName;
142 GetFileName(fileName, aChannel);
143 IgnoredErrorResult ignored;
144 SetTitle(fileName, ignored);
145 }
146
147 } // namespace mozilla::dom
148
NS_NewVideoDocument(mozilla::dom::Document ** aResult)149 nsresult NS_NewVideoDocument(mozilla::dom::Document** aResult) {
150 auto* doc = new mozilla::dom::VideoDocument();
151
152 NS_ADDREF(doc);
153 nsresult rv = doc->Init();
154
155 if (NS_FAILED(rv)) {
156 NS_RELEASE(doc);
157 }
158
159 *aResult = doc;
160
161 return rv;
162 }
163