tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaDocument.cpp (13852B)


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