1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "mozilla/dom/TextTrackCue.h"
7
8 #include "mozilla/dom/Document.h"
9 #include "mozilla/dom/HTMLTrackElement.h"
10 #include "mozilla/dom/TextTrackList.h"
11 #include "mozilla/dom/TextTrackRegion.h"
12 #include "nsComponentManagerUtils.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "unicode/ubidi.h"
15
16 extern mozilla::LazyLogModule gTextTrackLog;
17
18 #define LOG(msg, ...) \
19 MOZ_LOG(gTextTrackLog, LogLevel::Debug, \
20 ("TextTrackCue=%p, " msg, this, ##__VA_ARGS__))
21
22 namespace mozilla::dom {
23
24 NS_IMPL_CYCLE_COLLECTION_INHERITED(TextTrackCue, DOMEventTargetHelper,
25 mDocument, mTrack, mTrackElement,
26 mDisplayState, mRegion)
27
28 NS_IMPL_ADDREF_INHERITED(TextTrackCue, DOMEventTargetHelper)
29 NS_IMPL_RELEASE_INHERITED(TextTrackCue, DOMEventTargetHelper)
30 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackCue)
31 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
32
33 StaticRefPtr<nsIWebVTTParserWrapper> TextTrackCue::sParserWrapper;
34
35 // Set default value for cue, spec https://w3c.github.io/webvtt/#model-cues
SetDefaultCueSettings()36 void TextTrackCue::SetDefaultCueSettings() {
37 mPositionIsAutoKeyword = true;
38 // Spec https://www.w3.org/TR/webvtt1/#webvtt-cue-position-automatic-alignment
39 mPositionAlign = PositionAlignSetting::Auto;
40 mSize = 100.0;
41 mPauseOnExit = false;
42 mSnapToLines = true;
43 mLineIsAutoKeyword = true;
44 mAlign = AlignSetting::Center;
45 mLineAlign = LineAlignSetting::Start;
46 mVertical = DirectionSetting::_empty;
47 mActive = false;
48 }
49
TextTrackCue(nsPIDOMWindowInner * aOwnerWindow,double aStartTime,double aEndTime,const nsAString & aText,ErrorResult & aRv)50 TextTrackCue::TextTrackCue(nsPIDOMWindowInner* aOwnerWindow, double aStartTime,
51 double aEndTime, const nsAString& aText,
52 ErrorResult& aRv)
53 : DOMEventTargetHelper(aOwnerWindow),
54 mText(aText),
55 mStartTime(aStartTime),
56 mEndTime(aEndTime),
57 mPosition(0.0),
58 mLine(0.0),
59 mReset(false, "TextTrackCue::mReset"),
60 mHaveStartedWatcher(false),
61 mWatchManager(
62 this, GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other)) {
63 LOG("create TextTrackCue");
64 SetDefaultCueSettings();
65 MOZ_ASSERT(aOwnerWindow);
66 if (NS_FAILED(StashDocument())) {
67 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
68 }
69 }
70
TextTrackCue(nsPIDOMWindowInner * aOwnerWindow,double aStartTime,double aEndTime,const nsAString & aText,HTMLTrackElement * aTrackElement,ErrorResult & aRv)71 TextTrackCue::TextTrackCue(nsPIDOMWindowInner* aOwnerWindow, double aStartTime,
72 double aEndTime, const nsAString& aText,
73 HTMLTrackElement* aTrackElement, ErrorResult& aRv)
74 : DOMEventTargetHelper(aOwnerWindow),
75 mText(aText),
76 mStartTime(aStartTime),
77 mEndTime(aEndTime),
78 mTrackElement(aTrackElement),
79 mPosition(0.0),
80 mLine(0.0),
81 mReset(false, "TextTrackCue::mReset"),
82 mHaveStartedWatcher(false),
83 mWatchManager(
84 this, GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other)) {
85 LOG("create TextTrackCue");
86 SetDefaultCueSettings();
87 MOZ_ASSERT(aOwnerWindow);
88 if (NS_FAILED(StashDocument())) {
89 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
90 }
91 }
92
93 TextTrackCue::~TextTrackCue() = default;
94
95 /** Save a reference to our creating document so we don't have to
96 * keep getting it from our window.
97 */
StashDocument()98 nsresult TextTrackCue::StashDocument() {
99 nsPIDOMWindowInner* window = GetOwner();
100 if (!window) {
101 return NS_ERROR_NO_INTERFACE;
102 }
103 mDocument = window->GetDoc();
104 if (!mDocument) {
105 return NS_ERROR_NOT_AVAILABLE;
106 }
107 return NS_OK;
108 }
109
GetCueAsHTML()110 already_AddRefed<DocumentFragment> TextTrackCue::GetCueAsHTML() {
111 // mDocument may be null during cycle collector shutdown.
112 // See bug 941701.
113 if (!mDocument) {
114 return nullptr;
115 }
116
117 if (!sParserWrapper) {
118 nsresult rv;
119 nsCOMPtr<nsIWebVTTParserWrapper> parserWrapper =
120 do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID, &rv);
121 if (NS_FAILED(rv)) {
122 return mDocument->CreateDocumentFragment();
123 }
124 sParserWrapper = parserWrapper;
125 ClearOnShutdown(&sParserWrapper);
126 }
127
128 nsPIDOMWindowInner* window = mDocument->GetInnerWindow();
129 if (!window) {
130 return mDocument->CreateDocumentFragment();
131 }
132
133 RefPtr<DocumentFragment> frag;
134 sParserWrapper->ConvertCueToDOMTree(window, this, getter_AddRefs(frag));
135 if (!frag) {
136 return mDocument->CreateDocumentFragment();
137 }
138 return frag.forget();
139 }
140
SetTrackElement(HTMLTrackElement * aTrackElement)141 void TextTrackCue::SetTrackElement(HTMLTrackElement* aTrackElement) {
142 mTrackElement = aTrackElement;
143 }
144
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)145 JSObject* TextTrackCue::WrapObject(JSContext* aCx,
146 JS::Handle<JSObject*> aGivenProto) {
147 return VTTCue_Binding::Wrap(aCx, this, aGivenProto);
148 }
149
GetRegion()150 TextTrackRegion* TextTrackCue::GetRegion() { return mRegion; }
151
SetRegion(TextTrackRegion * aRegion)152 void TextTrackCue::SetRegion(TextTrackRegion* aRegion) {
153 if (mRegion == aRegion) {
154 return;
155 }
156 mRegion = aRegion;
157 mReset = true;
158 }
159
ComputedLine()160 double TextTrackCue::ComputedLine() {
161 // See spec https://w3c.github.io/webvtt/#cue-computed-line
162 if (!mLineIsAutoKeyword && !mSnapToLines && (mLine < 0.0 || mLine > 100.0)) {
163 return 100.0;
164 } else if (!mLineIsAutoKeyword) {
165 return mLine;
166 } else if (mLineIsAutoKeyword && !mSnapToLines) {
167 return 100.0;
168 } else if (!mTrack || !mTrack->GetTextTrackList() ||
169 !mTrack->GetTextTrackList()->GetMediaElement()) {
170 return -1.0;
171 }
172
173 RefPtr<TextTrackList> trackList = mTrack->GetTextTrackList();
174 bool dummy;
175 uint32_t showingTracksNum = 0;
176 for (uint32_t idx = 0; idx < trackList->Length(); idx++) {
177 RefPtr<TextTrack> track = trackList->IndexedGetter(idx, dummy);
178 if (track->Mode() == TextTrackMode::Showing) {
179 showingTracksNum++;
180 }
181
182 if (mTrack == track) {
183 break;
184 }
185 }
186
187 return (-1.0) * showingTracksNum;
188 }
189
ComputedPosition()190 double TextTrackCue::ComputedPosition() {
191 // See spec https://w3c.github.io/webvtt/#cue-computed-position
192 if (!mPositionIsAutoKeyword) {
193 return mPosition;
194 }
195 if (ComputedPositionAlign() == PositionAlignSetting::Line_left) {
196 return 0.0;
197 }
198 if (ComputedPositionAlign() == PositionAlignSetting::Line_right) {
199 return 100.0;
200 }
201 return 50.0;
202 }
203
ComputedPositionAlign()204 PositionAlignSetting TextTrackCue::ComputedPositionAlign() {
205 // See spec https://w3c.github.io/webvtt/#cue-computed-position-alignment
206 if (mPositionAlign != PositionAlignSetting::Auto) {
207 return mPositionAlign;
208 } else if (mAlign == AlignSetting::Left) {
209 return PositionAlignSetting::Line_left;
210 } else if (mAlign == AlignSetting::Right) {
211 return PositionAlignSetting::Line_right;
212 } else if (mAlign == AlignSetting::Start) {
213 return IsTextBaseDirectionLTR() ? PositionAlignSetting::Line_left
214 : PositionAlignSetting::Line_right;
215 } else if (mAlign == AlignSetting::End) {
216 return IsTextBaseDirectionLTR() ? PositionAlignSetting::Line_right
217 : PositionAlignSetting::Line_left;
218 }
219 return PositionAlignSetting::Center;
220 }
221
IsTextBaseDirectionLTR() const222 bool TextTrackCue::IsTextBaseDirectionLTR() const {
223 // The returned result by `ubidi_getBaseDirection` might be `neutral` if the
224 // text only contains netural charaters. In this case, we would treat its
225 // base direction as LTR.
226 return ubidi_getBaseDirection(mText.BeginReading(), mText.Length()) !=
227 UBIDI_RTL;
228 }
229
NotifyDisplayStatesChanged()230 void TextTrackCue::NotifyDisplayStatesChanged() {
231 if (!mReset) {
232 return;
233 }
234
235 if (!mTrack || !mTrack->GetTextTrackList() ||
236 !mTrack->GetTextTrackList()->GetMediaElement()) {
237 return;
238 }
239
240 mTrack->GetTextTrackList()
241 ->GetMediaElement()
242 ->NotifyCueDisplayStatesChanged();
243 }
244
SetActive(bool aActive)245 void TextTrackCue::SetActive(bool aActive) {
246 if (mActive == aActive) {
247 return;
248 }
249
250 LOG("TextTrackCue, SetActive=%d", aActive);
251 mActive = aActive;
252 mDisplayState = mActive ? mDisplayState : nullptr;
253 if (mTrack) {
254 mTrack->NotifyCueActiveStateChanged(this);
255 }
256 }
257
258 #undef LOG
259
260 } // namespace mozilla::dom
261