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