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