Bug 567077 - Sniff types of media files that are served with no Content-Type. r=cpearce
authorPaul Adenot <paul@paul.cx>
Tue, 04 Sep 2012 16:22:34 -0700
changeset 104356 a027c9d63d20d697f61832f324077eb0c8cbef7c
parent 104355 8a11353cad22ba883227444f7c734d3fa815a397
child 104357 cb3dd01ba9becccddb39b39a5c4d599913780089
push id14471
push userryanvm@gmail.com
push dateWed, 05 Sep 2012 23:10:59 +0000
treeherdermozilla-inbound@c6768c151b64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs567077
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 567077 - Sniff types of media files that are served with no Content-Type. r=cpearce
content/html/content/src/nsHTMLMediaElement.cpp
content/media/test/Makefile.in
netwerk/mime/nsMimeTypes.h
toolkit/components/Makefile.in
toolkit/components/mediasniffer/Makefile.in
toolkit/components/mediasniffer/nsMediaSniffer.cpp
toolkit/components/mediasniffer/nsMediaSniffer.h
toolkit/components/mediasniffer/nsMediaSnifferModule.cpp
toolkit/library/Makefile.in
toolkit/library/nsStaticXULComponents.cpp
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -1067,17 +1067,18 @@ nsresult nsHTMLMediaElement::LoadResourc
     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
   }
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      mLoadingSrc,
                      nullptr,
                      loadGroup,
                      nullptr,
-                     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
+                     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
+                     nsIChannel::LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN,
                      channelPolicy);
   NS_ENSURE_SUCCESS(rv,rv);
 
   // The listener holds a strong reference to us.  This creates a
   // reference cycle, once we've set mChannel, which is manually broken
   // in the listener's OnStartRequest method after it is finished with
   // the element. The cycle will also be broken if we get a shutdown
   // notification before OnStartRequest fires.  Necko guarantees that
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -122,16 +122,18 @@ MOCHITEST_FILES = \
 		test_autoplay_contentEditable.html \
 		test_buffered.html \
 		test_bug448534.html \
 		test_bug463162.xhtml \
 		test_decoder_disable.html \
 		test_media_selection.html \
 		test_playback.html \
 		test_seekLies.html \
+		test_media_sniffer.html \
+		contentType.sjs \
 		$(NULL)
 
 $(warning test_error_in_video_document.html is disabled for intermittent failures. Bug 608634)
 
 # Disabled on Windows for frequent intermittent failures
 ifneq ($(OS_ARCH), WINNT)
 MOCHITEST_FILES += \
 		test_streams_element_capture.html \
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -70,16 +70,17 @@
 #define APPLICATION_XSLT_XML                "application/xslt+xml"
 #define APPLICATION_MATHML_XML              "application/mathml+xml"
 #define APPLICATION_RDF_XML                 "application/rdf+xml"
 
 #define AUDIO_BASIC                         "audio/basic"
 #define AUDIO_OGG                           "audio/ogg"
 #define AUDIO_WAV                           "audio/x-wav"
 #define AUDIO_WEBM                          "audio/webm"
+#define AUDIO_MP3                           "audio/mpeg"
 
 #define BINARY_OCTET_STREAM                 "binary/octet-stream"
 
 #define IMAGE_GIF                           "image/gif"
 #define IMAGE_JPG                           "image/jpeg"
 #define IMAGE_PJPG                          "image/pjpeg"
 #define IMAGE_PNG                           "image/png"
 #define IMAGE_PPM                           "image/x-portable-pixmap"
--- a/toolkit/components/Makefile.in
+++ b/toolkit/components/Makefile.in
@@ -23,16 +23,17 @@ PARALLEL_DIRS += \
   console \
   contentprefs \
   cookie \
   downloads \
   exthelper \
   filepicker \
   find \
   intl \
+  mediasniffer \
   microformats \
   osfile \
   parentalcontrols \
   passwordmgr \
   perf \
   places \
   prompts \
   reflect \
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/Makefile.in
@@ -0,0 +1,30 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = mediasniffer
+LIBRARY_NAME = mediasniffer
+LIBXUL_LIBRARY  = 1
+EXPORT_LIBRARY = 1
+MODULE_NAME = nsMediaSnifferModule
+IS_COMPONENT = 1
+
+
+CPPSRCS = \
+    nsMediaSniffer.cpp \
+    nsMediaSnifferModule.cpp \
+    $(NULL)
+
+EXPORTS = \
+    nsMediaSniffer.h \
+    $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 tw=80 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMediaSniffer.h"
+#include "nsMemory.h"
+#include "nsIHttpChannel.h"
+#include "nsAString.h"
+#include "nsMimeTypes.h"
+#include "mozilla/ModuleUtils.h"
+
+#include "nsIClassInfoImpl.h"
+
+// The minimum number of bytes that are needed to attempt to sniff an mp4 file.
+static const unsigned MP4_MIN_BYTES_COUNT = 12;
+// The maximum number of bytes to consider when attempting to sniff a file.
+static const PRUint32 MAX_BYTES_SNIFFED = 512;
+
+NS_IMPL_CLASSINFO(nsMediaSniffer, NULL, 0, NS_MEDIA_SNIFFER_CID)
+NS_IMPL_ISUPPORTS1(nsMediaSniffer, nsIContentSniffer)
+
+nsMediaSniffer::nsMediaSnifferEntry nsMediaSniffer::sSnifferEntries[] = {
+  // The string OggS, followed by the null byte.
+  PATTERN_ENTRY("\xFF\xFF\xFF\xFF\xFF", "OggS", APPLICATION_OGG),
+  // The string RIFF, followed by four bytes, followed by the string WAVE
+  PATTERN_ENTRY("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF", "RIFF\x00\x00\x00\x00WAVE", AUDIO_WAV),
+  // WebM
+  PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "\x1A\x45\xDF\xA3", VIDEO_WEBM),
+  // mp3 without ID3 tags.
+  PATTERN_ENTRY("\xFF\xFB", "\xFF\xFA", AUDIO_MP3),
+  // mp3 with ID3 tags, the string "ID3".
+  PATTERN_ENTRY("\xFF\xFF\xFF", "ID3", AUDIO_MP3)
+};
+
+// This function implements mp4 sniffing algorithm, described at
+// http://mimesniff.spec.whatwg.org/#signature-for-mp4
+static bool MatchesMP4(const PRUint8* aData, const PRUint32 aLength)
+{
+  if (aLength <= MP4_MIN_BYTES_COUNT) {
+    return false;
+  }
+  // Conversion from big endian to host byte order.
+  PRUint32 boxSize = (PRUint32)(aData[3] | aData[2] << 8 | aData[1] << 16 | aData[0] << 24);
+
+  // Boxsize should be evenly divisible by 4.
+  if (boxSize % 4 || aLength < boxSize) {
+    return false;
+  }
+  // The string "ftyp".
+  if (aData[4] != 0x66 ||
+      aData[5] != 0x74 ||
+      aData[6] != 0x79 ||
+      aData[7] != 0x70) {
+    return false;
+  }
+  for (PRUint32 i = 2; i <= boxSize / 4 - 1 ; i++) {
+    if (i == 3) {
+      continue;
+    }
+    // The string "mp4".
+    if (aData[4*i]   == 0x6D &&
+        aData[4*i+1] == 0x70 &&
+        aData[4*i+2] == 0x34) {
+      return true;
+    }
+  }
+  return false;
+}
+
+NS_IMETHODIMP
+nsMediaSniffer::GetMIMETypeFromContent(nsIRequest* aRequest,
+                                       const PRUint8* aData,
+                                       const PRUint32 aLength,
+                                       nsACString& aSniffedType)
+{
+  const PRUint32 clampedLength = NS_MIN(aLength, MAX_BYTES_SNIFFED);
+
+  for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sSnifferEntries); ++i) {
+    const nsMediaSnifferEntry& currentEntry = sSnifferEntries[i];
+    if (clampedLength < currentEntry.mLength || currentEntry.mLength == 0) {
+      continue;
+    }
+    bool matched = true;
+    for (PRUint32 j = 0; j < currentEntry.mLength; ++j) {
+      if ((currentEntry.mMask[j] & aData[j]) != currentEntry.mPattern[j]) {
+        matched = false;
+        break;
+      }
+    }
+    if (matched) {
+      aSniffedType.AssignASCII(currentEntry.mContentType);
+      return NS_OK;
+    }
+  }
+
+  if (MatchesMP4(aData, clampedLength)) {
+    aSniffedType.AssignLiteral(VIDEO_MP4);
+    return NS_OK;
+  }
+
+  // Could not sniff the media type, we are required to set it to
+  // application/octet-stream.
+  aSniffedType.AssignLiteral(APPLICATION_OCTET_STREAM);
+  return NS_ERROR_NOT_AVAILABLE;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/nsMediaSniffer.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 tw=80 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsIModule.h"
+#include "nsIFactory.h"
+
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIContentSniffer.h"
+
+// ed905ba3-c656-480e-934e-6bc35bd36aff
+#define NS_MEDIA_SNIFFER_CID \
+{0x3fdd6c28, 0x5b87, 0x4e3e, \
+{0x8b, 0x57, 0x8e, 0x83, 0xc2, 0x3c, 0x1a, 0x6d}}
+
+#define NS_MEDIA_SNIFFER_CONTRACTID "@mozilla.org/media/sniffer;1"
+
+class nsMediaSniffer : public nsIContentSniffer
+{
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICONTENTSNIFFER
+  protected:
+    ~nsMediaSniffer() {};
+
+#define PATTERN_ENTRY(mask, pattern, contentType) \
+    {(const PRUint8*)mask, (const PRUint8*)pattern, sizeof(mask) - 1, contentType}
+
+  struct nsMediaSnifferEntry {
+    const PRUint8* mMask;
+    const PRUint8* mPattern;
+    const PRUint32 mLength;
+    const char* mContentType;
+  };
+
+  static nsMediaSnifferEntry sSnifferEntries[];
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/nsMediaSnifferModule.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+
+#include "nsMediaSniffer.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaSniffer)
+
+NS_DEFINE_NAMED_CID(NS_MEDIA_SNIFFER_CID);
+
+static const mozilla::Module::CIDEntry kMediaSnifferCIDs[] = {
+    { &kNS_MEDIA_SNIFFER_CID, false, NULL, nsMediaSnifferConstructor },
+    { NULL }
+};
+
+static const mozilla::Module::ContractIDEntry kMediaSnifferContracts[] = {
+    { NS_MEDIA_SNIFFER_CONTRACTID, &kNS_MEDIA_SNIFFER_CID },
+    { NULL }
+};
+
+static const mozilla::Module::CategoryEntry kMediaSnifferCategories[] = {
+    { "content-sniffing-services", NS_MEDIA_SNIFFER_CONTRACTID, NS_MEDIA_SNIFFER_CONTRACTID},
+    { "net-content-sniffers", NS_MEDIA_SNIFFER_CONTRACTID, NS_MEDIA_SNIFFER_CONTRACTID},
+    { NULL }
+};
+
+static const mozilla::Module kMediaSnifferModule = {
+    mozilla::Module::kVersion,
+    kMediaSnifferCIDs,
+    kMediaSnifferContracts,
+    kMediaSnifferCategories
+};
+
+NSMODULE_DEFN(nsMediaSnifferModule) = &kMediaSnifferModule;
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -142,16 +142,17 @@ COMPONENT_LIBS += \
   i18n \
   chardet \
   jar$(VERSION_NUMBER) \
   startupcache \
   pref \
   htmlpars \
   identity \
   imglib2 \
+  mediasniffer \
   gkgfx \
   gklayout \
   docshell \
   embedcomponents \
   webbrwsr \
   nsappshell \
   txmgr \
   commandlines \
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -175,16 +175,17 @@
     MODULE(nsJarModule)                      \
     ZIPWRITER_MODULE                         \
     MODULE(StartupCacheModule)               \
     MODULE(nsPrefModule)                     \
     MODULE(nsRDFModule)                      \
     MODULE(nsWindowDataSourceModule)         \
     MODULE(nsParserModule)                   \
     MODULE(nsImageLib2Module)                \
+    MODULE(nsMediaSnifferModule)             \
     MODULE(nsGfxModule)                      \
     PROFILER_MODULE                          \
     WIDGET_MODULES                           \
     ICON_MODULE                              \
     MODULE(nsPluginModule)                   \
     MODULE(nsLayoutModule)                   \
     MODULE(docshell_provider)                \
     MODULE(embedcomponents)                  \