content/base/src/nsHostObjectProtocolHandler.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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/. */
     7 #include "nsHostObjectProtocolHandler.h"
     8 #include "nsHostObjectURI.h"
     9 #include "nsError.h"
    10 #include "nsClassHashtable.h"
    11 #include "nsNetUtil.h"
    12 #include "nsIPrincipal.h"
    13 #include "nsIDOMFile.h"
    14 #include "nsIDOMMediaStream.h"
    15 #include "mozilla/dom/MediaSource.h"
    16 #include "nsIMemoryReporter.h"
    17 #include "mozilla/Preferences.h"
    19 // -----------------------------------------------------------------------
    20 // Hash table
    21 struct DataInfo
    22 {
    23   // mObject is expected to be an nsIDOMBlob, nsIDOMMediaStream, or MediaSource
    24   nsCOMPtr<nsISupports> mObject;
    25   nsCOMPtr<nsIPrincipal> mPrincipal;
    26   nsCString mStack;
    27 };
    29 static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
    31 // Memory reporting for the hash table.
    32 namespace mozilla {
    34 class HostObjectURLsReporter MOZ_FINAL : public nsIMemoryReporter
    35 {
    36  public:
    37   NS_DECL_ISUPPORTS
    39   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    40                             nsISupports* aData)
    41   {
    42     return MOZ_COLLECT_REPORT(
    43       "host-object-urls", KIND_OTHER, UNITS_COUNT,
    44       gDataTable ? gDataTable->Count() : 0,
    45       "The number of host objects stored for access via URLs "
    46       "(e.g. blobs passed to URL.createObjectURL).");
    47   }
    48 };
    50 NS_IMPL_ISUPPORTS(HostObjectURLsReporter, nsIMemoryReporter)
    52 class BlobURLsReporter MOZ_FINAL : public nsIMemoryReporter
    53 {
    54  public:
    55   NS_DECL_ISUPPORTS
    57   NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback,
    58                             nsISupports* aData)
    59   {
    60     EnumArg env;
    61     env.mCallback = aCallback;
    62     env.mData = aData;
    64     if (gDataTable) {
    65       gDataTable->EnumerateRead(CountCallback, &env);
    66       gDataTable->EnumerateRead(ReportCallback, &env);
    67     }
    68     return NS_OK;
    69   }
    71   // Initialize info->mStack to record JS stack info, if enabled.
    72   // The string generated here is used in ReportCallback, below.
    73   static void GetJSStackForBlob(DataInfo* aInfo)
    74   {
    75     nsCString& stack = aInfo->mStack;
    76     MOZ_ASSERT(stack.IsEmpty());
    77     const uint32_t maxFrames = Preferences::GetUint("memory.blob_report.stack_frames");
    79     if (maxFrames == 0) {
    80       return;
    81     }
    83     nsresult rv;
    84     nsIXPConnect* xpc = nsContentUtils::XPConnect();
    85     nsCOMPtr<nsIStackFrame> frame;
    86     rv = xpc->GetCurrentJSStack(getter_AddRefs(frame));
    87     NS_ENSURE_SUCCESS_VOID(rv);
    89     nsAutoCString origin;
    90     nsCOMPtr<nsIURI> principalURI;
    91     if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI)))
    92         && principalURI) {
    93       principalURI->GetPrePath(origin);
    94     }
    96     for (uint32_t i = 0; i < maxFrames && frame; ++i) {
    97       nsString fileNameUTF16;
    98       int32_t lineNumber = 0;
   100       frame->GetFilename(fileNameUTF16);
   101       frame->GetLineNumber(&lineNumber);
   103       if (!fileNameUTF16.IsEmpty()) {
   104         NS_ConvertUTF16toUTF8 fileName(fileNameUTF16);
   105         stack += "js(";
   106         if (!origin.IsEmpty()) {
   107           // Make the file name root-relative for conciseness if possible.
   108           const char* originData;
   109           uint32_t originLen;
   111           originLen = origin.GetData(&originData);
   112           // If fileName starts with origin + "/", cut up to that "/".
   113           if (fileName.Length() >= originLen + 1 &&
   114               memcmp(fileName.get(), originData, originLen) == 0 &&
   115               fileName[originLen] == '/') {
   116             fileName.Cut(0, originLen);
   117           }
   118         }
   119         fileName.ReplaceChar('/', '\\');
   120         stack += fileName;
   121         if (lineNumber > 0) {
   122           stack += ", line=";
   123           stack.AppendInt(lineNumber);
   124         }
   125         stack += ")/";
   126       }
   128       rv = frame->GetCaller(getter_AddRefs(frame));
   129       NS_ENSURE_SUCCESS_VOID(rv);
   130     }
   131   }
   133  private:
   134   struct EnumArg {
   135     nsIHandleReportCallback* mCallback;
   136     nsISupports* mData;
   137     nsDataHashtable<nsPtrHashKey<nsIDOMBlob>, uint32_t> mRefCounts;
   138   };
   140   // Determine number of URLs per blob, to handle the case where it's > 1.
   141   static PLDHashOperator CountCallback(nsCStringHashKey::KeyType aKey,
   142                                        DataInfo* aInfo,
   143                                        void* aUserArg)
   144   {
   145     EnumArg* envp = static_cast<EnumArg*>(aUserArg);
   146     nsCOMPtr<nsIDOMBlob> blob;
   148     blob = do_QueryInterface(aInfo->mObject);
   149     if (blob) {
   150       envp->mRefCounts.Put(blob, envp->mRefCounts.Get(blob) + 1);
   151     }
   152     return PL_DHASH_NEXT;
   153   }
   155   static PLDHashOperator ReportCallback(nsCStringHashKey::KeyType aKey,
   156                                         DataInfo* aInfo,
   157                                         void* aUserArg)
   158   {
   159     EnumArg* envp = static_cast<EnumArg*>(aUserArg);
   160     nsCOMPtr<nsIDOMBlob> blob;
   162     blob = do_QueryInterface(aInfo->mObject);
   163     if (blob) {
   164       NS_NAMED_LITERAL_CSTRING
   165         (desc, "A blob URL allocated with URL.createObjectURL; the referenced "
   166          "blob cannot be freed until all URLs for it have been explicitly "
   167          "invalidated with URL.revokeObjectURL.");
   168       nsAutoCString path, url, owner, specialDesc;
   169       nsCOMPtr<nsIURI> principalURI;
   170       uint64_t size = 0;
   171       uint32_t refCount = 1;
   172       DebugOnly<bool> blobWasCounted;
   174       blobWasCounted = envp->mRefCounts.Get(blob, &refCount);
   175       MOZ_ASSERT(blobWasCounted);
   176       MOZ_ASSERT(refCount > 0);
   178       bool isMemoryFile = blob->IsMemoryFile();
   180       if (isMemoryFile) {
   181         if (NS_FAILED(blob->GetSize(&size))) {
   182           size = 0;
   183         }
   184       }
   186       path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
   187       if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) &&
   188           principalURI != nullptr &&
   189           NS_SUCCEEDED(principalURI->GetSpec(owner)) &&
   190           !owner.IsEmpty()) {
   191         owner.ReplaceChar('/', '\\');
   192         path += "owner(";
   193         path += owner;
   194         path += ")";
   195       } else {
   196         path += "owner unknown";
   197       }
   198       path += "/";
   199       path += aInfo->mStack;
   200       url = aKey;
   201       url.ReplaceChar('/', '\\');
   202       path += url;
   203       if (refCount > 1) {
   204         nsAutoCString addrStr;
   206         addrStr = "0x";
   207         addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16);
   209         path += " ";
   210         path.AppendInt(refCount);
   211         path += "@";
   212         path += addrStr;
   214         specialDesc = desc;
   215         specialDesc += "\n\nNOTE: This blob (address ";
   216         specialDesc += addrStr;
   217         specialDesc += ") has ";
   218         specialDesc.AppendInt(refCount);
   219         specialDesc += " URLs.";
   220         if (isMemoryFile) {
   221           specialDesc += " Its size is divided ";
   222           specialDesc += refCount > 2 ? "among" : "between";
   223           specialDesc += " them in this report.";
   224         }
   225       }
   227       const nsACString& descString = specialDesc.IsEmpty()
   228           ? static_cast<const nsACString&>(desc)
   229           : static_cast<const nsACString&>(specialDesc);
   230       if (isMemoryFile) {
   231         envp->mCallback->Callback(EmptyCString(),
   232             path,
   233             KIND_OTHER,
   234             UNITS_BYTES,
   235             size / refCount,
   236             descString,
   237             envp->mData);
   238       }
   239       else {
   240         envp->mCallback->Callback(EmptyCString(),
   241             path,
   242             KIND_OTHER,
   243             UNITS_COUNT,
   244             1,
   245             descString,
   246             envp->mData);
   247       }
   248     }
   249     return PL_DHASH_NEXT;
   250   }
   251 };
   253 NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
   255 }
   257 void
   258 nsHostObjectProtocolHandler::Init(void)
   259 {
   260   static bool initialized = false;
   262   if (!initialized) {
   263     initialized = true;
   264     RegisterStrongMemoryReporter(new mozilla::HostObjectURLsReporter());
   265     RegisterStrongMemoryReporter(new mozilla::BlobURLsReporter());
   266   }
   267 }
   269 nsHostObjectProtocolHandler::nsHostObjectProtocolHandler()
   270 {
   271   Init();
   272 }
   274 nsresult
   275 nsHostObjectProtocolHandler::AddDataEntry(const nsACString& aScheme,
   276                                           nsISupports* aObject,
   277                                           nsIPrincipal* aPrincipal,
   278                                           nsACString& aUri)
   279 {
   280   Init();
   282   nsresult rv = GenerateURIString(aScheme, aUri);
   283   NS_ENSURE_SUCCESS(rv, rv);
   285   if (!gDataTable) {
   286     gDataTable = new nsClassHashtable<nsCStringHashKey, DataInfo>;
   287   }
   289   DataInfo* info = new DataInfo;
   291   info->mObject = aObject;
   292   info->mPrincipal = aPrincipal;
   293   mozilla::BlobURLsReporter::GetJSStackForBlob(info);
   295   gDataTable->Put(aUri, info);
   296   return NS_OK;
   297 }
   299 void
   300 nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri)
   301 {
   302   if (gDataTable) {
   303     nsCString uriIgnoringRef;
   304     int32_t hashPos = aUri.FindChar('#');
   305     if (hashPos < 0) {
   306       uriIgnoringRef = aUri;
   307     }
   308     else {
   309       uriIgnoringRef = StringHead(aUri, hashPos);
   310     }
   311     gDataTable->Remove(uriIgnoringRef);
   312     if (gDataTable->Count() == 0) {
   313       delete gDataTable;
   314       gDataTable = nullptr;
   315     }
   316   }
   317 }
   319 nsresult
   320 nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
   321                                                nsACString& aUri)
   322 {
   323   nsresult rv;
   324   nsCOMPtr<nsIUUIDGenerator> uuidgen =
   325     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   326   NS_ENSURE_SUCCESS(rv, rv);
   328   nsID id;
   329   rv = uuidgen->GenerateUUIDInPlace(&id);
   330   NS_ENSURE_SUCCESS(rv, rv);
   332   char chars[NSID_LENGTH];
   333   id.ToProvidedString(chars);
   335   aUri += aScheme;
   336   aUri += NS_LITERAL_CSTRING(":");
   337   aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
   339   return NS_OK;
   340 }
   342 static DataInfo*
   343 GetDataInfo(const nsACString& aUri)
   344 {
   345   if (!gDataTable) {
   346     return nullptr;
   347   }
   349   DataInfo* res;
   350   nsCString uriIgnoringRef;
   351   int32_t hashPos = aUri.FindChar('#');
   352   if (hashPos < 0) {
   353     uriIgnoringRef = aUri;
   354   }
   355   else {
   356     uriIgnoringRef = StringHead(aUri, hashPos);
   357   }
   358   gDataTable->Get(uriIgnoringRef, &res);
   360   return res;
   361 }
   363 nsIPrincipal*
   364 nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
   365 {
   366   if (!gDataTable) {
   367     return nullptr;
   368   }
   370   DataInfo* res = GetDataInfo(aUri);
   372   if (!res) {
   373     return nullptr;
   374   }
   376   return res->mPrincipal;
   377 }
   379 void
   380 nsHostObjectProtocolHandler::Traverse(const nsACString& aUri,
   381                                       nsCycleCollectionTraversalCallback& aCallback)
   382 {
   383   if (!gDataTable) {
   384     return;
   385   }
   387   DataInfo* res;
   388   gDataTable->Get(aUri, &res);
   389   if (!res) {
   390     return;
   391   }
   393   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCallback, "HostObjectProtocolHandler DataInfo.mObject");
   394   aCallback.NoteXPCOMChild(res->mObject);
   395 }
   397 static nsISupports*
   398 GetDataObject(nsIURI* aURI)
   399 {
   400   nsCString spec;
   401   aURI->GetSpec(spec);
   403   DataInfo* info = GetDataInfo(spec);
   404   return info ? info->mObject : nullptr;
   405 }
   407 // -----------------------------------------------------------------------
   408 // Protocol handler
   410 NS_IMPL_ISUPPORTS(nsHostObjectProtocolHandler, nsIProtocolHandler)
   412 NS_IMETHODIMP
   413 nsHostObjectProtocolHandler::GetDefaultPort(int32_t *result)
   414 {
   415   *result = -1;
   416   return NS_OK;
   417 }
   419 NS_IMETHODIMP
   420 nsHostObjectProtocolHandler::GetProtocolFlags(uint32_t *result)
   421 {
   422   *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_SUBSUMERS |
   423             URI_IS_LOCAL_RESOURCE | URI_NON_PERSISTABLE;
   424   return NS_OK;
   425 }
   427 NS_IMETHODIMP
   428 nsHostObjectProtocolHandler::NewURI(const nsACString& aSpec,
   429                                     const char *aCharset,
   430                                     nsIURI *aBaseURI,
   431                                     nsIURI **aResult)
   432 {
   433   *aResult = nullptr;
   434   nsresult rv;
   436   DataInfo* info = GetDataInfo(aSpec);
   438   nsRefPtr<nsHostObjectURI> uri =
   439     new nsHostObjectURI(info ? info->mPrincipal.get() : nullptr);
   441   rv = uri->SetSpec(aSpec);
   442   NS_ENSURE_SUCCESS(rv, rv);
   444   NS_TryToSetImmutable(uri);
   445   uri.forget(aResult);
   447   return NS_OK;
   448 }
   450 NS_IMETHODIMP
   451 nsHostObjectProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
   452 {
   453   *result = nullptr;
   455   nsCString spec;
   456   uri->GetSpec(spec);
   458   DataInfo* info = GetDataInfo(spec);
   460   if (!info) {
   461     return NS_ERROR_DOM_BAD_URI;
   462   }
   463   nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(info->mObject);
   464   if (!blob) {
   465     return NS_ERROR_DOM_BAD_URI;
   466   }
   468 #ifdef DEBUG
   469   {
   470     nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(uri);
   471     nsCOMPtr<nsIPrincipal> principal;
   472     uriPrinc->GetPrincipal(getter_AddRefs(principal));
   473     NS_ASSERTION(info->mPrincipal == principal, "Wrong principal!");
   474   }
   475 #endif
   477   nsCOMPtr<nsIInputStream> stream;
   478   nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
   479   NS_ENSURE_SUCCESS(rv, rv);
   481   nsCOMPtr<nsIChannel> channel;
   482   rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
   483                                 uri,
   484                                 stream);
   485   NS_ENSURE_SUCCESS(rv, rv);
   487   nsCOMPtr<nsISupports> owner = do_QueryInterface(info->mPrincipal);
   489   nsString type;
   490   rv = blob->GetType(type);
   491   NS_ENSURE_SUCCESS(rv, rv);
   493   nsCOMPtr<nsIDOMFile> file = do_QueryInterface(info->mObject);
   494   if (file) {
   495     nsString filename;
   496     rv = file->GetName(filename);
   497     NS_ENSURE_SUCCESS(rv, rv);
   498     channel->SetContentDispositionFilename(filename);
   499   }
   501   uint64_t size;
   502   rv = blob->GetSize(&size);
   503   NS_ENSURE_SUCCESS(rv, rv);
   505   channel->SetOwner(owner);
   506   channel->SetOriginalURI(uri);
   507   channel->SetContentType(NS_ConvertUTF16toUTF8(type));
   508   channel->SetContentLength(size);
   510   channel.forget(result);
   512   return NS_OK;
   513 }
   515 NS_IMETHODIMP
   516 nsHostObjectProtocolHandler::AllowPort(int32_t port, const char *scheme,
   517                                        bool *_retval)
   518 {
   519   // don't override anything.
   520   *_retval = false;
   521   return NS_OK;
   522 }
   524 NS_IMETHODIMP
   525 nsBlobProtocolHandler::GetScheme(nsACString &result)
   526 {
   527   result.AssignLiteral(BLOBURI_SCHEME);
   528   return NS_OK;
   529 }
   531 NS_IMETHODIMP
   532 nsMediaStreamProtocolHandler::GetScheme(nsACString &result)
   533 {
   534   result.AssignLiteral(MEDIASTREAMURI_SCHEME);
   535   return NS_OK;
   536 }
   538 NS_IMETHODIMP
   539 nsMediaSourceProtocolHandler::GetScheme(nsACString &result)
   540 {
   541   result.AssignLiteral(MEDIASOURCEURI_SCHEME);
   542   return NS_OK;
   543 }
   545 NS_IMETHODIMP
   546 nsFontTableProtocolHandler::GetScheme(nsACString &result)
   547 {
   548   result.AssignLiteral(FONTTABLEURI_SCHEME);
   549   return NS_OK;
   550 }
   552 nsresult
   553 NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
   554 {
   555   NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs");
   557   *aStream = nullptr;
   559   nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(GetDataObject(aURI));
   560   if (!blob) {
   561     return NS_ERROR_DOM_BAD_URI;
   562   }
   564   return blob->GetInternalStream(aStream);
   565 }
   567 nsresult
   568 NS_GetStreamForMediaStreamURI(nsIURI* aURI, nsIDOMMediaStream** aStream)
   569 {
   570   NS_ASSERTION(IsMediaStreamURI(aURI), "Only call this with mediastream URIs");
   572   *aStream = nullptr;
   574   nsCOMPtr<nsIDOMMediaStream> stream = do_QueryInterface(GetDataObject(aURI));
   575   if (!stream) {
   576     return NS_ERROR_DOM_BAD_URI;
   577   }
   579   *aStream = stream;
   580   NS_ADDREF(*aStream);
   581   return NS_OK;
   582 }
   584 NS_IMETHODIMP
   585 nsFontTableProtocolHandler::NewURI(const nsACString& aSpec,
   586                                    const char *aCharset,
   587                                    nsIURI *aBaseURI,
   588                                    nsIURI **aResult)
   589 {
   590   nsRefPtr<nsIURI> uri;
   592   // Either you got here via a ref or a fonttable: uri
   593   if (aSpec.Length() && aSpec.CharAt(0) == '#') {
   594     nsresult rv = aBaseURI->CloneIgnoringRef(getter_AddRefs(uri));
   595     NS_ENSURE_SUCCESS(rv, rv);
   597     uri->SetRef(aSpec);
   598   } else {
   599     // Relative URIs (other than #ref) are not meaningful within the
   600     // fonttable: scheme.
   601     // If aSpec is a relative URI -other- than a bare #ref,
   602     // this will leave uri empty, and we'll return a failure code below.
   603     uri = new nsSimpleURI();
   604     uri->SetSpec(aSpec);
   605   }
   607   bool schemeIs;
   608   if (NS_FAILED(uri->SchemeIs(FONTTABLEURI_SCHEME, &schemeIs)) || !schemeIs) {
   609     NS_WARNING("Non-fonttable spec in nsFontTableProtocolHander");
   610     return NS_ERROR_NOT_AVAILABLE;
   611   }
   613   uri.forget(aResult);
   614   return NS_OK;
   615 }
   617 nsresult
   618 NS_GetSourceForMediaSourceURI(nsIURI* aURI, mozilla::dom::MediaSource** aSource)
   619 {
   620   NS_ASSERTION(IsMediaSourceURI(aURI), "Only call this with mediasource URIs");
   622   *aSource = nullptr;
   624   nsCOMPtr<mozilla::dom::MediaSource> source = do_QueryInterface(GetDataObject(aURI));
   625   if (!source) {
   626     return NS_ERROR_DOM_BAD_URI;
   627   }
   629   source.forget(aSource);
   630   return NS_OK;
   631 }

mercurial