Bug 1227662 - Fix code and add tests for fetching video mimetypes r=margaret
authorMark Finkle <mfinkle@mozilla.com>
Thu, 04 Feb 2016 18:19:59 -0500
changeset 283158 47242d5db87afa496ec1c597dc31d8b91b78f99d
parent 283157 10dfe5e3ded1621a7ef777ddbc54e5f80696c3d6
child 283159 a7f63b3721cd3ba105990bbb37a87044383d26d9
push id29975
push userphilringnalda@gmail.com
push dateSat, 06 Feb 2016 02:27:56 +0000
treeherdermozilla-central@6148b5848349 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs1227662
milestone47.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 1227662 - Fix code and add tests for fetching video mimetypes r=margaret
mobile/android/chrome/content/CastingApps.js
mobile/android/tests/browser/chrome/chrome.ini
mobile/android/tests/browser/chrome/test_video_discovery.html
mobile/android/tests/browser/chrome/video_discovery.html
mobile/android/tests/browser/chrome/video_discovery.sjs
--- a/mobile/android/chrome/content/CastingApps.js
+++ b/mobile/android/chrome/content/CastingApps.js
@@ -345,22 +345,28 @@ var CastingApps = {
           this._getVideo(element, types, extensions, aCallback);
           return;
         }
       }
     } catch(e) {}
   },
 
   _getContentTypeForURI: function(aURI, aElement, aCallback) {
-    let channel = Services.io.newChannelFromURI2(aURI,
-                                                 aElement,
-                                                 null, // aLoadingPrincipal
-                                                 null, // aTriggeringPrincipal
-                                                 Ci.nsILoadInfo.SEC_NORMAL,
-                                                 Ci.nsIContentPolicy.TYPE_OTHER);
+    let channel;
+    try {
+     channel = Services.io.newChannelFromURI2(aURI,
+                                              aElement,
+                                              null, // aLoadingPrincipal
+                                              null, // aTriggeringPrincipal
+                                              Ci.nsILoadInfo.SEC_NORMAL,
+                                              Ci.nsIContentPolicy.TYPE_OTHER);
+     } catch(e) {
+      aCallback(null);
+      return;
+     }
 
     let listener = {
       onStartRequest: function(request, context) {
         switch (channel.responseStatus) {
           case 301:
           case 302:
           case 303:
             request.cancel(0);
@@ -371,17 +377,22 @@ var CastingApps = {
             aCallback(channel.contentType);
             request.cancel(0);
             break;
         }
       },
       onStopRequest: function(request, context, statusCode)  {},
       onDataAvailable: function(request, context, stream, offset, count) {}
     };
-    channel.asyncOpen(listener, null)
+
+    if (channel) {
+      channel.asyncOpen(listener, null);
+    } else {
+      aCallback(null);
+    }
   },
 
   // Because this method uses a callback, make sure we return ASAP if we know
   // we have a castable video source.
   _getVideo: function(aElement, aTypes, aExtensions, aCallback) {
     // Keep a list of URIs we need for an async mimetype check
     let asyncURIs = [];
 
@@ -436,32 +447,40 @@ var CastingApps = {
           return;
         }
       }
 
       // Delay the async check until we sync scan all possible URIs
       asyncURIs.push(sourceURI);
     }
 
-    // If we didn't find a good URI directly, let's look using async methods
+    // Helper method that walks the array of possible URIs, fetching the mimetype as we go.
     // As soon as we find a good sourceURL, avoid firing the callback any further
-    aCallback.fired = false;
-    for (let sourceURI of asyncURIs) {
+    var _getContentTypeForURIs = (aURIs) => {
       // Do an async fetch to figure out the mimetype of the source video
+      let sourceURI = aURIs.pop();
       this._getContentTypeForURI(sourceURI, aElement, (aType) => {
-        if (!aCallback.fired && this.allowableMimeType(aType, aTypes)) {
-          aCallback.fired = true;
+        if (this.allowableMimeType(aType, aTypes)) {
+          // We found a supported mimetype.
           aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: aType });
+        } else {
+          // This URI was not a supported mimetype, so let's try the next, if we have more.
+          if (aURIs.length > 0) {
+            _getContentTypeForURIs(aURIs);
+          } else {
+            // We were not able to find a supported mimetype.
+            aCallback(null);
+          }
         }
       });
     }
 
-    // If we didn't find any castable source, let's send back a signal
-    if (!aCallback.fired) {
-      aCallback(null);
+    // If we didn't find a good URI directly, let's look using async methods.
+    if (asyncURIs.length > 0) {
+      _getContentTypeForURIs(asyncURIs);
     }
   },
 
   // This code depends on handleVideoBindingAttached setting mozAllowCasting
   // so we can quickly figure out if the video is castable
   isVideoCastable: function(aElement, aX, aY) {
     // Use the flag set when the <video> binding was created as the check
     if (aElement instanceof HTMLVideoElement) {
--- a/mobile/android/tests/browser/chrome/chrome.ini
+++ b/mobile/android/tests/browser/chrome/chrome.ini
@@ -5,16 +5,17 @@ support-files =
   desktopmode_user_agent.sjs
   devicesearch.xml
   head.js
   head_search.js
   session_formdata_sample.html
   simpleservice.xml
   video_controls.html
   video_discovery.html
+  video_discovery.sjs
   web_channel.html
   tp5/**
 
 [test_about_logins.html]
 [test_accounts.html]
 [test_android_log.html]
 [test_app_constants.html]
 [test_awsy_lite.html]
--- a/mobile/android/tests/browser/chrome/test_video_discovery.html
+++ b/mobile/android/tests/browser/chrome/test_video_discovery.html
@@ -78,58 +78,69 @@ Migrated from Robocop: https://bugzilla.
   let videoDiscoveryTests = [
     { id: "simple-mp4", source: "http://mochi.test:8888/simple.mp4", poster: "http://mochi.test:8888/simple.png", text: "simple video with mp4 src" },
     { id: "simple-fail", pass: false, text: "simple video with no mp4 src" },
     { id: "with-sources-mp4", source: "http://mochi.test:8888/simple.mp4", text: "video with mp4 extension source child" },
     { id: "with-sources-webm", source: "http://mochi.test:8888/simple.webm", text: "video with webm extension source child" },
     { id: "with-sources-fail", pass: false, text: "video with no mp4 extension source child" },
     { id: "with-sources-mimetype-mp4", source: "http://mochi.test:8888/simple-video-mp4", text: "video with mp4 mimetype source child" },
     { id: "with-sources-mimetype-webm", source: "http://mochi.test:8888/simple-video-webm", text: "video with webm mimetype source child" },
+    { id: "simple-fetch-pass", source: "http://mochi.test:8888/chrome/mobile/android/tests/browser/chrome/video_discovery.sjs?type=video/mp4", text: "simple video with mp4 mimetype fetched from server" },
+    { id: "simple-fetch-fail", pass: false, text: "simple video with non-video mimetype fetched from server" },
     { id: "video-overlay", source: "http://mochi.test:8888/simple.mp4", text: "div overlay covering a simple video with mp4 src" }
   ];
 
+  let expectedTests = videoDiscoveryTests.length;
+
   function execute_video_test(test) {
     let element = browser.contentDocument.getElementById(test.id);
     if (element) {
       let [x, y] = middle(element);
       dump("Starting to getVideo");
       chromeWin.CastingApps.getVideo(element, x, y, (video) => {
-        dump("got a Video");
+        dump("Completed getVideo");
         if (video) {
+          dump("video source: " + video.source);
+
           let matchPoster = (test.poster == video.poster);
           let matchSource = (test.source == video.source);
           ok(matchPoster && matchSource && test.pass, test.text);
         } else {
           ok(!test.pass, test.text);
         }
+        expectedTests--;
+        if (expectedTests == 0) {
+          SimpleTest.finish();
+        }
       });
     } else {
       ok(false, "test element not found: [" + test.id + "]");
+      SimpleTest.finish();
     }
   }
 
   function test_video() {
     let videoTest;
     while ((videoTest = videoDiscoveryTests.shift())) {
       if (!("poster" in videoTest)) {
         videoTest.poster = "";
       }
       if (!("pass" in videoTest)) {
         videoTest.pass = true;
       }
       execute_video_test(videoTest);
     }
-    SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
-  // On debug runs, 6 assertions typically observed; 3 each of:
+
+  // On debug runs, 10 assertions typically observed; 5 each of:
   //  - ASSERTION: cancel with non-failure status code: 'NS_FAILED(status)'
   //  - ASSERTION: OnDataAvailable implementation consumed no data: 'Error'
-  SimpleTest.expectAssertions(0,6);
+  SimpleTest.expectAssertions(0,10);
   setup_browser();
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=953381">Mozilla Bug 953381</a>
 <br>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1184186">Migrated from Robocop testVideoDiscovery</a>
--- a/mobile/android/tests/browser/chrome/video_discovery.html
+++ b/mobile/android/tests/browser/chrome/video_discovery.html
@@ -55,16 +55,22 @@
     </video>
 
     <!-- PASS: source list uses a mp4 mimetype and extra data -->
     <video id="with-sources-mimetype-plus">
       <source src="/simple-video-ogg" type="video/ogg">
       <source src="/simple-video-mp4" type="video/mp4; codecs='avc1.42E01E, mp4a.40.2'">
     </video>
 
+    <!-- PASS: src uses a mp4 mimetype from the server -->
+    <video id="simple-fetch-pass" src="http://mochi.test:8888/chrome/mobile/android/tests/browser/chrome/video_discovery.sjs?type=video/mp4"></video>
+
+    <!-- FAIL: src uses a non-video mimetype from the server -->
+    <video id="simple-fetch-fail" src="http://mochi.test:8888/chrome/mobile/android/tests/browser/chrome/video_discovery.sjs?type=image/png"></video>
+
     <!-- PASS: div overlay covers a video with mp4 src -->
     <div id="video-box">
       <div id="video-overlay"></div>
       <div>
         <video id="video-player" src="/simple.mp4"></video>
       </div>
     </div>
   </body>
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/chrome/video_discovery.sjs
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function parseQuery(request, key) {
+  var params = request.queryString.split('&');
+  for (var j = 0; j < params.length; ++j) {
+    var p = params[j];
+	if (p == key)
+	  return true;
+    if (p.indexOf(key + "=") == 0)
+	  return p.substring(key.length + 1);
+	if (p.indexOf("=") < 0 && key == "")
+	  return p;
+  }
+  return false;
+}
+
+function handleRequest(request, response) {
+  // Pretend to be the type requested from the test
+  var type = parseQuery(request, "type");
+
+  response.setHeader("Content-Type", type, false);
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.setHeader("Access-Control-Allow-Origin", "*", false);
+
+  response.write("fake video");
+}