Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/MediaDocument.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsRect.h"
10
#include "nsPresContext.h"
11
#include "nsIPresShell.h"
12
#include "nsIScrollable.h"
13
#include "nsViewManager.h"
14
#include "nsITextToSubURI.h"
15
#include "nsIURL.h"
16
#include "nsIContentViewer.h"
17
#include "nsIDocShell.h"
18
#include "nsCharsetSource.h" // kCharsetFrom* macro definition
19
#include "nsNodeInfoManager.h"
20
#include "nsContentUtils.h"
21
#include "nsDocElementCreatedNotificationRunner.h"
22
#include "mozilla/Services.h"
23
#include "nsServiceManagerUtils.h"
24
#include "nsIPrincipal.h"
25
#include "nsIMultiPartChannel.h"
26
#include "nsProxyRelease.h"
27
28
namespace mozilla {
29
namespace dom {
30
31
MediaDocumentStreamListener::MediaDocumentStreamListener(MediaDocument *aDocument)
32
  : mDocument(aDocument)
33
0
{
34
0
}
35
36
MediaDocumentStreamListener::~MediaDocumentStreamListener()
37
0
{
38
0
  if (mDocument && !NS_IsMainThread()) {
39
0
    nsCOMPtr<nsIEventTarget> mainTarget(do_GetMainThread());
40
0
    NS_ProxyRelease("MediaDocumentStreamListener::mDocument",
41
0
                    mainTarget, mDocument.forget());
42
0
  }
43
0
}
44
45
46
NS_IMPL_ISUPPORTS(MediaDocumentStreamListener,
47
                  nsIRequestObserver,
48
                  nsIStreamListener,
49
                  nsIThreadRetargetableStreamListener)
50
51
52
void
53
MediaDocumentStreamListener::SetStreamListener(nsIStreamListener *aListener)
54
0
{
55
0
  mNextStream = aListener;
56
0
}
57
58
NS_IMETHODIMP
59
MediaDocumentStreamListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
60
0
{
61
0
  NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
62
0
63
0
  mDocument->StartLayout();
64
0
65
0
  if (mNextStream) {
66
0
    return mNextStream->OnStartRequest(request, ctxt);
67
0
  }
68
0
69
0
  return NS_ERROR_PARSED_DATA_CACHED;
70
0
}
71
72
NS_IMETHODIMP
73
MediaDocumentStreamListener::OnStopRequest(nsIRequest* request,
74
                                           nsISupports *ctxt,
75
                                           nsresult status)
76
0
{
77
0
  nsresult rv = NS_OK;
78
0
  if (mNextStream) {
79
0
    rv = mNextStream->OnStopRequest(request, ctxt, status);
80
0
  }
81
0
82
0
  // Don't release mDocument here if we're in the middle of a multipart response.
83
0
  bool lastPart = true;
84
0
  nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(request));
85
0
  if (mpchan) {
86
0
    mpchan->GetIsLastPart(&lastPart);
87
0
  }
88
0
89
0
  if (lastPart) {
90
0
    mDocument = nullptr;
91
0
  }
92
0
  return rv;
93
0
}
94
95
NS_IMETHODIMP
96
MediaDocumentStreamListener::OnDataAvailable(nsIRequest* request,
97
                                             nsISupports *ctxt,
98
                                             nsIInputStream *inStr,
99
                                             uint64_t sourceOffset,
100
                                             uint32_t count)
101
0
{
102
0
  if (mNextStream) {
103
0
    return mNextStream->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
104
0
  }
105
0
106
0
  return NS_OK;
107
0
}
108
109
NS_IMETHODIMP
110
MediaDocumentStreamListener::CheckListenerChain()
111
0
{
112
0
  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
113
0
    do_QueryInterface(mNextStream);
114
0
  if (retargetable) {
115
0
    return retargetable->CheckListenerChain();
116
0
  }
117
0
  return NS_ERROR_NO_INTERFACE;
118
0
}
119
120
// default format names for MediaDocument.
121
const char* const MediaDocument::sFormatNames[4] =
122
{
123
  "MediaTitleWithNoInfo",    // eWithNoInfo
124
  "MediaTitleWithFile",      // eWithFile
125
  "",                        // eWithDim
126
  ""                         // eWithDimAndFile
127
};
128
129
MediaDocument::MediaDocument()
130
    : nsHTMLDocument(),
131
      mDidInitialDocumentSetup(false)
132
0
{
133
0
}
134
MediaDocument::~MediaDocument()
135
0
{
136
0
}
137
138
nsresult
139
MediaDocument::Init()
140
0
{
141
0
  nsresult rv = nsHTMLDocument::Init();
142
0
  NS_ENSURE_SUCCESS(rv, rv);
143
0
144
0
  // Create a bundle for the localization
145
0
  nsCOMPtr<nsIStringBundleService> stringService =
146
0
    mozilla::services::GetStringBundleService();
147
0
  if (stringService) {
148
0
    stringService->CreateBundle(NSMEDIADOCUMENT_PROPERTIES_URI,
149
0
                                getter_AddRefs(mStringBundle));
150
0
  }
151
0
152
0
  mIsSyntheticDocument = true;
153
0
154
0
  return NS_OK;
155
0
}
156
157
nsresult
158
MediaDocument::StartDocumentLoad(const char*         aCommand,
159
                                 nsIChannel*         aChannel,
160
                                 nsILoadGroup*       aLoadGroup,
161
                                 nsISupports*        aContainer,
162
                                 nsIStreamListener** aDocListener,
163
                                 bool                aReset,
164
                                 nsIContentSink*     aSink)
165
0
{
166
0
  nsresult rv = nsDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup,
167
0
                                              aContainer, aDocListener, aReset,
168
0
                                              aSink);
169
0
  if (NS_FAILED(rv)) {
170
0
    return rv;
171
0
  }
172
0
173
0
  // We try to set the charset of the current document to that of the
174
0
  // 'genuine' (as opposed to an intervening 'chrome') parent document
175
0
  // that may be in a different window/tab. Even if we fail here,
176
0
  // we just return NS_OK because another attempt is made in
177
0
  // |UpdateTitleAndCharset| and the worst thing possible is a mangled
178
0
  // filename in the titlebar and the file picker.
179
0
180
0
  // Note that we
181
0
  // exclude UTF-8 as 'invalid' because UTF-8 is likely to be the charset
182
0
  // of a chrome document that has nothing to do with the actual content
183
0
  // whose charset we want to know. Even if "the actual content" is indeed
184
0
  // in UTF-8, we don't lose anything because the default empty value is
185
0
  // considered synonymous with UTF-8.
186
0
187
0
  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
188
0
189
0
  // not being able to set the charset is not critical.
190
0
  NS_ENSURE_TRUE(docShell, NS_OK);
191
0
192
0
  const Encoding* encoding;
193
0
  int32_t source;
194
0
  nsCOMPtr<nsIPrincipal> principal;
195
0
  // opening in a new tab
196
0
  docShell->GetParentCharset(encoding, &source, getter_AddRefs(principal));
197
0
198
0
  if (encoding && encoding != UTF_8_ENCODING &&
199
0
      NodePrincipal()->Equals(principal)) {
200
0
    SetDocumentCharacterSetSource(source);
201
0
    SetDocumentCharacterSet(WrapNotNull(encoding));
202
0
  }
203
0
204
0
  return NS_OK;
205
0
}
206
207
void
208
MediaDocument::InitialSetupDone()
209
0
{
210
0
  MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING,
211
0
             "Bad readyState: we should still be doing our initial load");
212
0
  mDidInitialDocumentSetup = true;
213
0
  nsContentUtils::AddScriptRunner(
214
0
    new nsDocElementCreatedNotificationRunner(this));
215
0
  SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
216
0
}
217
218
nsresult
219
MediaDocument::CreateSyntheticDocument()
220
0
{
221
0
  MOZ_ASSERT(!InitialSetupHasBeenDone());
222
0
223
0
  // Synthesize an empty html document
224
0
  nsresult rv;
225
0
226
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
227
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nullptr,
228
0
                                           kNameSpaceID_XHTML,
229
0
                                           nsINode::ELEMENT_NODE);
230
0
231
0
  RefPtr<nsGenericHTMLElement> root = NS_NewHTMLHtmlElement(nodeInfo.forget());
232
0
  NS_ENSURE_TRUE(root, NS_ERROR_OUT_OF_MEMORY);
233
0
234
0
  NS_ASSERTION(GetChildCount() == 0, "Shouldn't have any kids");
235
0
  rv = AppendChildTo(root, false);
236
0
  NS_ENSURE_SUCCESS(rv, rv);
237
0
238
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head, nullptr,
239
0
                                           kNameSpaceID_XHTML,
240
0
                                           nsINode::ELEMENT_NODE);
241
0
242
0
  // Create a <head> so our title has somewhere to live
243
0
  RefPtr<nsGenericHTMLElement> head = NS_NewHTMLHeadElement(nodeInfo.forget());
244
0
  NS_ENSURE_TRUE(head, NS_ERROR_OUT_OF_MEMORY);
245
0
246
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::meta, nullptr,
247
0
                                           kNameSpaceID_XHTML,
248
0
                                           nsINode::ELEMENT_NODE);
249
0
250
0
  RefPtr<nsGenericHTMLElement> metaContent = NS_NewHTMLMetaElement(nodeInfo.forget());
251
0
  NS_ENSURE_TRUE(metaContent, NS_ERROR_OUT_OF_MEMORY);
252
0
  metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
253
0
                       NS_LITERAL_STRING("viewport"),
254
0
                       true);
255
0
256
0
  metaContent->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
257
0
                       NS_LITERAL_STRING("width=device-width; height=device-height;"),
258
0
                       true);
259
0
  head->AppendChildTo(metaContent, false);
260
0
261
0
  root->AppendChildTo(head, false);
262
0
263
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::body, nullptr,
264
0
                                           kNameSpaceID_XHTML,
265
0
                                           nsINode::ELEMENT_NODE);
266
0
267
0
  RefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo.forget());
268
0
  NS_ENSURE_TRUE(body, NS_ERROR_OUT_OF_MEMORY);
269
0
270
0
  root->AppendChildTo(body, false);
271
0
272
0
  return NS_OK;
273
0
}
274
275
nsresult
276
MediaDocument::StartLayout()
277
0
{
278
0
  mMayStartLayout = true;
279
0
  nsCOMPtr<nsIPresShell> shell = GetShell();
280
0
  // Don't mess with the presshell if someone has already handled
281
0
  // its initial reflow.
282
0
  if (shell && !shell->DidInitialize()) {
283
0
    nsresult rv = shell->Initialize();
284
0
    NS_ENSURE_SUCCESS(rv, rv);
285
0
  }
286
0
287
0
  return NS_OK;
288
0
}
289
290
void
291
MediaDocument::GetFileName(nsAString& aResult, nsIChannel* aChannel)
292
0
{
293
0
  aResult.Truncate();
294
0
295
0
  if (aChannel) {
296
0
    aChannel->GetContentDispositionFilename(aResult);
297
0
    if (!aResult.IsEmpty())
298
0
      return;
299
0
  }
300
0
301
0
  nsCOMPtr<nsIURL> url = do_QueryInterface(mDocumentURI);
302
0
  if (!url)
303
0
    return;
304
0
305
0
  nsAutoCString fileName;
306
0
  url->GetFileName(fileName);
307
0
  if (fileName.IsEmpty())
308
0
    return;
309
0
310
0
  nsAutoCString docCharset;
311
0
  // Now that the charset is set in |StartDocumentLoad| to the charset of
312
0
  // the document viewer instead of a bogus value ("windows-1252" set in
313
0
  // |nsDocument|'s ctor), the priority is given to the current charset.
314
0
  // This is necessary to deal with a media document being opened in a new
315
0
  // window or a new tab.
316
0
  if (mCharacterSetSource != kCharsetUninitialized) {
317
0
    mCharacterSet->Name(docCharset);
318
0
  } else {
319
0
    // resort to UTF-8
320
0
    SetDocumentCharacterSet(UTF_8_ENCODING);
321
0
  }
322
0
323
0
  nsresult rv;
324
0
  nsCOMPtr<nsITextToSubURI> textToSubURI =
325
0
    do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
326
0
  if (NS_SUCCEEDED(rv)) {
327
0
    // UnEscapeURIForUI always succeeds
328
0
    textToSubURI->UnEscapeURIForUI(docCharset, fileName, aResult);
329
0
  } else {
330
0
    CopyUTF8toUTF16(fileName, aResult);
331
0
  }
332
0
}
333
334
nsresult
335
MediaDocument::LinkStylesheet(const nsAString& aStylesheet)
336
0
{
337
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
338
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::link, nullptr,
339
0
                                           kNameSpaceID_XHTML,
340
0
                                           nsINode::ELEMENT_NODE);
341
0
342
0
  RefPtr<nsGenericHTMLElement> link = NS_NewHTMLLinkElement(nodeInfo.forget());
343
0
  NS_ENSURE_TRUE(link, NS_ERROR_OUT_OF_MEMORY);
344
0
345
0
  link->SetAttr(kNameSpaceID_None, nsGkAtoms::rel,
346
0
                NS_LITERAL_STRING("stylesheet"), true);
347
0
348
0
  link->SetAttr(kNameSpaceID_None, nsGkAtoms::href, aStylesheet, true);
349
0
350
0
  Element* head = GetHeadElement();
351
0
  return head->AppendChildTo(link, false);
352
0
}
353
354
nsresult
355
MediaDocument::LinkScript(const nsAString& aScript)
356
0
{
357
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
358
0
  nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::script, nullptr,
359
0
                                           kNameSpaceID_XHTML,
360
0
                                           nsINode::ELEMENT_NODE);
361
0
362
0
  RefPtr<nsGenericHTMLElement> script = NS_NewHTMLScriptElement(nodeInfo.forget());
363
0
  NS_ENSURE_TRUE(script, NS_ERROR_OUT_OF_MEMORY);
364
0
365
0
  script->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
366
0
                  NS_LITERAL_STRING("text/javascript"), true);
367
0
368
0
  script->SetAttr(kNameSpaceID_None, nsGkAtoms::src, aScript, true);
369
0
370
0
  Element* head = GetHeadElement();
371
0
  return head->AppendChildTo(script, false);
372
0
}
373
374
void
375
MediaDocument::UpdateTitleAndCharset(const nsACString& aTypeStr,
376
                                     nsIChannel* aChannel,
377
                                     const char* const* aFormatNames,
378
                                     int32_t aWidth, int32_t aHeight,
379
                                     const nsAString& aStatus)
380
0
{
381
0
  nsAutoString fileStr;
382
0
  GetFileName(fileStr, aChannel);
383
0
384
0
  NS_ConvertASCIItoUTF16 typeStr(aTypeStr);
385
0
  nsAutoString title;
386
0
387
0
  if (mStringBundle) {
388
0
    // if we got a valid size (not all media have a size)
389
0
    if (aWidth != 0 && aHeight != 0) {
390
0
      nsAutoString widthStr;
391
0
      nsAutoString heightStr;
392
0
      widthStr.AppendInt(aWidth);
393
0
      heightStr.AppendInt(aHeight);
394
0
      // If we got a filename, display it
395
0
      if (!fileStr.IsEmpty()) {
396
0
        const char16_t *formatStrings[4]  = {fileStr.get(), typeStr.get(),
397
0
          widthStr.get(), heightStr.get()};
398
0
        mStringBundle->FormatStringFromName(aFormatNames[eWithDimAndFile],
399
0
                                            formatStrings, 4, title);
400
0
      }
401
0
      else {
402
0
        const char16_t *formatStrings[3]  = {typeStr.get(), widthStr.get(),
403
0
          heightStr.get()};
404
0
        mStringBundle->FormatStringFromName(aFormatNames[eWithDim],
405
0
                                            formatStrings, 3, title);
406
0
      }
407
0
    }
408
0
    else {
409
0
    // If we got a filename, display it
410
0
      if (!fileStr.IsEmpty()) {
411
0
        const char16_t *formatStrings[2] = {fileStr.get(), typeStr.get()};
412
0
        mStringBundle->FormatStringFromName(aFormatNames[eWithFile],
413
0
                                            formatStrings, 2, title);
414
0
      }
415
0
      else {
416
0
        const char16_t *formatStrings[1] = {typeStr.get()};
417
0
        mStringBundle->FormatStringFromName(aFormatNames[eWithNoInfo],
418
0
                                            formatStrings, 1, title);
419
0
      }
420
0
    }
421
0
  }
422
0
423
0
  // set it on the document
424
0
  if (aStatus.IsEmpty()) {
425
0
    IgnoredErrorResult ignored;
426
0
    SetTitle(title, ignored);
427
0
  }
428
0
  else {
429
0
    nsAutoString titleWithStatus;
430
0
    const nsPromiseFlatString& status = PromiseFlatString(aStatus);
431
0
    const char16_t *formatStrings[2] = {title.get(), status.get()};
432
0
    mStringBundle->FormatStringFromName("TitleWithStatus", formatStrings,
433
0
                                        2, titleWithStatus);
434
0
    IgnoredErrorResult ignored;
435
0
    SetTitle(titleWithStatus, ignored);
436
0
  }
437
0
}
438
439
} // namespace dom
440
} // namespace mozilla