Bug 1043562 - Hide the Contacts API from the contexts that lack sufficient privileges, such as Firefox desktop and Android; r=smaug
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 12 Mar 2016 13:45:54 -0500
changeset 288513 389126282bc6993149e84ad7ac0b27c9a6eecb3f
parent 288512 51766683141be21bc5621337c9221b99dcea20a1
child 288514 080f495f9e41044e8de7cbd66909199e83b977cc
push id30082
push userryanvm@gmail.com
push dateSun, 13 Mar 2016 23:08:35 +0000
treeherdermozilla-central@f0c0480732d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1043562
milestone48.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 1043562 - Hide the Contacts API from the contexts that lack sufficient privileges, such as Firefox desktop and Android; r=smaug
b2g/app/b2g.js
dom/base/test/chrome.ini
dom/base/test/file_navigator_resolve_identity_xrays.xul
dom/base/test/test_navigator_resolve_identity.html
dom/base/test/test_navigator_resolve_identity_xrays.xul
dom/bindings/test/file_bug707564-2.html
dom/bindings/test/mochitest.ini
dom/bindings/test/test_bug707564-chrome.html
dom/bindings/test/test_bug707564.html
dom/contacts/tests/file_contacts_basics.html
dom/contacts/tests/file_contacts_basics2.html
dom/contacts/tests/file_contacts_blobs.html
dom/contacts/tests/file_contacts_events.html
dom/contacts/tests/file_contacts_getall.html
dom/contacts/tests/file_contacts_getall2.html
dom/contacts/tests/file_contacts_international.html
dom/contacts/tests/file_contacts_substringmatching.html
dom/contacts/tests/file_contacts_substringmatchingCL.html
dom/contacts/tests/file_contacts_substringmatchingVE.html
dom/contacts/tests/file_migration.html
dom/contacts/tests/file_permission_denied.html
dom/contacts/tests/mochitest.ini
dom/contacts/tests/shared.js
dom/contacts/tests/test_contacts_basics.html
dom/contacts/tests/test_contacts_basics2.html
dom/contacts/tests/test_contacts_blobs.html
dom/contacts/tests/test_contacts_events.html
dom/contacts/tests/test_contacts_getall.html
dom/contacts/tests/test_contacts_getall2.html
dom/contacts/tests/test_contacts_international.html
dom/contacts/tests/test_contacts_substringmatching.html
dom/contacts/tests/test_contacts_substringmatchingCL.html
dom/contacts/tests/test_contacts_substringmatchingVE.html
dom/contacts/tests/test_migration.html
dom/contacts/tests/test_permission_denied.html
dom/icc/tests/marionette/test_icc_contact_read.js
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/Contacts.webidl
dom/webidl/MozContactChangeEvent.webidl
modules/libpref/init/all.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -437,19 +437,16 @@ pref("dom.meta-viewport.enabled", true);
 #endif
 
 // SMS/MMS
 pref("dom.sms.enabled", true);
 
 //The waiting time in network manager.
 pref("network.gonk.ms-release-mms-connection", 30000);
 
-// WebContacts
-pref("dom.mozContacts.enabled", true);
-
 // Shortnumber matching needed for e.g. Brazil:
 // 03187654321 can be found with 87654321
 pref("dom.phonenumber.substringmatching.BR", 8);
 pref("dom.phonenumber.substringmatching.CO", 10);
 pref("dom.phonenumber.substringmatching.VE", 7);
 pref("dom.phonenumber.substringmatching.CL", 8);
 pref("dom.phonenumber.substringmatching.PE", 7);
 
--- a/dom/base/test/chrome.ini
+++ b/dom/base/test/chrome.ini
@@ -9,16 +9,17 @@ support-files =
 
 [test_anonymousContent_xul_window.xul]
 [test_bug715041.xul]
 [test_bug715041_removal.xul]
 [test_domrequesthelper.xul]
 [test_url.xul]
 [test_console.xul]
 [test_navigator_resolve_identity_xrays.xul]
+support-files = file_navigator_resolve_identity_xrays.xul
 [test_sendQueryContentAndSelectionSetEvent.html]
 [test_bug1016960.html]
 [test_bug357450.js]
 [test_copypaste.xul]
 [test_messagemanager_principal.html]
 [test_messagemanager_send_principal.html]
 skip-if = buildapp == 'mulet'
 [test_bug945152.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_navigator_resolve_identity_xrays.xul
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=985827
+-->
+<window title="Mozilla Bug 985827"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <iframe id="t"></iframe>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Test for Bug 985827 **/
+
+  addLoadEvent(function() {
+    var ok = parent.ok;
+    var is = parent.is;
+
+    var nav = document.getElementById("t").contentWindow.navigator;
+
+    ok(Components.utils.isXrayWrapper(nav), "Should have an Xray here");
+
+    // Test WebIDL NavigatorProperty objects
+    is(typeof nav.mozContacts, "object", "Should have a mozContacts object");
+    is(nav.mozContacts, nav.mozContacts,
+       "Should have gotten the same mozContacts object again");
+  });
+
+  ]]>
+  </script>
+</window>
--- a/dom/base/test/test_navigator_resolve_identity.html
+++ b/dom/base/test/test_navigator_resolve_identity.html
@@ -7,22 +7,39 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 985827</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 985827 **/
 
-  // Test WebIDL NavigatorProperty objects
-  var x = navigator.mozContacts;
-  is(typeof x, "object", "Should have a mozContacts object");
-  delete navigator.mozContacts;
-  var y = navigator.mozContacts;
-  is(x, y, "Should have gotten the same mozContacts object again");
+  function test() {
+    var is = parent.is;
+
+    // Test WebIDL NavigatorProperty objects
+    var x = navigator.mozContacts;
+    is(typeof x, "object", "Should have a mozContacts object");
+    delete navigator.mozContacts;
+    var y = navigator.mozContacts;
+    is(x, y, "Should have gotten the same mozContacts object again");
+  }
+
+  SimpleTest.waitForExplicitFinish();
+
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], function() {
+    var iframe = document.createElement("iframe");
+    iframe.src = "data:text/html,<script>(" + escape(test.toString()) + ")();</scr" + "ipt>";
+    document.body.appendChild(iframe);
+    iframe.onload = function() { SimpleTest.finish(); };
+  });
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=985827">Mozilla Bug 985827</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/base/test/test_navigator_resolve_identity_xrays.xul
+++ b/dom/base/test/test_navigator_resolve_identity_xrays.xul
@@ -4,36 +4,55 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=985827
 -->
 <window title="Mozilla Bug 985827"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
-  <iframe id="t"></iframe>
-
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=985827"
      target="_blank">Mozilla Bug 985827</a>
+  <iframe id="t"></iframe>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug 985827 **/
 
   SimpleTest.waitForExplicitFinish();
+
+  Components.utils.import("resource://gre/modules/Services.jsm");
+
   addLoadEvent(function() {
-    var nav = $("t").contentWindow.navigator;
-    ok(Components.utils.isXrayWrapper(nav), "Should have an Xray here");
+    var iframe = document.getElementById("t");
+
+    Services.perms.addFromPrincipal(iframe.contentDocument.nodePrincipal,
+                                    "contacts-read",
+                                    Services.perms.ALLOW_ACTION);
+    Services.perms.addFromPrincipal(iframe.contentDocument.nodePrincipal,
+                                    "contacts-write",
+                                    Services.perms.ALLOW_ACTION);
+    Services.perms.addFromPrincipal(iframe.contentDocument.nodePrincipal,
+                                    "contacts-create",
+                                    Services.perms.ALLOW_ACTION);
 
-    // Test WebIDL NavigatorProperty objects
-    is(typeof nav.mozContacts, "object", "Should have a mozContacts object");
-    is(nav.mozContacts, nav.mozContacts,
-       "Should have gotten the same mozContacts object again");
+    var dir = "chrome://mochitests/content/chrome/dom/base/test/";
+    iframe.src = dir + "file_navigator_resolve_identity_xrays.xul";
+    iframe.onload = function() { finish(); };
 
-    SimpleTest.finish();
+    function finish() {
+      Services.perms.removeFromPrincipal(document.nodePrincipal,
+                                         "contacts-read");
+      Services.perms.removeFromPrincipal(document.nodePrincipal,
+                                         "contacts-write");
+      Services.perms.removeFromPrincipal(document.nodePrincipal,
+                                         "contacts-create");
+      SimpleTest.finish();
+    }
   });
+
   ]]>
   </script>
 </window>
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/file_bug707564-2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=707564
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 707564</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 707564 **/
+  var ok = parent.ok;
+  var isnot = parent.isnot;
+
+  addLoadEvent(function() {
+    var props = Object.getOwnPropertyNames(frames[0].navigator);
+    isnot(props.indexOf("mozContacts"), -1,
+          "Should enumerate a mozContacts property on navigator");
+
+    // Now enumerate a different navigator object
+    var found = false;
+    for (var name in frames[1].navigator) {
+      if (name == "mozContacts") {
+        found = true;
+      }
+    }
+    ok(found, "Should enumerate a mozContacts property on navigator via for...in");
+    parent.SimpleTest.finish();
+  });
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=707564">Mozilla Bug 707564</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+<iframe></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   file_InstanceOf.html
   file_bug707564.html
+  file_bug707564-2.html
   file_bug775543.html
   file_document_location_set_via_xray.html
   file_dom_xrays.html
   file_proxies_via_xray.html
   forOf_iframe.html
 
 [test_async_stacks.html]
 [test_ByteString.html]
--- a/dom/bindings/test/test_bug707564-chrome.html
+++ b/dom/bindings/test/test_bug707564-chrome.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=707564">Mozilla Bug 707564</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 <iframe id="t1" src="http://example.org/tests/dom/bindings/test/file_bug707564.html"></iframe>
-<iframe id="t2" src="http://example.org/tests/dom/bindings/test/file_bug707564.html"></iframe>
+<iframe id="t2"></iframe>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 775543 **/
 function test()
 {
   var nav = document.getElementById("t1").contentWindow.navigator;
@@ -38,15 +38,29 @@ function test()
       found = true;
     }
   }
   ok(found, "Should enumerate a mozContacts property on navigator xray via for...in");
 
   SimpleTest.finish();
 }
 
-addLoadEvent(test);
+onload = test;
+onload = function() {
+  var iframe1 = document.getElementById("t1");
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: iframe1.contentDocument},
+    {type: "contacts-write", allow: true, context: iframe1.contentDocument},
+    {type: "contacts-create", allow: true, context: iframe1.contentDocument},
+  ], function() {
+    iframe1.src = "http://example.org/tests/dom/bindings/test/file_bug707564.html";
+    iframe1.onload = function() {
+      var iframe2 = document.getElementById("t2");
+      iframe2.src = "http://example.org/tests/dom/bindings/test/file_bug707564.html";
+      iframe2.onload = test;
+    };
+  });
+};
 
-SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/bindings/test/test_bug707564.html
+++ b/dom/bindings/test/test_bug707564.html
@@ -1,43 +1,29 @@
 <!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=707564
--->
 <head>
-  <meta charset="utf-8">
-  <title>Test for Bug 707564</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for Bug 707564 **/
-  SimpleTest.waitForExplicitFinish();
-
-  addLoadEvent(function() {
-    var props = Object.getOwnPropertyNames(frames[0].navigator);
-    isnot(props.indexOf("mozContacts"), -1,
-          "Should enumerate a mozContacts property on navigator");
-
-    // Now enumerate a different navigator object
-    var found = false;
-    for (var name in frames[1].navigator) {
-      if (name == "mozContacts") {
-        found = true;
-      }
-    }
-    ok(found, "Should enumerate a mozContacts property on navigator via for...in");
-    SimpleTest.finish();
-  });
-  </script>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=707564">Mozilla Bug 707564</a>
-<p id="display"></p>
-<div id="content" style="display: none">
 <iframe></iframe>
-<iframe></iframe>
-</div>
 <pre id="test">
+<script type="application/javascript">
+
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_bug707564-2.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
+
+</script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_basics.html
@@ -0,0 +1,787 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674720
+-->
+<head>
+  <title>Test for Bug 674720 WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674720">Mozilla Bug 674720</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+
+var initialRev;
+
+function checkRevision(revision, msg, then) {
+  var revReq = mozContacts.getRevision();
+  revReq.onsuccess = function(e) {
+    is(e.target.result, initialRev+revision, msg);
+    then();
+  };
+  // The revision function isn't supported on Android so treat on failure as success
+  if (isAndroid) {
+    revReq.onerror = function(e) {
+      then();
+    };
+  } else {
+    revReq.onerror = onFailure;
+  }
+}
+
+var req;
+
+var steps = [
+  function() {
+    req = mozContacts.getRevision();
+    req.onsuccess = function(e) {
+      initialRev = e.target.result;
+      next();
+    };
+
+    // Android does not support the revision function. Treat errors as success.
+    if (isAndroid) {
+      req.onerror = function(e) {
+        initialRev = 0;
+        next();
+      };
+    } else {
+      req.onerror = onFailure;
+    }
+  },
+  function () {
+    ok(true, "Deleting database");
+    checkRevision(0, "Initial revision is 0", function() {
+      req = mozContacts.clear();
+      req.onsuccess = function () {
+        ok(true, "Deleted the database");
+        checkCount(0, "No contacts after clear", function() {
+          checkRevision(1, "Revision was incremented on clear", next);
+        });
+      };
+      req.onerror = onFailure;
+    });
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Empty database.");
+      checkRevision(1, "Revision was not incremented on find", next);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding empty contact");
+    createResult1 = new mozContact({});
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      checkCount(1, "1 contact after adding empty contact", function() {
+        checkRevision(2, "Revision was incremented on save", next);
+      });
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "One contact.");
+      findResult1 = req.result[0];
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting empty contact");
+    req = navigator.mozContacts.remove(findResult1);
+    req.onsuccess = function () {
+      var req2 = mozContacts.find(defaultOptions);
+      req2.onsuccess = function () {
+        is(req2.result.length, 0, "Empty Database.");
+        clearTemps();
+        checkRevision(3, "Revision was incremented on remove", next);
+      }
+      req2.onerror = onFailure;
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact1");
+    createResult1 = new mozContact(properties1);
+
+    mozContacts.oncontactchange = function(event) {
+      is(event.contactID, createResult1.id, "Same contactID");
+      is(event.reason, "create", "Same reason");
+      next();
+    }
+
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      checkContacts(createResult1, properties1);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 1");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[1].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      // Some manual testing. Testint the testfunctions
+      // tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}],
+      is(findResult1.tel[0].carrier, "testCarrier", "Same Carrier");
+      is(String(findResult1.tel[0].type), "work", "Same type");
+      is(findResult1.tel[0].value, "123456", "Same Value");
+      is(findResult1.tel[1].type[1], "fax", "Same type");
+      is(findResult1.tel[1].value, "+55 (31) 9876-3456", "Same Value");
+
+      is(findResult1.adr[0].countryName, "country 1", "Same country");
+
+      // email: [{type: ["work"], value: "x@y.com"}]
+      is(String(findResult1.email[0].type), "work", "Same Type");
+      is(findResult1.email[0].value, "x@y.com", "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for exact email");
+    var options = {filterBy: ["email"],
+                   filterOp: "equals",
+                   filterValue: properties1.email[0].value};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring and update");
+    mozContacts.oncontactchange = function(event) {
+       is(event.contactID, findResult1.id, "Same contactID");
+       is(event.reason, "update", "Same reason");
+     }
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      findResult1.jobTitle = ["new Job"];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact");
+    mozContacts.oncontactchange = function(event) {
+       is(event.contactID, createResult2.id, "Same contactID");
+       is(event.reason, "create", "Same reason");
+     }
+    createResult2 = new mozContact({name: ["newName"]});
+    req = navigator.mozContacts.save(createResult2);
+    req.onsuccess = function () {
+      ok(createResult2.id, "The contact now has an ID.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 2");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      checkContacts(createResult1, findResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Retrieving by name equality 1");
+    var options = {filterBy: ["name"],
+                   filterOp: "equals",
+                   filterValue: properties1.name[0]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      checkContacts(createResult1, findResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Retrieving by name equality 2");
+    var options = {filterBy: ["name"],
+                   filterOp: "equals",
+                   filterValue: properties1.name[1]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      checkContacts(createResult1, findResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Retrieving by name substring 1");
+    var options = {filterBy: ["name"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.name[0].substring(0,3).toLowerCase()};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      checkContacts(createResult1, findResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Retrieving by name substring 2");
+    var options = {filterBy: ["name"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.name[1].substring(0,3).toLowerCase()};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      checkContacts(createResult1, findResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Remove contact1");
+    mozContacts.oncontactchange = function(event) {
+      is(event.contactID, createResult1.id, "Same contactID");
+      is(event.reason, "remove", "Same reason");
+    }
+    req = navigator.mozContacts.remove(createResult1);
+    req.onsuccess = function () {
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 3");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[1].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found no contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Remove contact2");
+    mozContacts.oncontactchange = function(event) {
+      is(event.contactID, createResult2.id, "Same contactID");
+      is(event.reason, "remove", "Same reason");
+    }
+    req = navigator.mozContacts.remove(createResult2);
+    req.onsuccess = function () {
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 4");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[1].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found no contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    mozContacts.oncontactchange = function(event) {
+      is(event.contactID, "undefined", "Same contactID");
+      is(event.reason, "remove", "Same reason");
+    }
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact with properties1");
+    createResult1 = new mozContact(properties1);
+    mozContacts.oncontactchange = null;
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring tel1");
+    var options = {filterBy: ["tel"],
+                   filterOp: "contains",
+                   filterValue: properties1.tel[1].value.substring(2,5)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel exact");
+    var options = {filterBy: ["tel"],
+                   filterOp: "equals",
+                   filterValue: "+55 319 8 7 6 3456"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel exact with substring");
+    var options = {filterBy: ["tel"],
+                   filterOp: "equals",
+                   filterValue: "3456"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found no contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel exact with substring");
+    var options = {filterBy: ["tel"],
+                   filterOp: "equals",
+                   filterValue: "+55 (31)"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found no contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel match national number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "3198763456"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel match national format");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "0451 491934"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel match entered number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "123456"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by tel match international number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "+55 31 98763456"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by match with field other than tel");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "match",
+                   filterValue: "my friends call me 555-4040"};
+    req = mozContacts.find(options);
+    req.onsuccess = onUnwantedSuccess;
+    req.onerror = function() {
+      ok(true, "Failed");
+      next();
+    }
+  },
+  function () {
+    ok(true, "Retrieving by substring tel2");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: "9876"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring tel3");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: "98763456"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 5");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 6");
+    var options = {filterBy: ["familyName", "givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring3, Testing multi entry");
+    var options = {filterBy: ["givenName", "familyName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.familyName[1].substring(0,3).toLowerCase()};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function() {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(createResult1, findResult1);
+      if (!isAndroid) {
+        ok(findResult1.updated, "Has updated field");
+        ok(findResult1.published, "Has published field");
+      }
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Modifying contact1");
+    if (!findResult1) {
+      SpecialPowers.executeSoon(next);
+    } else {
+      findResult1.impp = properties1.impp = [{value:"phil impp"}];
+      req = navigator.mozContacts.save(findResult1);
+      req.onsuccess = function () {
+        var req2 = mozContacts.find(defaultOptions);
+        req2.onsuccess = function() {
+          is(req2.result.length, 1, "Found exactly 1 contact.");
+          findResult2 = req2.result[0];
+          ok(findResult2.id == sample_id1, "Same ID");
+          checkContacts(findResult2, properties1);
+          is(findResult2.impp.length, 1, "Found exactly 1 IMS info.");
+          next();
+        };
+        req2.onerror = onFailure;
+      };
+      req.onerror = onFailure;
+    }
+  },
+  function() {
+    // Android does not support published/updated fields. Skip this.
+    if (isAndroid) {
+      next();
+      return;
+    }
+
+    ok(true, "Saving old contact, should abort!");
+    req = mozContacts.save(createResult1);
+    req.onsuccess = onUnwantedSuccess;
+    req.onerror   = function() { ok(true, "Successfully declined updating old contact!"); next(); };
+  },
+  function () {
+    ok(true, "Retrieving a specific contact by ID");
+    var options = {filterBy: ["id"],
+                   filterOp: "equals",
+                   filterValue: sample_id1};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving a specific contact by givenName");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "equals",
+                   filterValue: properties1.givenName[0]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, properties1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Modifying contact2");
+    if (!findResult1) {
+      SpecialPowers.executeSoon(next);
+    } else {
+      findResult1.impp = properties1.impp = [{value: "phil impp"}];
+      req = mozContacts.save(findResult1);
+      req.onsuccess = function () {
+        var req2 = mozContacts.find(defaultOptions);
+        req2.onsuccess = function () {
+          is(req2.result.length, 1, "Found exactly 1 contact.");
+          findResult1 = req2.result[0];
+          ok(findResult1.id == sample_id1, "Same ID");
+          checkContacts(findResult1, properties1);
+          is(findResult1.impp.length, 1, "Found exactly 1 IMS info.");
+          next();
+        }
+        req2.onerror = onFailure;
+      };
+      req.onerror = onFailure;
+    }
+  },
+  function () {
+    ok(true, "Searching contacts by query");
+    var options = {filterBy: ["givenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0,4)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by query");
+    var options = {filterBy: ["givenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts with multiple indices");
+    var options = {filterBy: ["email", "givenName"],
+                   filterOp: "equals",
+                   filterValue: properties1.givenName[1]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, properties1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Modifying contact3");
+    if (!findResult1) {
+      SpecialPowers.executeSoon(next);
+    } else {
+      findResult1.email = [{value: properties1.nickname}];
+      findResult1.nickname = ["TEST"];
+      var newContact = new mozContact(findResult1);
+      req = mozContacts.save(newContact);
+      req.onsuccess = function () {
+        var options = {filterBy: ["email", "givenName"],
+                       filterOp: "startsWith",
+                       filterValue: properties1.givenName[0]};
+        // One contact has it in nickname and the other in email
+        var req2 = mozContacts.find(options);
+        req2.onsuccess = function () {
+          is(req2.result.length, 2, "Found exactly 2 contacts.");
+          ok(req2.result[0].id != req2.result[1].id, "Different ID");
+          next();
+        }
+        req2.onerror = onFailure;
+      };
+      req.onerror = onFailure;
+    }
+  },
+  function () {
+    ok(true, "Deleting contact" + findResult1);
+    req = mozContacts.remove(findResult1);
+    req.onsuccess = function () {
+      var req2 = mozContacts.find(defaultOptions);
+      req2.onsuccess = function () {
+        is(req2.result.length, 1, "One contact left.");
+        findResult1 = req2.result[0];
+        next();
+      }
+      req2.onerror = onFailure;
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.remove(findResult1);
+    req.onsuccess =  function () {
+      clearTemps();
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Test JSON.stringify output for mozContact objects");
+    var json = JSON.parse(JSON.stringify(new mozContact(properties1)));
+    checkContacts(json, properties1);
+    next();
+  },
+  function() {
+    ok(true, "Test slice");
+    var c = new mozContact();
+    c.email = [{ type: ["foo"], value: "bar@baz" }]
+    var arr = c.email;
+    is(arr[0].value, "bar@baz", "Should have the right value");
+    arr = arr.slice();
+    is(arr[0].value, "bar@baz", "Should have the right value after slicing");
+    next();
+  },
+  function () {
+    ok(true, "all done!\n");
+    clearTemps();
+
+    parent.SimpleTest.finish();
+  }
+];
+
+start_tests();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_basics2.html
@@ -0,0 +1,1153 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674720
+-->
+<head>
+  <title>Test for Bug 674720 WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674720">Mozilla Bug 674720</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+var isnot = parent.isnot;
+
+var req;
+
+var steps = [
+  function () {
+    ok(true, "Adding a new contact");
+    createResult1 = new mozContact(properties1);
+    req = mozContacts.save(createResult1)
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact2");
+    createResult2 = new mozContact(properties2);
+    req = mozContacts.save(createResult2);
+    req.onsuccess = function () {
+      ok(createResult2.id, "The contact now has an ID.");
+      sample_id2 = createResult2.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({sortBy: "familyName"});
+    req.onsuccess = function () {
+      is(req.result.length, 2, "Found exactly 2 contact.");
+      checkContacts(req.result[1], properties1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    console.log("Searching contacts by query1");
+    var options = {filterBy: ["givenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0, 4)}
+    req = mozContacts.find(options)
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, createResult1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by query2");
+    var options = {filterBy: ["givenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties2.givenName[0].substring(0, 4)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.adr.length, 2, "Adr length 2");
+      checkContacts(findResult1, createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by tel");
+    var options = {filterBy: ["tel"],
+                   filterOp: "contains",
+                   filterValue: properties2.tel[0].value.substring(3, 7)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id2, "Same ID");
+      checkContacts(findResult1, createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by email");
+    var options = {filterBy: ["email"],
+                   filterOp: "startsWith",
+                   filterValue: properties2.email[0].value.substring(0, 4)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id2, "Same ID");
+      checkContacts(findResult1, createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding 20 contacts");
+    for (var i=0; i<19; i++) {
+      createResult1 = new mozContact(properties1);
+      req = mozContacts.save(createResult1);
+      req.onsuccess = function () {
+        ok(createResult1.id, "The contact now has an ID.");
+      };
+      req.onerror = onFailure;
+    };
+    createResult1 = new mozContact(properties1);
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkStrArray(createResult1.name, properties1.name, "Same Name");
+      checkCount(20, "20 contacts in DB", next);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts with limit 10");
+    var options = { filterLimit: 10 };
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 10, "10 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts with limit 10 and sorted");
+    var options = { filterLimit: 10,
+                    sortBy: 'FamilyName',
+                    sortOrder: 'descending' };
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 10, "10 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts2");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0, 4)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      checkContacts(createResult1, req.result[19]);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts3");
+    var options = {filterBy: ["givenName", "tel", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0, 4)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      checkContacts(createResult1, req.result[10]);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Testing clone contact");
+    createResult1 = new mozContact(properties1);
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkStrArray(createResult1.name, properties1.name, "Same Name");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Testing clone contact2");
+    var cloned = new mozContact(createResult1);
+    ok(cloned.id != createResult1.id, "Cloned contact has new ID");
+    cloned.email = [{value: "new email!"}];
+    cloned.givenName = ["Tom"];
+    req = mozContacts.save(cloned);
+    req.onsuccess = function () {
+      ok(cloned.id, "The contact now has an ID.");
+      is(cloned.email[0].value, "new email!", "Same Email");
+      isnot(createResult1.email[0].value, cloned.email[0].value, "Clone has different email");
+      is(String(cloned.givenName), "Tom", "New Name");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties2.givenName[0].substring(0, 4)};
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function () {
+      is(req.result.length, 2, "2 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Search with redundant fields should only return 1 contact");
+    createResult1 = new mozContact({name: ["XXX"],
+                                    givenName: ["XXX"],
+                                    email: [{value: "XXX"}],
+                                    tel: [{value: "XXX"}]
+                                   });
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function() {
+      var options = {filterBy: ["givenName", "familyName"],
+                     filterOp: "equals",
+                     filterValue: "XXX"};
+      var req2 = mozContacts.find(options);
+      req2.onsuccess = function() {
+        is(req2.result.length, 1, "1 Entry");
+        next();
+      }
+      req2.onerror = onFailure;
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c3);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c3, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c2);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c2, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c4);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c4, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c1);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c1, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    var options = {sortBy: "familyName",
+                   sortOrder: "ascending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 4, "4 results");
+      checkContacts(req.result[0], c1);
+      checkContacts(req.result[1], c2);
+      checkContacts(req.result[2], c3);
+      checkContacts(req.result[3], c4);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    var options = {sortBy: "familyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 4, "4 results");
+      checkContacts(req.result[0], c4);
+      checkContacts(req.result[1], c3);
+      checkContacts(req.result[2], c2);
+      checkContacts(req.result[3], c1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c5);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c5, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting with empty string");
+    var options = {sortBy: "familyName",
+                   sortOrder: "ascending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 5, "5 results");
+      checkContacts(req.result[0], c5);
+      checkContacts(req.result[1], c1);
+      checkContacts(req.result[2], c2);
+      checkContacts(req.result[3], c3);
+      checkContacts(req.result[4], c4);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Don't allow to add custom fields");
+    createResult1 = new mozContact({givenName: ["customTest"], yyy: "XXX"});
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function() {
+      var options = {filterBy: ["givenName"],
+                     filterOp: "equals",
+                     filterValue: "customTest"};
+      var req2 = mozContacts.find(options);
+      req2.onsuccess = function() {
+        is(req2.result.length, 1, "1 Entry");
+        checkStrArray(req2.result[0].givenName, ["customTest"], "same name");
+        ok(req2.result.yyy === undefined, "custom property undefined");
+        next();
+      }
+      req2.onerror = onFailure;
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c7);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c7, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c6);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c6, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c8);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c8, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    // Android does not support published/updated fields. Skip this.
+    if (isAndroid) {
+      next();
+      return;
+    }
+
+    ok(true, "Test sorting with published");
+    var options = {sortBy: "familyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 3, "3 results");
+      ok(req.result[0].published < req.result[1].published, "Right sorting order");
+      ok(req.result[1].published < req.result[2].published, "Right sorting order");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact with properties2");
+    createResult2 = new mozContact(properties2);
+    req = mozContacts.save(createResult2);
+    req.onsuccess = function () {
+      ok(createResult2.id, "The contact now has an ID.");
+      sample_id2 = createResult2.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test category search with startsWith");
+    var options = {filterBy: ["category"],
+                   filterOp: "startsWith",
+                   filterValue: properties2.category[0]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "1 Entry.");
+      checkContacts(req.result[0], createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test category search with equals");
+    var options = {filterBy: ["category"],
+                   filterOp: "equals",
+                   filterValue: properties2.category[0]};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "1 Entry.");
+      checkContacts(req.result[0], createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact for category search");
+    createResult1 = new mozContact({name: ["5"], givenName: ["5"]});
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test category search with equals");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: "5"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "1 Entry.");
+      checkContacts(req.result[0], createResult1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact with invalid data");
+    var obj = {
+        honorificPrefix: [],
+        honorificSuffix: [{foo: "bar"}],
+        sex: 17,
+        genderIdentity: 18,
+        email: [{type: ["foo"], value: "bar"}]
+    };
+    obj.honorificPrefix.__defineGetter__('0',(function() {
+      var c = 0;
+      return function() {
+        if (c == 0) {
+          c++;
+          return "string";
+        } else {
+          return {foo:"bar"};
+        }
+      }
+    })());
+    createResult1 = new mozContact(obj);
+    createResult1.email.push({aeiou: "abcde"});
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      checkContacts(createResult1, {
+        honorificPrefix: ["string"],
+        honorificSuffix: ["[object Object]"],
+        sex: "17",
+        genderIdentity: "18",
+        email: [{type: ["foo"], value: "bar"}, {}]
+      });
+      next();
+    };
+  },
+  function () {
+    ok(true, "Adding contact with no number but carrier");
+    createResult1 = new mozContact({ tel: [{type: ["home"], carrier: "myCarrier"} ] });
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact with email but no value");
+    createResult1 = new mozContact({ email: [{type: ["home"]}] });
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Testing numbersOnly search 1");
+    createResult1 = new mozContact({ name: ["aaaaaaaaa"], givenName: ["aaaaaaaaa"], tel: [{ value: "1234567890"}]});
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test numbersOnly search 2");
+    var options = {filterBy: ["givenName", "tel"],
+                   filterOp: "contains",
+                   filterValue: "a"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "1 Entry.");
+      checkContacts(req.result[0], createResult1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test numbersOnly search 3");
+    var options = {filterBy: ["givenName", "tel"],
+                   filterOp: "contains",
+                   filterValue: "b"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 0, "0 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test numbersOnly search 4");
+    var options = {filterBy: ["givenName", "tel"],
+                   filterOp: "contains",
+                   filterValue: "1a"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 0, "0 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test numbersOnly search 5");
+    var options = {filterBy: ["givenName", "tel"],
+                   filterOp: "contains",
+                   filterValue: "1(23)"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "1 Entry.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test numbersOnly search 6");
+    var options = {filterBy: ["givenName", "tel"],
+                   filterOp: "contains",
+                   filterValue: "1(23)a"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 0, "0 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Test that after setting array properties to scalar values the property os not a non-array")
+    const FIELDS = ["email","url","adr","tel","impp"];
+    createResult1 = new mozContact();
+    for (var prop of FIELDS) {
+      try {
+        createResult1[prop] = {type: ["foo"]};
+      } catch (e) {}
+      ok(createResult1[prop] === null ||
+         Array.isArray(createResult1[prop]), prop + " is array");
+    }
+    next();
+  },
+  function() {
+    ok(true, "Undefined properties of fields should be treated correctly");
+    var c = new mozContact({
+      adr: [{streetAddress: undefined}],
+      email: [{value: undefined}],
+      url: [{value: undefined}],
+      impp: [{value: undefined}],
+      tel: [{value: undefined}],
+    });
+    is(c.adr[0].streetAddress, undefined, "adr.streetAddress is undefined");
+    is(c.adr[0].locality, undefined, "adr.locality is undefined");
+    is(c.adr[0].pref, undefined, "adr.pref is undefined");
+    is(c.email[0].value, undefined, "email.value is undefined");
+    is(c.url[0].value, undefined, "url.value is undefined");
+    is(c.impp[0].value, undefined, "impp.value is undefined");
+    is(c.tel[0].value, undefined, "tel.value is undefined");
+    next();
+  },
+  function() {
+    ok(true, "Setting array properties to an empty array should work");
+    var c = new mozContact();
+    function testArrayProp(prop) {
+      is(c[prop], null, "property is initially null");
+      c[prop] = [];
+      ok(Array.isArray(c[prop]), "property is an array after setting");
+      is(c[prop].length, 0, "property has length 0 after setting");
+    }
+    testArrayProp("email");
+    testArrayProp("adr");
+    testArrayProp("tel");
+    testArrayProp("impp");
+    testArrayProp("url");
+    next();
+  },
+  function() {
+    ok(true, "Passing a mozContact with invalid data to save() should throw");
+    var c = new mozContact({
+      photo: [],
+      tel: []
+    });
+    c.photo.push({});
+    SimpleTest.doesThrow(()=>navigator.mozContacts.save(c), "Invalid data in Blob array");
+    c.tel.push(123);
+    SimpleTest.doesThrow(()=>navigator.mozContacts.save(c), "Invalid data in dictionary array");
+    next();
+  },
+  function() {
+    ok(true, "Inline changes to array properties should be seen by save");
+    var c = new mozContact({
+      name: [],
+      familyName: [],
+      givenName: [],
+      phoneticFamilyName: [],
+      phoneticGivenName: [],
+      nickname: [],
+      tel: [],
+      adr: [],
+      email: []
+    });
+    for (var prop of Object.getOwnPropertyNames(properties1)) {
+      if (!Array.isArray(properties1[prop])) {
+        continue;
+      }
+      for (var i = 0; i < properties1[prop].length; ++i) {
+        c[prop].push(properties1[prop][i]);
+      }
+    }
+    req = navigator.mozContacts.save(c);
+    req.onsuccess = function() {
+      req = navigator.mozContacts.find(defaultOptions);
+      req.onsuccess = function() {
+        is(req.result.length, 1, "Got 1 contact");
+        checkContacts(req.result[0], properties1);
+        next();
+      };
+      req.onerror = onFailure;
+    };
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function() {
+    ok(true, "mozContact.init deprecation message");
+    var c = new mozContact();
+    SimpleTest.monitorConsole(next, [
+      { errorMessage: "mozContact.init is DEPRECATED. Use the mozContact constructor instead. " +
+                      "See https://developer.mozilla.org/docs/WebAPI/Contacts for details." }
+    ], /* forbidUnexpectedMsgs */ true);
+    c.init({name: ["Bar"]});
+    c.init({name: ["Bar"]});
+    SimpleTest.endMonitorConsole();
+  },
+  function() {
+    ok(true, "mozContact.init works as expected");
+    var c = new mozContact({name: ["Foo"]});
+    c.init({name: ["Bar"]});
+    is(c.name[0], "Bar", "Same name");
+    next();
+  },
+  function() {
+    ok(true, "mozContact.init without parameters");
+    var c = new mozContact({name: ["Foo"]});
+    c.init();
+    next();
+  },
+  function() {
+    ok(true, "mozContact.init resets properties");
+    var c = new mozContact({jobTitle: ["Software Engineer"]});
+    c.init({nickname: ["Jobless Johnny"]});
+    is(c.nickname[0], "Jobless Johnny", "Same nickname");
+    ok(!c.jobTitle, "jobTitle is not set");
+    next();
+  },
+  function() {
+    ok(true, "mozContacts.remove with an ID works");
+    var c = new mozContact({name: ["Ephemeral Jimmy"]});
+    req = navigator.mozContacts.save(c);
+    req.onsuccess = function() {
+      req = navigator.mozContacts.remove(c.id);
+      req.onsuccess = function() {
+        req = navigator.mozContacts.find({
+          filterBy: ["id"],
+          filterOp: "equals",
+          filterValue: c.id
+        });
+        req.onsuccess = function() {
+          is(req.result.length, 0, "Successfully removed contact by ID");
+          next();
+        };
+        req.onerror = onFailure;
+      };
+      req.onerror = onFailure;
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact");
+    createResult1 = new mozContact(properties3);
+    req = mozContacts.save(createResult1)
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact2");
+    createResult2 = new mozContact(properties4);
+    req = mozContacts.save(createResult2);
+    req.onsuccess = function () {
+      ok(createResult2.id, "The contact now has an ID.");
+      sample_id2 = createResult2.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({sortBy: "phoneticFamilyName"});
+    req.onsuccess = function () {
+      is(req.result.length, 2, "Found exactly 2 contact.");
+      checkContacts(req.result[1], properties3);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by query1");
+    var options = {filterBy: ["phoneticGivenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)}
+    req = mozContacts.find(options)
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, createResult1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by query2");
+    var options = {filterBy: ["phoneticGivenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties4.phoneticGivenName[0].substring(0, 3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.adr.length, 2, "Adr length 2");
+      checkContacts(findResult1, createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Adding 20 contacts");
+    for (var i=0; i<19; i++) {
+      createResult1 = new mozContact(properties3);
+      req = mozContacts.save(createResult1);
+      req.onsuccess = function () {
+        ok(createResult1.id, "The contact now has an ID.");
+      };
+      req.onerror = onFailure;
+    };
+    createResult1 = new mozContact(properties3);
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkStrArray(createResult1.name, properties3.name, "Same Name");
+      checkCount(20, "20 contacts in DB", next);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts2");
+    var options = {filterBy: ["phoneticGivenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      checkContacts(createResult1, req.result[19]);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts3");
+    var options = {filterBy: ["phoneticGivenName", "tel", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      checkContacts(createResult1, req.result[10]);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Testing clone contact");
+    createResult1 = new mozContact(properties3);
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkStrArray(createResult1.phoneticFamilyName, properties3.phoneticFamilyName, "Same phoneticFamilyName");
+      checkStrArray(createResult1.phoneticGivenName, properties3.phoneticGivenName, "Same phoneticGivenName");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({sortBy: "phoneticGivenName"});
+    req.onsuccess = function () {
+      is(req.result.length, 1, "1 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c11);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c11, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c10);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c10, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c12);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c12, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c9);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c9, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "ascending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 4, "4 results");
+      checkContacts(req.result[0], c9);
+      checkContacts(req.result[1], c10);
+      checkContacts(req.result[2], c11);
+      checkContacts(req.result[3], c12);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 4, "4 results");
+      checkContacts(req.result[0], c12);
+      checkContacts(req.result[1], c11);
+      checkContacts(req.result[2], c10);
+      checkContacts(req.result[3], c9);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c13);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c13, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting with empty string");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "ascending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 5, "5 results");
+      checkContacts(req.result[0], c13);
+      checkContacts(req.result[1], c9);
+      checkContacts(req.result[2], c10);
+      checkContacts(req.result[3], c11);
+      checkContacts(req.result[4], c12);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c15);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c15, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c14);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c14, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c16);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c16, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    // Android does not support published/updated fields. Skip this.
+    if (isAndroid) {
+      next();
+      return;
+    }
+
+    ok(true, "Test sorting with published");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 3, "3 results");
+      ok(req.result[0].published < req.result[1].published, "Right sorting order");
+      ok(req.result[1].published < req.result[2].published, "Right sorting order");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+function next() {
+  ok(true, "Begin!");
+  if (index >= steps.length) {
+    ok(false, "Shouldn't get here!");
+    return;
+  }
+  try {
+    var i = index++;
+    steps[i]();
+  } catch(ex) {
+    ok(false, "Caught exception", ex);
+  }
+}
+
+start_tests();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_blobs.html
@@ -0,0 +1,226 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674720
+-->
+<head>
+  <title>Test for Bug 674720 WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674720">Mozilla Bug 674720</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+var isnot = parent.isnot;
+
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+function getView(size)
+{
+ var buffer = new ArrayBuffer(size);
+ var view = new Uint8Array(buffer);
+ is(buffer.byteLength, size, "Correct byte length");
+ return view;
+}
+
+function getRandomView(size)
+{
+ var view = getView(size);
+ for (var i = 0; i < size; i++) {
+   view[i] = parseInt(Math.random() * 255)
+ }
+ return view;
+}
+
+function getRandomBlob(size)
+{
+  return new Blob([getRandomView(size)], { type: "binary/random" });
+}
+
+function compareBuffers(buffer1, buffer2)
+{
+  if (buffer1.byteLength != buffer2.byteLength) {
+    return false;
+  }
+  var view1 = new Uint8Array(buffer1);
+  var view2 = new Uint8Array(buffer2);
+  for (var i = 0; i < buffer1.byteLength; i++) {
+    if (view1[i] != view2[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+function verifyBuffers(buffer1, buffer2, isLast)
+{
+  ok(compareBuffers(buffer1, buffer2), "Correct blob data");
+  if (isLast)
+    next();
+}
+
+var randomBlob = getRandomBlob(1024);
+var randomBlob2 = getRandomBlob(1024);
+
+var properties1 = {
+  name: ["xTestname1"],
+  givenName: ["xTestname1"],
+  photo: [randomBlob]
+};
+
+var properties2 = {
+  name: ["yTestname2"],
+  givenName: ["yTestname2"],
+  photo: [randomBlob, randomBlob2]
+};
+
+var sample_id1;
+var createResult1;
+var findResult1;
+
+function verifyBlob(blob1, blob2, isLast)
+{
+  is(blob1 instanceof Blob, true,
+     "blob1 is an instance of DOMBlob");
+  is(blob2 instanceof Blob, true,
+     "blob2 is an instance of DOMBlob");
+  isnot(blob1 instanceof File, true,
+     "blob1 is an instance of File");
+  isnot(blob2 instanceof File, true,
+     "blob2 is an instance of File");
+  is(blob1.size, blob2.size, "Same size");
+  is(blob1.type, blob2.type, "Same type");
+
+  var buffer1;
+  var buffer2;
+
+  var reader1 = new FileReader();
+  reader1.readAsArrayBuffer(blob2);
+  reader1.onload = function(event) {
+    buffer2 = event.target.result;
+    if (buffer1) {
+      verifyBuffers(buffer1, buffer2, isLast);
+    }
+  }
+
+  var reader2 = new FileReader();
+  reader2.readAsArrayBuffer(blob1);
+  reader2.onload = function(event) {
+    buffer1 = event.target.result;
+    if (buffer2) {
+      verifyBuffers(buffer1, buffer2, isLast);
+    }
+  }
+}
+
+function verifyBlobArray(blobs1, blobs2)
+{
+  is(blobs1 instanceof Array, true, "blobs1 is an array object");
+  is(blobs2 instanceof Array, true, "blobs2 is an array object");
+  is(blobs1.length, blobs2.length, "Same length");
+
+  if (!blobs1.length) {
+    next();
+    return;
+  }
+
+  for (var i = 0; i < blobs1.length; i++) {
+    verifyBlob(blobs1[i], blobs2[i], i == blobs1.length - 1);
+  }
+}
+
+var req;
+
+var steps = [
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact with photo");
+    createResult1 = new mozContact(properties1);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties1.givenName[0].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      verifyBlobArray(findResult1.photo, properties1.photo);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact with 2 photos");
+    createResult1 = new mozContact(properties2);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring");
+    var options = {filterBy: ["givenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties2.givenName[0].substring(0,3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      verifyBlobArray(findResult1.photo, properties2.photo);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "all done!\n");
+
+    parent.SimpleTest.finish();
+  }
+];
+
+start_tests();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_events.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=764667
+-->
+<head>
+  <title>Test for Bug 678695</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=764667">Mozilla Bug 764667</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 764667 **/
+
+var ok = parent.ok;
+var is = parent.is;
+
+var e = new MozContactChangeEvent("contactchanged", {contactID: "123", reason: "create"});
+ok(e, "Should have contactsChange event!");
+is(e.contactID, "123", "ID should be 123.");
+is(e.reason, "create", "Reason should be create.");
+
+e = new MozContactChangeEvent("contactchanged", {contactID: "test", reason: "test"});
+is(e.contactID, "test", "Name should be 'test'.");
+is(e.reason, "test", "Name should be 'test'.");
+
+e = new MozContactChangeEvent("contactchanged", {contactID: "a", reason: ""});
+is(e.contactID, "a", "Name should be a.");
+is(e.reason, "", "Value should be empty");
+
+parent.SimpleTest.finish();
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_getall.html
@@ -0,0 +1,156 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=836519
+-->
+<head>
+  <title>Mozilla Bug 836519</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836519">Mozilla Bug 836519</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript;version=1.8">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+var isnot = parent.isnot;
+
+let req;
+
+let steps = [
+  function start() {
+    SpecialPowers.Cc["@mozilla.org/tools/profiler;1"].getService(SpecialPowers.Ci.nsIProfiler).AddMarker("GETALL_START");
+    next();
+  },
+  clearDatabase,
+  addContacts,
+
+  function() {
+    ok(true, "Delete the current contact while iterating");
+    req = mozContacts.getAll({});
+    let count = 0;
+    let previousId = null;
+    req.onsuccess = function() {
+      if (req.result) {
+        ok(true, "on success");
+        if (previousId) {
+          isnot(previousId, req.result.id, "different contacts returned");
+        }
+        previousId = req.result.id;
+        count++;
+        let delReq = mozContacts.remove(req.result);
+        delReq.onsuccess = function() {
+          ok(true, "deleted current contact");
+          req.continue();
+        };
+      } else {
+        is(count, 40, "returned 40 contacts");
+        next();
+      }
+    };
+  },
+
+  clearDatabase,
+  addContacts,
+
+  function() {
+    ok(true, "Iterating through the contact list inside a cursor callback");
+    let count1 = 0, count2 = 0;
+    let req1 = mozContacts.getAll({});
+    let req2;
+    req1.onsuccess = function() {
+      if (count1 == 0) {
+        count1++;
+        req2 = mozContacts.getAll({});
+        req2.onsuccess = function() {
+          if (req2.result) {
+            count2++;
+            req2.continue();
+          } else {
+            is(count2, 40, "inner cursor returned 40 contacts");
+            req1.continue();
+          }
+        };
+      } else {
+        if (req1.result) {
+          count1++;
+          req1.continue();
+        } else {
+          is(count1, 40, "outer cursor returned 40 contacts");
+          next();
+        }
+      }
+    };
+  },
+
+  clearDatabase,
+  addContacts,
+
+  function() {
+    ok(true, "20 concurrent cursors");
+    const NUM_CURSORS = 20;
+    let completed = 0;
+    for (let i = 0; i < NUM_CURSORS; ++i) {
+      mozContacts.getAll({}).onsuccess = (function(i) {
+        let count = 0;
+        return function(event) {
+          let req = event.target;
+          if (req.result) {
+            count++;
+            req.continue();
+          } else {
+            is(count, 40, "cursor " + i + " returned 40 contacts");
+            if (++completed == NUM_CURSORS) {
+              next();
+            }
+          }
+        };
+      })(i);
+    }
+  },
+
+  clearDatabase,
+  addContacts,
+
+  function() {
+    if (!SpecialPowers.isMainProcess()) {
+      // We stop calling continue() intentionally here to see if the cursor gets
+      // cleaned up properly in the parent.
+      ok(true, "Leaking a cursor");
+      req = mozContacts.getAll({
+        sortBy: "familyName",
+        sortOrder: "ascending"
+      });
+      req.onsuccess = function(event) {
+        next();
+      };
+      req.onerror = onFailure;
+    } else {
+      next();
+    }
+  },
+
+  clearDatabase,
+
+  function() {
+    ok(true, "all done!\n");
+    SpecialPowers.Cc["@mozilla.org/tools/profiler;1"].getService(SpecialPowers.Ci.nsIProfiler).AddMarker("GETALL_END");
+    parent.SimpleTest.finish();
+  }
+];
+
+start_tests();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_getall2.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=836519
+-->
+<head>
+  <title>Mozilla Bug 836519</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836519">Mozilla Bug 836519</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript;version=1.8">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+
+let req;
+
+let steps = [
+  clearDatabase,
+  function() {
+    // add a contact
+    createResult1 = new mozContact({});
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function() {
+      next();
+    };
+    req.onerror = onFailure;
+  },
+
+  getOne(),
+  getOne("Retrieving one contact with getAll - cached"),
+
+  clearDatabase,
+  addContacts,
+
+  getAll(),
+  getAll("Retrieving 40 contacts with getAll - cached"),
+
+  function() {
+    ok(true, "Deleting one contact");
+    req = mozContacts.remove(createResult1);
+    req.onsuccess = function() {
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Test cache invalidation");
+    req = mozContacts.getAll({});
+    let count = 0;
+    req.onsuccess = function(event) {
+      ok(true, "on success");
+      if (req.result) {
+        ok(true, "result is valid");
+        count++;
+        req.continue();
+      } else {
+        is(count, 39, "last contact - 39 contacts returned");
+        next();
+      }
+    };
+    req.onerror = onFailure;
+  },
+
+  clearDatabase,
+  addContacts,
+
+  function() {
+    ok(true, "Test cache consistency when deleting contact during getAll");
+    req = mozContacts.find({});
+    req.onsuccess = function(e) {
+      let lastContact = e.target.result[e.target.result.length-1];
+      req = mozContacts.getAll({});
+      let count = 0;
+      let firstResult = true;
+      req.onsuccess = function(event) {
+        ok(true, "on success");
+        if (firstResult) {
+          if (req.result) {
+            count++;
+          }
+          let delReq = mozContacts.remove(lastContact);
+          delReq.onsuccess = function() {
+            firstResult = false;
+            req.continue();
+          };
+        } else {
+          if (req.result) {
+            ok(true, "result is valid");
+            count++;
+            req.continue();
+          } else {
+            is(count, 40, "last contact - 40 contacts returned");
+            next();
+          }
+        }
+      };
+    };
+  },
+
+  clearDatabase,
+
+  function() {
+    ok(true, "all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+start_tests();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_international.html
@@ -0,0 +1,277 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=815833
+-->
+<head>
+  <title>Test for Bug 815833 WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=815833">Mozilla Bug 815833</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var ise = parent.ise;
+
+var number1 = {
+  local: "7932012345",
+  international: "+557932012345"
+};
+
+var number2 = {
+  local: "7932012346",
+  international: "+557932012346"
+};
+
+var properties1 = {
+  name: ["Testname1"],
+  tel: [{type: ["work"], value: number1.local, carrier: "testCarrier"} , {type: ["home", "fax"], value: number2.local}],
+};
+
+var shortNumber = "888";
+var properties2 = {
+  name: ["Testname2"],
+  tel: [{type: ["work"], value: shortNumber, carrier: "testCarrier"}]
+};
+
+var number3 = {
+  local: "7932012345",
+  international: "+557932012345"
+};
+
+var properties3 = {
+  name: ["Testname2"],
+  tel: [{value: number3.international}]
+};
+
+var req;
+var createResult1;
+var findResult1;
+var sample_id1;
+
+var steps = [
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact1");
+    createResult1 = new mozContact(properties1);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact2");
+    var createResult2 = new mozContact(properties2);
+    req = navigator.mozContacts.save(createResult2);
+    req.onsuccess = function () {
+      ok(createResult2.id, "The contact now has an ID.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for local number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: number1.local};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for international number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: number1.international};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found exactly 0 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for a short number matching the prefix");
+    var shortNumber = number1.local.substring(0, 3);
+    var options = {filterBy: ["tel"],
+                   filterOp: "equals",
+                   filterValue: shortNumber};
+    req = mozContacts.find(options);
+    req.onsuccess = function() {
+      is(req.result.length, 0, "The prefix short number should not match any contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for a short number matching the suffix");
+    var shortNumber = number1.local.substring(number1.local.length - 3);
+    var options = {filterBy: ["tel"],
+                   filterOp: "equals",
+                   filterValue: shortNumber};
+    req = mozContacts.find(options);
+    req.onsuccess = function() {
+      is(req.result.length, 0, "The suffix short number should not match any contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for a short number matching a contact");
+    var options = {filterBy: ["tel"],
+                   filterOp: "equals",
+                   filterValue: shortNumber};
+    req = mozContacts.find(options);
+    req.onsuccess = function() {
+      is(req.result.length, 1, "Found the contact equally matching the shortNumber.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function() {
+    ok(true, "Modifying number");
+    if (!findResult1) {
+      SpecialPowers.executeSoon(next);
+    } else {
+      findResult1.tel[0].value = number2.local;
+      req = mozContacts.save(findResult1);
+      req.onsuccess = function () {
+        next();
+      };
+    }
+  },
+  function () {
+    ok(true, "Searching for local number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: number1.local};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found exactly 0 contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for local number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: number1.international};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found exactly 0 contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for local number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: number2.local};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for local number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "startsWith",
+                   filterValue: number2.international};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found exactly 1 contact.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a contact with a Brazilian country code");
+    createResult1 = new mozContact(properties3);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for Brazilian number using local number");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: number3.local};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+SpecialPowers.pushPrefEnv({
+  set: [
+    ["ril.lastKnownSimMcc", "000"]
+  ]
+}, start_tests);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_substringmatching.html
@@ -0,0 +1,351 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877302
+-->
+<head>
+  <title>Test for Bug 877302 substring matching for WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877302">Mozilla Bug 877302</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+
+var substringLength = 8;
+
+var prop = {
+  tel: [{value: "7932012345" }, {value: "7932012346"}]
+};
+
+var prop2 = {
+  tel: [{value: "01187654321" }]
+};
+
+var prop3 = {
+  tel: [{ value: "+43332112346" }]
+};
+
+var prop4 = {
+  tel: [{ value: "(0414) 233-9888" }]
+};
+
+var brazilianNumber = {
+  international1: "0041557932012345",
+  international2: "+557932012345"
+};
+
+var prop5 = {
+  tel: [{value: brazilianNumber.international2}]
+};
+
+var req;
+var steps = [
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact");
+    createResult1 = new mozContact(prop);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({});
+    req.onsuccess = function () {
+      is(req.result.length, 1, "One contact.");
+      findResult1 = req.result[0];
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 1");
+    var length = prop.tel[0].value.length;
+    var num = prop.tel[0].value.substring(length - substringLength, length);
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      is(findResult1.tel[0].value, "7932012345", "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 2");
+    var length = prop.tel[1].value.length;
+    var num = prop.tel[1].value.substring(length - substringLength, length);
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      is(findResult1.tel[0].value, "7932012345", "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 3");
+    var length = prop.tel[0].value.length;
+    var num = prop.tel[0].value.substring(length - substringLength + 1, length);
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found exactly 0 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 4");
+    var length = prop.tel[0].value.length;
+    var num = prop.tel[0].value.substring(length - substringLength - 1, length);
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact");
+    createResult1 = new mozContact(prop2);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 5");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "87654321"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 6");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "01187654321"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 7");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "909087654321"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 8");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "0411187654321"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 9");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "90411187654321"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 10");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: "+551187654321"};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact");
+    createResult1 = new mozContact(prop3);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    if (!isAndroid) { // Bug 905927
+      ok(true, "Retrieving by substring 1");
+      var length = prop3.tel[0].value.length;
+      var num = prop3.tel[0].value.substring(length - substringLength, length);
+      var options = {filterBy: ["tel"],
+                     filterOp: "match",
+                     filterValue: num};
+      req = mozContacts.find(options);
+      req.onsuccess = function () {
+        is(req.result.length, 0, "Found exactly 0 contacts.");
+        next();
+      };
+      req.onerror = onFailure;
+    } else {
+      SpecialPowers.executeSoon(next);
+    }
+  },
+  function () {
+    ok(true, "Adding contact");
+    createResult1 = new mozContact(prop4);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 1");
+    var num = "(0424) 233-9888"
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact with a Brazilian country code");
+    createResult1 = new mozContact(prop5);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for international number with prefix");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: brazilianNumber.international1};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+SpecialPowers.pushPrefEnv({
+  set: [
+    ["dom.phonenumber.substringmatching.BR", substringLength],
+    ["ril.lastKnownSimMcc", "724"]
+  ]
+}, start_tests);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_substringmatchingCL.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877302
+-->
+<head>
+  <title>Test for Bug 949537 substring matching for WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=949537">Mozilla Bug 949537</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var ise = parent.ise;
+
+var landlineNumber = "+56 2 27654321";
+
+var number = {
+  local: "87654321",
+  international: "+56 9 87654321"
+};
+
+var properties = {
+  name: ["Testname2"],
+  tel: [{value: number.international}]
+};
+
+var req;
+var steps = [
+  function () {
+    ok(true, "Adding a contact with a Chilean number");
+    createResult1 = new mozContact(properties);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for Chilean number with prefix");
+    req = mozContacts.find({
+      filterBy: ["tel"],
+      filterOp: "match",
+      filterValue: number.international
+    });
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for Chilean number using local number");
+    req = mozContacts.find({
+      filterBy: ["tel"],
+      filterOp: "match",
+      filterValue: number.local
+    });
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found 0 contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+
+  clearDatabase,
+
+  function () {
+    ok(true, "Adding contact with mobile number");
+    createResult1 = new mozContact({tel: [{value: number.international}]});
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({});
+    req.onsuccess = function () {
+      is(req.result.length, 1, "One contact.");
+      findResult1 = req.result[0];
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by last 8 digits");
+    req = mozContacts.find({
+      filterBy: ["tel"],
+      filterOp: "match",
+      filterValue: number.international.slice(-8)
+    });
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.tel[0].value, number.international, "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by last 9 digits");
+    req = mozContacts.find({
+      filterBy: ["tel"],
+      filterOp: "match",
+      filterValue: number.international.slice(-9)
+    });
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.tel[0].value, number.international, "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by last 6 digits");
+    req = mozContacts.find({
+      filterBy: ["tel"],
+      filterOp: "match",
+      filterValue: number.international.slice(-6)
+    });
+    req.onsuccess = function () {
+      is(req.result.length, 0, "Found exactly zero contacts.");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+
+  clearDatabase,
+
+  function () {
+    ok(true, "Adding contact with landline number");
+    createResult1 = new mozContact({tel: [{value: landlineNumber}]});
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({});
+    req.onsuccess = function () {
+      is(req.result.length, 1, "One contact.");
+      findResult1 = req.result[0];
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by last 7 digits (local number) with landline calling prefix");
+    req = mozContacts.find({
+      filterBy: ["tel"],
+      filterOp: "match",
+      filterValue: "022" + landlineNumber.slice(-7)
+    });
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.id, sample_id1, "Same ID");
+      is(findResult1.tel[0].value, landlineNumber, "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+
+  clearDatabase,
+
+  function () {
+    ok(true, "all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+SpecialPowers.pushPrefEnv({
+  set: [
+    ["dom.phonenumber.substringmatching.CL", 8],
+    ["ril.lastKnownSimMcc", "730"]
+  ]
+}, start_tests);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_contacts_substringmatchingVE.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877302
+-->
+<head>
+  <title>Test for Bug 877302 substring matching for WebContacts</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877302">Mozilla Bug 877302</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var is = parent.is;
+
+var prop = {
+  tel: [{value: "7932012345" }, {value: "7704143727591"}]
+};
+
+var prop2 = {
+  tel: [{value: "7932012345" }, {value: "+58 212 5551212"}]
+};
+
+var req;
+var steps = [
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact");
+    createResult1 = new mozContact(prop);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({});
+    req.onsuccess = function () {
+      is(req.result.length, 1, "One contact.");
+      findResult1 = req.result[0];
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 1");
+    var length = prop.tel[0].value.length;
+    var num = "04143727591"
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      is(findResult1.tel[1].value, "7704143727591", "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding contact");
+    createResult1 = new mozContact(prop2);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving by substring 2");
+    var num = "5551212";
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: num};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      is(findResult1.tel[1].value, "+58 212 5551212", "Same Value");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+SpecialPowers.pushPrefEnv({
+  set: [
+    ["dom.phonenumber.substringmatching.VE", 7],
+    ["ril.lastKnownSimMcc", "734"]
+  ]
+}, start_tests);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_migration.html
@@ -0,0 +1,197 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Migration tests</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<h1>migration tests</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var ok = parent.ok;
+var ise = parent.ise;
+
+var backend, contactsCount, allContacts;
+function loadChromeScript() {
+  var url = SimpleTest.getTestFileURL("test_migration_chrome.js");
+  backend = SpecialPowers.loadChromeScript(url);
+}
+
+function addBackendEvents() {
+  backend.addMessageListener("createDB.success", function(count) {
+    contactsCount = count;
+    ok(true, "Created the database");
+    next();
+  });
+  backend.addMessageListener("createDB.error", function(err) {
+    ok(false, err);
+    next();
+  });
+
+  backend.addMessageListener("deleteDB.success", function() {
+    ok(true, "Deleted the database");
+    next();
+  });
+  backend.addMessageListener("deleteDB.error", function(err) {
+    ok(false, err);
+    next();
+  });
+}
+
+function createDB(version) {
+  info("Will create the DB at version " + version);
+  backend.sendAsyncMessage("createDB", version);
+}
+
+function deleteDB() {
+  info("Will delete the DB.");
+  backend.sendAsyncMessage("deleteDB");
+}
+
+var steps = [
+  function setupChromeScript() {
+    loadChromeScript();
+    addBackendEvents();
+    next();
+  },
+
+  deleteDB, // let's be sure the DB does not exist yet
+  createDB.bind(null, 12),
+
+  function testAccessMozContacts() {
+    info("Checking we have the right number of contacts: " + contactsCount);
+    var req = mozContacts.getCount();
+    req.onsuccess = function onsuccess() {
+      ok(true, "Could access the mozContacts API");
+      is(this.result, contactsCount, "Contacts count is correct");
+      next();
+    };
+
+    req.onerror = function onerror() {
+      ok(false, "Couldn't access the mozContacts API");
+      next();
+    };
+  },
+
+  function testRetrieveAllContacts() {
+    /* if the migration does not work right, either we'll have an error, or the
+       contacts won't be migrated properly and thus will fail WebIDL conversion,
+       which will manifest as a timeout */
+    info("Checking the contacts are corrected to obey WebIDL constraints.  (upgrades 14 to 17)");
+    var req = mozContacts.find();
+    req.onsuccess = function onsuccess() {
+      if (this.result) {
+        is(this.result.length, contactsCount, "Contacts array length is correct");
+        allContacts = this.result;
+        next();
+      } else {
+        ok(false, "Could access the mozContacts API but got no contacts!");
+        next();
+      }
+    };
+
+    req.onerror = function onerror() {
+      ok(false, "Couldn't access the mozContacts API");
+      next();
+    };
+  },
+
+  function checkNameIndex() {
+    info("Checking name index migration (upgrades 17 to 19).");
+    if (!allContacts) {
+      next();
+    }
+
+    var count = allContacts.length;
+
+    function finishRequest() {
+      count--;
+      if (!count) {
+        next();
+      }
+    }
+
+    allContacts.forEach(function(contact) {
+      var name = contact.name && contact.name[0];
+      if (!name) {
+        count--;
+        return;
+      }
+
+      var req = mozContacts.find({
+        filterBy: ["name"],
+        filterValue: name,
+        filterOp: "equals"
+      });
+
+      req.onsuccess = function onsuccess() {
+        if (this.result) {
+          info("Found contact '" + name + "', checking it's the correct one.");
+          checkContacts(this.result[0], contact);
+        } else {
+          ok(false, "Could not find contact with name '" + name + "'");
+        }
+
+        finishRequest();
+      };
+
+      req.onerror = function onerror() {
+        ok(false, "Error while finding contact with name '" + name + "'!");
+        finishRequest();
+      }
+    });
+
+    if (!count) {
+      ok(false, "No contact had a name, this is unexpected.");
+      next();
+    }
+  },
+
+  function checkSubstringMatching() {
+    var subject = "0004567890"; // the last 7 digits are the same that at least one contact
+    info("Looking for a contact matching " + subject);
+    var req = mozContacts.find({
+      filterValue: subject,
+      filterOp: "match",
+      filterBy: ["tel"],
+      filterLimit: 1
+    });
+
+    req.onsuccess = function onsuccess() {
+      if (this.result && this.result[0]) {
+        ok(true, "Found a contact with number " + this.result[0].tel[0].value);
+      }
+      next();
+    };
+
+    req.onerror = function onerror() {
+      ok(false, "Error while finding contact for substring matching check!");
+      next();
+    };
+  },
+
+  deleteDB,
+
+  function finish() {
+    backend.destroy();
+    info("all done!\n");
+    parent.SimpleTest.finish();
+  }
+];
+
+// this is the Mcc for Brazil, so that we trigger the previous pref
+SpecialPowers.pushPrefEnv({"set": [["dom.phonenumber.substringmatching.BR", 7],
+                                   ["ril.lastKnownSimMcc", "724"]]}, start_tests);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/file_permission_denied.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1081873
+-->
+<head>
+  <title>Test for Bug 1081873</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081873">Mozilla Bug 1081873</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+"use strict";
+
+parent.is("mozContacts" in navigator, false, "navigator.mozContacts must be inaccessible");
+parent.SimpleTest.finish();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/contacts/tests/mochitest.ini
+++ b/dom/contacts/tests/mochitest.ini
@@ -1,10 +1,24 @@
 [DEFAULT]
-support-files = shared.js
+support-files =
+  shared.js
+  file_contacts_basics.html
+  file_contacts_basics2.html
+  file_contacts_blobs.html
+  file_contacts_events.html
+  file_contacts_getall.html
+  file_contacts_getall2.html
+  file_contacts_international.html
+  file_contacts_substringmatching.html
+  file_contacts_substringmatchingVE.html
+  file_contacts_substringmatchingCL.html
+  test_migration_chrome.js
+  file_migration.html
+  file_permission_denied.html
 
 [test_contacts_basics.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_basics2.html]
 skip-if = (toolkit == 'gonk' && debug) || (os == 'win' && os_version == '5.1') #debug-only failure, bug 967258 on XP
 [test_contacts_blobs.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_events.html]
@@ -12,12 +26,11 @@ skip-if = (toolkit == 'gonk' && debug) #
 skip-if = (toolkit == 'gonk' && debug) || (toolkit == 'android' && processor == 'x86') #debug-only failure #x86 only
 [test_contacts_getall2.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_international.html]
 [test_contacts_substringmatching.html]
 [test_contacts_substringmatchingVE.html]
 [test_contacts_substringmatchingCL.html]
 [test_migration.html]
-  support-files =
-    test_migration_chrome.js
+  support-files +=
   skip-if = os == "android"
 [test_permission_denied.html]
--- a/dom/contacts/tests/shared.js
+++ b/dom/contacts/tests/shared.js
@@ -489,21 +489,15 @@ function next() {
 SimpleTest.waitForExplicitFinish();
 
 function start_tests() {
   // Skip tests on Android < 4.0 due to test failures on tbpl (see bugs 897924 & 888891)
   let androidVersion = SpecialPowers.Cc['@mozilla.org/system-info;1']
                                     .getService(SpecialPowers.Ci.nsIPropertyBag2)
                                     .getProperty('version');
   if (!isAndroid || androidVersion >= 14) {
-    SpecialPowers.pushPermissions([
-      {type: "contacts-write", allow: 1, context: document},
-      {type: "contacts-read", allow: 1, context: document},
-      {type: "contacts-create", allow: 1, context: document},
-    ], function() {
-      mozContacts = navigator.mozContacts;
-      next();
-    });
+    mozContacts = navigator.mozContacts;
+    next();
   } else {
     ok(true, "Skip tests on Android < 4.0 (bugs 897924 & 888891");
-    SimpleTest.finish();
+    parent.SimpleTest.finish();
   }
 }
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -1,784 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=674720
--->
 <head>
-  <title>Test for Bug 674720 WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674720">Mozilla Bug 674720</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
-
-var initialRev;
+<script type="application/javascript">
 
-function checkRevision(revision, msg, then) {
-  var revReq = mozContacts.getRevision();
-  revReq.onsuccess = function(e) {
-    is(e.target.result, initialRev+revision, msg);
-    then();
-  };
-  // The revision function isn't supported on Android so treat on failure as success
-  if (isAndroid) {
-    revReq.onerror = function(e) {
-      then();
-    };
-  } else {
-    revReq.onerror = onFailure;
-  }
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_basics.html";
 }
 
-var req;
-
-var steps = [
-  function() {
-    req = mozContacts.getRevision();
-    req.onsuccess = function(e) {
-      initialRev = e.target.result;
-      next();
-    };
-
-    // Android does not support the revision function. Treat errors as success.
-    if (isAndroid) {
-      req.onerror = function(e) {
-        initialRev = 0;
-        next();
-      };
-    } else {
-      req.onerror = onFailure;
-    }
-  },
-  function () {
-    ok(true, "Deleting database");
-    checkRevision(0, "Initial revision is 0", function() {
-      req = mozContacts.clear();
-      req.onsuccess = function () {
-        ok(true, "Deleted the database");
-        checkCount(0, "No contacts after clear", function() {
-          checkRevision(1, "Revision was incremented on clear", next);
-        });
-      };
-      req.onerror = onFailure;
-    });
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find(defaultOptions);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Empty database.");
-      checkRevision(1, "Revision was not incremented on find", next);
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding empty contact");
-    createResult1 = new mozContact({});
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      checkCount(1, "1 contact after adding empty contact", function() {
-        checkRevision(2, "Revision was incremented on save", next);
-      });
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find(defaultOptions);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "One contact.");
-      findResult1 = req.result[0];
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting empty contact");
-    req = navigator.mozContacts.remove(findResult1);
-    req.onsuccess = function () {
-      var req2 = mozContacts.find(defaultOptions);
-      req2.onsuccess = function () {
-        is(req2.result.length, 0, "Empty Database.");
-        clearTemps();
-        checkRevision(3, "Revision was incremented on remove", next);
-      }
-      req2.onerror = onFailure;
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact1");
-    createResult1 = new mozContact(properties1);
-
-    mozContacts.oncontactchange = function(event) {
-      is(event.contactID, createResult1.id, "Same contactID");
-      is(event.reason, "create", "Same reason");
-      next();
-    }
-
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      checkContacts(createResult1, properties1);
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 1");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[1].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      // Some manual testing. Testint the testfunctions
-      // tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}],
-      is(findResult1.tel[0].carrier, "testCarrier", "Same Carrier");
-      is(String(findResult1.tel[0].type), "work", "Same type");
-      is(findResult1.tel[0].value, "123456", "Same Value");
-      is(findResult1.tel[1].type[1], "fax", "Same type");
-      is(findResult1.tel[1].value, "+55 (31) 9876-3456", "Same Value");
-
-      is(findResult1.adr[0].countryName, "country 1", "Same country");
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
 
-      // email: [{type: ["work"], value: "x@y.com"}]
-      is(String(findResult1.email[0].type), "work", "Same Type");
-      is(findResult1.email[0].value, "x@y.com", "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for exact email");
-    var options = {filterBy: ["email"],
-                   filterOp: "equals",
-                   filterValue: properties1.email[0].value};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring and update");
-    mozContacts.oncontactchange = function(event) {
-       is(event.contactID, findResult1.id, "Same contactID");
-       is(event.reason, "update", "Same reason");
-     }
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      findResult1.jobTitle = ["new Job"];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact");
-    mozContacts.oncontactchange = function(event) {
-       is(event.contactID, createResult2.id, "Same contactID");
-       is(event.reason, "create", "Same reason");
-     }
-    createResult2 = new mozContact({name: ["newName"]});
-    req = navigator.mozContacts.save(createResult2);
-    req.onsuccess = function () {
-      ok(createResult2.id, "The contact now has an ID.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 2");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      checkContacts(createResult1, findResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Retrieving by name equality 1");
-    var options = {filterBy: ["name"],
-                   filterOp: "equals",
-                   filterValue: properties1.name[0]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      checkContacts(createResult1, findResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Retrieving by name equality 2");
-    var options = {filterBy: ["name"],
-                   filterOp: "equals",
-                   filterValue: properties1.name[1]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      checkContacts(createResult1, findResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Retrieving by name substring 1");
-    var options = {filterBy: ["name"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.name[0].substring(0,3).toLowerCase()};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      checkContacts(createResult1, findResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Retrieving by name substring 2");
-    var options = {filterBy: ["name"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.name[1].substring(0,3).toLowerCase()};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      checkContacts(createResult1, findResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Remove contact1");
-    mozContacts.oncontactchange = function(event) {
-      is(event.contactID, createResult1.id, "Same contactID");
-      is(event.reason, "remove", "Same reason");
-    }
-    req = navigator.mozContacts.remove(createResult1);
-    req.onsuccess = function () {
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 3");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[1].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found no contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Remove contact2");
-    mozContacts.oncontactchange = function(event) {
-      is(event.contactID, createResult2.id, "Same contactID");
-      is(event.reason, "remove", "Same reason");
-    }
-    req = navigator.mozContacts.remove(createResult2);
-    req.onsuccess = function () {
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 4");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[1].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found no contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    mozContacts.oncontactchange = function(event) {
-      is(event.contactID, "undefined", "Same contactID");
-      is(event.reason, "remove", "Same reason");
-    }
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact with properties1");
-    createResult1 = new mozContact(properties1);
-    mozContacts.oncontactchange = null;
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring tel1");
-    var options = {filterBy: ["tel"],
-                   filterOp: "contains",
-                   filterValue: properties1.tel[1].value.substring(2,5)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel exact");
-    var options = {filterBy: ["tel"],
-                   filterOp: "equals",
-                   filterValue: "+55 319 8 7 6 3456"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel exact with substring");
-    var options = {filterBy: ["tel"],
-                   filterOp: "equals",
-                   filterValue: "3456"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found no contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel exact with substring");
-    var options = {filterBy: ["tel"],
-                   filterOp: "equals",
-                   filterValue: "+55 (31)"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found no contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel match national number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "3198763456"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel match national format");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "0451 491934"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel match entered number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "123456"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by tel match international number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "+55 31 98763456"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by match with field other than tel");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "match",
-                   filterValue: "my friends call me 555-4040"};
-    req = mozContacts.find(options);
-    req.onsuccess = onUnwantedSuccess;
-    req.onerror = function() {
-      ok(true, "Failed");
-      next();
-    }
-  },
-  function () {
-    ok(true, "Retrieving by substring tel2");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: "9876"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring tel3");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: "98763456"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 5");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 6");
-    var options = {filterBy: ["familyName", "givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring3, Testing multi entry");
-    var options = {filterBy: ["givenName", "familyName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.familyName[1].substring(0,3).toLowerCase()};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find(defaultOptions);
-    req.onsuccess = function() {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(createResult1, findResult1);
-      if (!isAndroid) {
-        ok(findResult1.updated, "Has updated field");
-        ok(findResult1.published, "Has published field");
-      }
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Modifying contact1");
-    if (!findResult1) {
-      SpecialPowers.executeSoon(next);
-    } else {
-      findResult1.impp = properties1.impp = [{value:"phil impp"}];
-      req = navigator.mozContacts.save(findResult1);
-      req.onsuccess = function () {
-        var req2 = mozContacts.find(defaultOptions);
-        req2.onsuccess = function() {
-          is(req2.result.length, 1, "Found exactly 1 contact.");
-          findResult2 = req2.result[0];
-          ok(findResult2.id == sample_id1, "Same ID");
-          checkContacts(findResult2, properties1);
-          is(findResult2.impp.length, 1, "Found exactly 1 IMS info.");
-          next();
-        };
-        req2.onerror = onFailure;
-      };
-      req.onerror = onFailure;
-    }
-  },
-  function() {
-    // Android does not support published/updated fields. Skip this.
-    if (isAndroid) {
-      next();
-      return;
-    }
-
-    ok(true, "Saving old contact, should abort!");
-    req = mozContacts.save(createResult1);
-    req.onsuccess = onUnwantedSuccess;
-    req.onerror   = function() { ok(true, "Successfully declined updating old contact!"); next(); };
-  },
-  function () {
-    ok(true, "Retrieving a specific contact by ID");
-    var options = {filterBy: ["id"],
-                   filterOp: "equals",
-                   filterValue: sample_id1};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving a specific contact by givenName");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "equals",
-                   filterValue: properties1.givenName[0]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, properties1);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Modifying contact2");
-    if (!findResult1) {
-      SpecialPowers.executeSoon(next);
-    } else {
-      findResult1.impp = properties1.impp = [{value: "phil impp"}];
-      req = mozContacts.save(findResult1);
-      req.onsuccess = function () {
-        var req2 = mozContacts.find(defaultOptions);
-        req2.onsuccess = function () {
-          is(req2.result.length, 1, "Found exactly 1 contact.");
-          findResult1 = req2.result[0];
-          ok(findResult1.id == sample_id1, "Same ID");
-          checkContacts(findResult1, properties1);
-          is(findResult1.impp.length, 1, "Found exactly 1 IMS info.");
-          next();
-        }
-        req2.onerror = onFailure;
-      };
-      req.onerror = onFailure;
-    }
-  },
-  function () {
-    ok(true, "Searching contacts by query");
-    var options = {filterBy: ["givenName", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0,4)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts by query");
-    var options = {filterBy: ["givenName", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts with multiple indices");
-    var options = {filterBy: ["email", "givenName"],
-                   filterOp: "equals",
-                   filterValue: properties1.givenName[1]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, properties1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Modifying contact3");
-    if (!findResult1) {
-      SpecialPowers.executeSoon(next);
-    } else {
-      findResult1.email = [{value: properties1.nickname}];
-      findResult1.nickname = ["TEST"];
-      var newContact = new mozContact(findResult1);
-      req = mozContacts.save(newContact);
-      req.onsuccess = function () {
-        var options = {filterBy: ["email", "givenName"],
-                       filterOp: "startsWith",
-                       filterValue: properties1.givenName[0]};
-        // One contact has it in nickname and the other in email
-        var req2 = mozContacts.find(options);
-        req2.onsuccess = function () {
-          is(req2.result.length, 2, "Found exactly 2 contacts.");
-          ok(req2.result[0].id != req2.result[1].id, "Different ID");
-          next();
-        }
-        req2.onerror = onFailure;
-      };
-      req.onerror = onFailure;
-    }
-  },
-  function () {
-    ok(true, "Deleting contact" + findResult1);
-    req = mozContacts.remove(findResult1);
-    req.onsuccess = function () {
-      var req2 = mozContacts.find(defaultOptions);
-      req2.onsuccess = function () {
-        is(req2.result.length, 1, "One contact left.");
-        findResult1 = req2.result[0];
-        next();
-      }
-      req2.onerror = onFailure;
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.remove(findResult1);
-    req.onsuccess =  function () {
-      clearTemps();
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Test JSON.stringify output for mozContact objects");
-    var json = JSON.parse(JSON.stringify(new mozContact(properties1)));
-    checkContacts(json, properties1);
-    next();
-  },
-  function() {
-    ok(true, "Test slice");
-    var c = new mozContact();
-    c.email = [{ type: ["foo"], value: "bar@baz" }]
-    var arr = c.email;
-    is(arr[0].value, "bar@baz", "Should have the right value");
-    arr = arr.slice();
-    is(arr[0].value, "bar@baz", "Should have the right value after slicing");
-    next();
-  },
-  function () {
-    ok(true, "all done!\n");
-    clearTemps();
-
-    SimpleTest.finish();
-  }
-];
-
-start_tests();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_basics2.html
+++ b/dom/contacts/tests/test_contacts_basics2.html
@@ -1,1149 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=674720
--->
 <head>
-  <title>Test for Bug 674720 WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674720">Mozilla Bug 674720</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
-
-var req;
-
-var steps = [
-  function () {
-    ok(true, "Adding a new contact");
-    createResult1 = new mozContact(properties1);
-    req = mozContacts.save(createResult1)
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact2");
-    createResult2 = new mozContact(properties2);
-    req = mozContacts.save(createResult2);
-    req.onsuccess = function () {
-      ok(createResult2.id, "The contact now has an ID.");
-      sample_id2 = createResult2.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({sortBy: "familyName"});
-    req.onsuccess = function () {
-      is(req.result.length, 2, "Found exactly 2 contact.");
-      checkContacts(req.result[1], properties1);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    console.log("Searching contacts by query1");
-    var options = {filterBy: ["givenName", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0, 4)}
-    req = mozContacts.find(options)
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, createResult1);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts by query2");
-    var options = {filterBy: ["givenName", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties2.givenName[0].substring(0, 4)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.adr.length, 2, "Adr length 2");
-      checkContacts(findResult1, createResult2);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts by tel");
-    var options = {filterBy: ["tel"],
-                   filterOp: "contains",
-                   filterValue: properties2.tel[0].value.substring(3, 7)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id2, "Same ID");
-      checkContacts(findResult1, createResult2);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts by email");
-    var options = {filterBy: ["email"],
-                   filterOp: "startsWith",
-                   filterValue: properties2.email[0].value.substring(0, 4)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id2, "Same ID");
-      checkContacts(findResult1, createResult2);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding 20 contacts");
-    for (var i=0; i<19; i++) {
-      createResult1 = new mozContact(properties1);
-      req = mozContacts.save(createResult1);
-      req.onsuccess = function () {
-        ok(createResult1.id, "The contact now has an ID.");
-      };
-      req.onerror = onFailure;
-    };
-    createResult1 = new mozContact(properties1);
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkStrArray(createResult1.name, properties1.name, "Same Name");
-      checkCount(20, "20 contacts in DB", next);
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find(defaultOptions);
-    req.onsuccess = function () {
-      is(req.result.length, 20, "20 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts with limit 10");
-    var options = { filterLimit: 10 };
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 10, "10 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts with limit 10 and sorted");
-    var options = { filterLimit: 10,
-                    sortBy: 'FamilyName',
-                    sortOrder: 'descending' };
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 10, "10 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts2");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0, 4)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 20, "20 Entries.");
-      checkContacts(createResult1, req.result[19]);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts3");
-    var options = {filterBy: ["givenName", "tel", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0, 4)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 20, "20 Entries.");
-      checkContacts(createResult1, req.result[10]);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Testing clone contact");
-    createResult1 = new mozContact(properties1);
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkStrArray(createResult1.name, properties1.name, "Same Name");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Testing clone contact2");
-    var cloned = new mozContact(createResult1);
-    ok(cloned.id != createResult1.id, "Cloned contact has new ID");
-    cloned.email = [{value: "new email!"}];
-    cloned.givenName = ["Tom"];
-    req = mozContacts.save(cloned);
-    req.onsuccess = function () {
-      ok(cloned.id, "The contact now has an ID.");
-      is(cloned.email[0].value, "new email!", "Same Email");
-      isnot(createResult1.email[0].value, cloned.email[0].value, "Clone has different email");
-      is(String(cloned.givenName), "Tom", "New Name");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties2.givenName[0].substring(0, 4)};
-    req = mozContacts.find(defaultOptions);
-    req.onsuccess = function () {
-      is(req.result.length, 2, "2 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Search with redundant fields should only return 1 contact");
-    createResult1 = new mozContact({name: ["XXX"],
-                                    givenName: ["XXX"],
-                                    email: [{value: "XXX"}],
-                                    tel: [{value: "XXX"}]
-                                   });
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function() {
-      var options = {filterBy: ["givenName", "familyName"],
-                     filterOp: "equals",
-                     filterValue: "XXX"};
-      var req2 = mozContacts.find(options);
-      req2.onsuccess = function() {
-        is(req2.result.length, 1, "1 Entry");
-        next();
-      }
-      req2.onerror = onFailure;
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c3);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c3, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c2);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c2, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c4);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c4, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c1);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c1, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    var options = {sortBy: "familyName",
-                   sortOrder: "ascending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 4, "4 results");
-      checkContacts(req.result[0], c1);
-      checkContacts(req.result[1], c2);
-      checkContacts(req.result[2], c3);
-      checkContacts(req.result[3], c4);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    var options = {sortBy: "familyName",
-                   sortOrder: "descending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 4, "4 results");
-      checkContacts(req.result[0], c4);
-      checkContacts(req.result[1], c3);
-      checkContacts(req.result[2], c2);
-      checkContacts(req.result[3], c1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c5);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c5, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting with empty string");
-    var options = {sortBy: "familyName",
-                   sortOrder: "ascending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 5, "5 results");
-      checkContacts(req.result[0], c5);
-      checkContacts(req.result[1], c1);
-      checkContacts(req.result[2], c2);
-      checkContacts(req.result[3], c3);
-      checkContacts(req.result[4], c4);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Don't allow to add custom fields");
-    createResult1 = new mozContact({givenName: ["customTest"], yyy: "XXX"});
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function() {
-      var options = {filterBy: ["givenName"],
-                     filterOp: "equals",
-                     filterValue: "customTest"};
-      var req2 = mozContacts.find(options);
-      req2.onsuccess = function() {
-        is(req2.result.length, 1, "1 Entry");
-        checkStrArray(req2.result[0].givenName, ["customTest"], "same name");
-        ok(req2.result.yyy === undefined, "custom property undefined");
-        next();
-      }
-      req2.onerror = onFailure;
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c7);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c7, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c6);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c6, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c8);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c8, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    // Android does not support published/updated fields. Skip this.
-    if (isAndroid) {
-      next();
-      return;
-    }
+<script type="application/javascript">
 
-    ok(true, "Test sorting with published");
-    var options = {sortBy: "familyName",
-                   sortOrder: "descending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 3, "3 results");
-      ok(req.result[0].published < req.result[1].published, "Right sorting order");
-      ok(req.result[1].published < req.result[2].published, "Right sorting order");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact with properties2");
-    createResult2 = new mozContact(properties2);
-    req = mozContacts.save(createResult2);
-    req.onsuccess = function () {
-      ok(createResult2.id, "The contact now has an ID.");
-      sample_id2 = createResult2.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test category search with startsWith");
-    var options = {filterBy: ["category"],
-                   filterOp: "startsWith",
-                   filterValue: properties2.category[0]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "1 Entry.");
-      checkContacts(req.result[0], createResult2);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test category search with equals");
-    var options = {filterBy: ["category"],
-                   filterOp: "equals",
-                   filterValue: properties2.category[0]};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "1 Entry.");
-      checkContacts(req.result[0], createResult2);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact for category search");
-    createResult1 = new mozContact({name: ["5"], givenName: ["5"]});
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test category search with equals");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: "5"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "1 Entry.");
-      checkContacts(req.result[0], createResult1);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact with invalid data");
-    var obj = {
-        honorificPrefix: [],
-        honorificSuffix: [{foo: "bar"}],
-        sex: 17,
-        genderIdentity: 18,
-        email: [{type: ["foo"], value: "bar"}]
-    };
-    obj.honorificPrefix.__defineGetter__('0',(function() {
-      var c = 0;
-      return function() {
-        if (c == 0) {
-          c++;
-          return "string";
-        } else {
-          return {foo:"bar"};
-        }
-      }
-    })());
-    createResult1 = new mozContact(obj);
-    createResult1.email.push({aeiou: "abcde"});
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      checkContacts(createResult1, {
-        honorificPrefix: ["string"],
-        honorificSuffix: ["[object Object]"],
-        sex: "17",
-        genderIdentity: "18",
-        email: [{type: ["foo"], value: "bar"}, {}]
-      });
-      next();
-    };
-  },
-  function () {
-    ok(true, "Adding contact with no number but carrier");
-    createResult1 = new mozContact({ tel: [{type: ["home"], carrier: "myCarrier"} ] });
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact with email but no value");
-    createResult1 = new mozContact({ email: [{type: ["home"]}] });
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Testing numbersOnly search 1");
-    createResult1 = new mozContact({ name: ["aaaaaaaaa"], givenName: ["aaaaaaaaa"], tel: [{ value: "1234567890"}]});
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test numbersOnly search 2");
-    var options = {filterBy: ["givenName", "tel"],
-                   filterOp: "contains",
-                   filterValue: "a"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 1, "1 Entry.");
-      checkContacts(req.result[0], createResult1);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test numbersOnly search 3");
-    var options = {filterBy: ["givenName", "tel"],
-                   filterOp: "contains",
-                   filterValue: "b"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 0, "0 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test numbersOnly search 4");
-    var options = {filterBy: ["givenName", "tel"],
-                   filterOp: "contains",
-                   filterValue: "1a"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 0, "0 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test numbersOnly search 5");
-    var options = {filterBy: ["givenName", "tel"],
-                   filterOp: "contains",
-                   filterValue: "1(23)"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 1, "1 Entry.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test numbersOnly search 6");
-    var options = {filterBy: ["givenName", "tel"],
-                   filterOp: "contains",
-                   filterValue: "1(23)a"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 0, "0 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Test that after setting array properties to scalar values the property os not a non-array")
-    const FIELDS = ["email","url","adr","tel","impp"];
-    createResult1 = new mozContact();
-    for (var prop of FIELDS) {
-      try {
-        createResult1[prop] = {type: ["foo"]};
-      } catch (e) {}
-      ok(createResult1[prop] === null ||
-         Array.isArray(createResult1[prop]), prop + " is array");
-    }
-    next();
-  },
-  function() {
-    ok(true, "Undefined properties of fields should be treated correctly");
-    var c = new mozContact({
-      adr: [{streetAddress: undefined}],
-      email: [{value: undefined}],
-      url: [{value: undefined}],
-      impp: [{value: undefined}],
-      tel: [{value: undefined}],
-    });
-    is(c.adr[0].streetAddress, undefined, "adr.streetAddress is undefined");
-    is(c.adr[0].locality, undefined, "adr.locality is undefined");
-    is(c.adr[0].pref, undefined, "adr.pref is undefined");
-    is(c.email[0].value, undefined, "email.value is undefined");
-    is(c.url[0].value, undefined, "url.value is undefined");
-    is(c.impp[0].value, undefined, "impp.value is undefined");
-    is(c.tel[0].value, undefined, "tel.value is undefined");
-    next();
-  },
-  function() {
-    ok(true, "Setting array properties to an empty array should work");
-    var c = new mozContact();
-    function testArrayProp(prop) {
-      is(c[prop], null, "property is initially null");
-      c[prop] = [];
-      ok(Array.isArray(c[prop]), "property is an array after setting");
-      is(c[prop].length, 0, "property has length 0 after setting");
-    }
-    testArrayProp("email");
-    testArrayProp("adr");
-    testArrayProp("tel");
-    testArrayProp("impp");
-    testArrayProp("url");
-    next();
-  },
-  function() {
-    ok(true, "Passing a mozContact with invalid data to save() should throw");
-    var c = new mozContact({
-      photo: [],
-      tel: []
-    });
-    c.photo.push({});
-    SimpleTest.doesThrow(()=>navigator.mozContacts.save(c), "Invalid data in Blob array");
-    c.tel.push(123);
-    SimpleTest.doesThrow(()=>navigator.mozContacts.save(c), "Invalid data in dictionary array");
-    next();
-  },
-  function() {
-    ok(true, "Inline changes to array properties should be seen by save");
-    var c = new mozContact({
-      name: [],
-      familyName: [],
-      givenName: [],
-      phoneticFamilyName: [],
-      phoneticGivenName: [],
-      nickname: [],
-      tel: [],
-      adr: [],
-      email: []
-    });
-    for (var prop of Object.getOwnPropertyNames(properties1)) {
-      if (!Array.isArray(properties1[prop])) {
-        continue;
-      }
-      for (var i = 0; i < properties1[prop].length; ++i) {
-        c[prop].push(properties1[prop][i]);
-      }
-    }
-    req = navigator.mozContacts.save(c);
-    req.onsuccess = function() {
-      req = navigator.mozContacts.find(defaultOptions);
-      req.onsuccess = function() {
-        is(req.result.length, 1, "Got 1 contact");
-        checkContacts(req.result[0], properties1);
-        next();
-      };
-      req.onerror = onFailure;
-    };
-    req.onerror = onFailure;
-  },
-  clearDatabase,
-  function() {
-    ok(true, "mozContact.init deprecation message");
-    var c = new mozContact();
-    SimpleTest.monitorConsole(next, [
-      { errorMessage: "mozContact.init is DEPRECATED. Use the mozContact constructor instead. " +
-                      "See https://developer.mozilla.org/docs/WebAPI/Contacts for details." }
-    ], /* forbidUnexpectedMsgs */ true);
-    c.init({name: ["Bar"]});
-    c.init({name: ["Bar"]});
-    SimpleTest.endMonitorConsole();
-  },
-  function() {
-    ok(true, "mozContact.init works as expected");
-    var c = new mozContact({name: ["Foo"]});
-    c.init({name: ["Bar"]});
-    is(c.name[0], "Bar", "Same name");
-    next();
-  },
-  function() {
-    ok(true, "mozContact.init without parameters");
-    var c = new mozContact({name: ["Foo"]});
-    c.init();
-    next();
-  },
-  function() {
-    ok(true, "mozContact.init resets properties");
-    var c = new mozContact({jobTitle: ["Software Engineer"]});
-    c.init({nickname: ["Jobless Johnny"]});
-    is(c.nickname[0], "Jobless Johnny", "Same nickname");
-    ok(!c.jobTitle, "jobTitle is not set");
-    next();
-  },
-  function() {
-    ok(true, "mozContacts.remove with an ID works");
-    var c = new mozContact({name: ["Ephemeral Jimmy"]});
-    req = navigator.mozContacts.save(c);
-    req.onsuccess = function() {
-      req = navigator.mozContacts.remove(c.id);
-      req.onsuccess = function() {
-        req = navigator.mozContacts.find({
-          filterBy: ["id"],
-          filterOp: "equals",
-          filterValue: c.id
-        });
-        req.onsuccess = function() {
-          is(req.result.length, 0, "Successfully removed contact by ID");
-          next();
-        };
-        req.onerror = onFailure;
-      };
-      req.onerror = onFailure;
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact");
-    createResult1 = new mozContact(properties3);
-    req = mozContacts.save(createResult1)
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact2");
-    createResult2 = new mozContact(properties4);
-    req = mozContacts.save(createResult2);
-    req.onsuccess = function () {
-      ok(createResult2.id, "The contact now has an ID.");
-      sample_id2 = createResult2.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({sortBy: "phoneticFamilyName"});
-    req.onsuccess = function () {
-      is(req.result.length, 2, "Found exactly 2 contact.");
-      checkContacts(req.result[1], properties3);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts by query1");
-    var options = {filterBy: ["phoneticGivenName", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)}
-    req = mozContacts.find(options)
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      checkContacts(findResult1, createResult1);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching contacts by query2");
-    var options = {filterBy: ["phoneticGivenName", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties4.phoneticGivenName[0].substring(0, 3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.adr.length, 2, "Adr length 2");
-      checkContacts(findResult1, createResult2);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  clearDatabase,
-  function () {
-    ok(true, "Adding 20 contacts");
-    for (var i=0; i<19; i++) {
-      createResult1 = new mozContact(properties3);
-      req = mozContacts.save(createResult1);
-      req.onsuccess = function () {
-        ok(createResult1.id, "The contact now has an ID.");
-      };
-      req.onerror = onFailure;
-    };
-    createResult1 = new mozContact(properties3);
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkStrArray(createResult1.name, properties3.name, "Same Name");
-      checkCount(20, "20 contacts in DB", next);
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find(defaultOptions);
-    req.onsuccess = function () {
-      is(req.result.length, 20, "20 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts2");
-    var options = {filterBy: ["phoneticGivenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 20, "20 Entries.");
-      checkContacts(createResult1, req.result[19]);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts3");
-    var options = {filterBy: ["phoneticGivenName", "tel", "email"],
-                   filterOp: "startsWith",
-                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 20, "20 Entries.");
-      checkContacts(createResult1, req.result[10]);
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  clearDatabase,
-  function () {
-    ok(true, "Testing clone contact");
-    createResult1 = new mozContact(properties3);
-    req = mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkStrArray(createResult1.phoneticFamilyName, properties3.phoneticFamilyName, "Same phoneticFamilyName");
-      checkStrArray(createResult1.phoneticGivenName, properties3.phoneticGivenName, "Same phoneticGivenName");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({sortBy: "phoneticGivenName"});
-    req.onsuccess = function () {
-      is(req.result.length, 1, "1 Entries.");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  clearDatabase,
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c11);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c11, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c10);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c10, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c12);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c12, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c9);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c9, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    var options = {sortBy: "phoneticFamilyName",
-                   sortOrder: "ascending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 4, "4 results");
-      checkContacts(req.result[0], c9);
-      checkContacts(req.result[1], c10);
-      checkContacts(req.result[2], c11);
-      checkContacts(req.result[3], c12);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    var options = {sortBy: "phoneticFamilyName",
-                   sortOrder: "descending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 4, "4 results");
-      checkContacts(req.result[0], c12);
-      checkContacts(req.result[1], c11);
-      checkContacts(req.result[2], c10);
-      checkContacts(req.result[3], c9);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c13);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c13, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting with empty string");
-    var options = {sortBy: "phoneticFamilyName",
-                   sortOrder: "ascending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 5, "5 results");
-      checkContacts(req.result[0], c13);
-      checkContacts(req.result[1], c9);
-      checkContacts(req.result[2], c10);
-      checkContacts(req.result[3], c11);
-      checkContacts(req.result[4], c12);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  clearDatabase,
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c15);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c15, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c14);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c14, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Test sorting");
-    createResult1 = new mozContact(c16);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      checkContacts(c16, createResult1);
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    // Android does not support published/updated fields. Skip this.
-    if (isAndroid) {
-      next();
-      return;
-    }
-
-    ok(true, "Test sorting with published");
-    var options = {sortBy: "phoneticFamilyName",
-                   sortOrder: "descending"};
-    req = navigator.mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 3, "3 results");
-      ok(req.result[0].published < req.result[1].published, "Right sorting order");
-      ok(req.result[1].published < req.result[2].published, "Right sorting order");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  clearDatabase,
-  function () {
-    ok(true, "all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-function next() {
-  ok(true, "Begin!");
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    var i = index++;
-    steps[i]();
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_basics2.html";
 }
 
-start_tests();
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_blobs.html
+++ b/dom/contacts/tests/test_contacts_blobs.html
@@ -1,222 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=674720
--->
 <head>
-  <title>Test for Bug 674720 WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674720">Mozilla Bug 674720</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
-
-var utils = SpecialPowers.getDOMWindowUtils(window);
+<script type="application/javascript">
 
-function getView(size)
-{
- var buffer = new ArrayBuffer(size);
- var view = new Uint8Array(buffer);
- is(buffer.byteLength, size, "Correct byte length");
- return view;
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_blobs.html";
 }
 
-function getRandomView(size)
-{
- var view = getView(size);
- for (var i = 0; i < size; i++) {
-   view[i] = parseInt(Math.random() * 255)
- }
- return view;
-}
-
-function getRandomBlob(size)
-{
-  return new Blob([getRandomView(size)], { type: "binary/random" });
-}
-
-function compareBuffers(buffer1, buffer2)
-{
-  if (buffer1.byteLength != buffer2.byteLength) {
-    return false;
-  }
-  var view1 = new Uint8Array(buffer1);
-  var view2 = new Uint8Array(buffer2);
-  for (var i = 0; i < buffer1.byteLength; i++) {
-    if (view1[i] != view2[i]) {
-      return false;
-    }
-  }
-  return true;
-}
-
-function verifyBuffers(buffer1, buffer2, isLast)
-{
-  ok(compareBuffers(buffer1, buffer2), "Correct blob data");
-  if (isLast)
-    next();
-}
-
-var randomBlob = getRandomBlob(1024);
-var randomBlob2 = getRandomBlob(1024);
-
-var properties1 = {
-  name: ["xTestname1"],
-  givenName: ["xTestname1"],
-  photo: [randomBlob]
-};
-
-var properties2 = {
-  name: ["yTestname2"],
-  givenName: ["yTestname2"],
-  photo: [randomBlob, randomBlob2]
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
 };
 
-var sample_id1;
-var createResult1;
-var findResult1;
-
-function verifyBlob(blob1, blob2, isLast)
-{
-  is(blob1 instanceof Blob, true,
-     "blob1 is an instance of DOMBlob");
-  is(blob2 instanceof Blob, true,
-     "blob2 is an instance of DOMBlob");
-  isnot(blob1 instanceof File, true,
-     "blob1 is an instance of File");
-  isnot(blob2 instanceof File, true,
-     "blob2 is an instance of File");
-  is(blob1.size, blob2.size, "Same size");
-  is(blob1.type, blob2.type, "Same type");
-
-  var buffer1;
-  var buffer2;
-
-  var reader1 = new FileReader();
-  reader1.readAsArrayBuffer(blob2);
-  reader1.onload = function(event) {
-    buffer2 = event.target.result;
-    if (buffer1) {
-      verifyBuffers(buffer1, buffer2, isLast);
-    }
-  }
-
-  var reader2 = new FileReader();
-  reader2.readAsArrayBuffer(blob1);
-  reader2.onload = function(event) {
-    buffer1 = event.target.result;
-    if (buffer2) {
-      verifyBuffers(buffer1, buffer2, isLast);
-    }
-  }
-}
-
-function verifyBlobArray(blobs1, blobs2)
-{
-  is(blobs1 instanceof Array, true, "blobs1 is an array object");
-  is(blobs2 instanceof Array, true, "blobs2 is an array object");
-  is(blobs1.length, blobs2.length, "Same length");
-
-  if (!blobs1.length) {
-    next();
-    return;
-  }
-
-  for (var i = 0; i < blobs1.length; i++) {
-    verifyBlob(blobs1[i], blobs2[i], i == blobs1.length - 1);
-  }
-}
-
-var req;
-
-var steps = [
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact with photo");
-    createResult1 = new mozContact(properties1);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties1.givenName[0].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      verifyBlobArray(findResult1.photo, properties1.photo);
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact with 2 photos");
-    createResult1 = new mozContact(properties2);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring");
-    var options = {filterBy: ["givenName"],
-                   filterOp: "startsWith",
-                   filterValue: properties2.givenName[0].substring(0,3)};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      verifyBlobArray(findResult1.photo, properties2.photo);
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "all done!\n");
-
-    SimpleTest.finish();
-  }
-];
-
-start_tests();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_events.html
+++ b/dom/contacts/tests/test_contacts_events.html
@@ -1,41 +1,29 @@
 <!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=764667
--->
 <head>
-  <title>Test for Bug 678695</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=764667">Mozilla Bug 764667</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-  
-</div>
+<iframe></iframe>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 764667 **/
-
-SpecialPowers.addPermission("contacts-read", true, document);
-
-var e = new MozContactChangeEvent("contactchanged", {contactID: "123", reason: "create"});
-ok(e, "Should have contactsChange event!");
-is(e.contactID, "123", "ID should be 123.");
-is(e.reason, "create", "Reason should be create.");
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_events.html";
+}
 
-e = new MozContactChangeEvent("contactchanged", {contactID: "test", reason: "test"});
-is(e.contactID, "test", "Name should be 'test'.");
-is(e.reason, "test", "Name should be 'test'.");
-
-e = new MozContactChangeEvent("contactchanged", {contactID: "a", reason: ""});
-is(e.contactID, "a", "Name should be a.");
-is(e.reason, "", "Value should be empty");
-
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
 
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/contacts/tests/test_contacts_getall.html
+++ b/dom/contacts/tests/test_contacts_getall.html
@@ -1,152 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=836519
--->
 <head>
-  <title>Mozilla Bug 836519</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836519">Mozilla Bug 836519</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript;version=1.8">
-"use strict";
-
-let req;
-
-let steps = [
-  function start() {
-    SpecialPowers.Cc["@mozilla.org/tools/profiler;1"].getService(SpecialPowers.Ci.nsIProfiler).AddMarker("GETALL_START");
-    next();
-  },
-  clearDatabase,
-  addContacts,
+<script type="application/javascript">
 
-  function() {
-    ok(true, "Delete the current contact while iterating");
-    req = mozContacts.getAll({});
-    let count = 0;
-    let previousId = null;
-    req.onsuccess = function() {
-      if (req.result) {
-        ok(true, "on success");
-        if (previousId) {
-          isnot(previousId, req.result.id, "different contacts returned");
-        }
-        previousId = req.result.id;
-        count++;
-        let delReq = mozContacts.remove(req.result);
-        delReq.onsuccess = function() {
-          ok(true, "deleted current contact");
-          req.continue();
-        };
-      } else {
-        is(count, 40, "returned 40 contacts");
-        next();
-      }
-    };
-  },
-
-  clearDatabase,
-  addContacts,
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_getall.html";
+}
 
-  function() {
-    ok(true, "Iterating through the contact list inside a cursor callback");
-    let count1 = 0, count2 = 0;
-    let req1 = mozContacts.getAll({});
-    let req2;
-    req1.onsuccess = function() {
-      if (count1 == 0) {
-        count1++;
-        req2 = mozContacts.getAll({});
-        req2.onsuccess = function() {
-          if (req2.result) {
-            count2++;
-            req2.continue();
-          } else {
-            is(count2, 40, "inner cursor returned 40 contacts");
-            req1.continue();
-          }
-        };
-      } else {
-        if (req1.result) {
-          count1++;
-          req1.continue();
-        } else {
-          is(count1, 40, "outer cursor returned 40 contacts");
-          next();
-        }
-      }
-    };
-  },
-
-  clearDatabase,
-  addContacts,
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
 
-  function() {
-    ok(true, "20 concurrent cursors");
-    const NUM_CURSORS = 20;
-    let completed = 0;
-    for (let i = 0; i < NUM_CURSORS; ++i) {
-      mozContacts.getAll({}).onsuccess = (function(i) {
-        let count = 0;
-        return function(event) {
-          let req = event.target;
-          if (req.result) {
-            count++;
-            req.continue();
-          } else {
-            is(count, 40, "cursor " + i + " returned 40 contacts");
-            if (++completed == NUM_CURSORS) {
-              next();
-            }
-          }
-        };
-      })(i);
-    }
-  },
-
-  clearDatabase,
-  addContacts,
-
-  function() {
-    if (!SpecialPowers.isMainProcess()) {
-      // We stop calling continue() intentionally here to see if the cursor gets
-      // cleaned up properly in the parent.
-      ok(true, "Leaking a cursor");
-      req = mozContacts.getAll({
-        sortBy: "familyName",
-        sortOrder: "ascending"
-      });
-      req.onsuccess = function(event) {
-        next();
-      };
-      req.onerror = onFailure;
-    } else {
-      next();
-    }
-  },
-
-  clearDatabase,
-
-  function() {
-    ok(true, "all done!\n");
-    SpecialPowers.Cc["@mozilla.org/tools/profiler;1"].getService(SpecialPowers.Ci.nsIProfiler).AddMarker("GETALL_END");
-    SimpleTest.finish();
-  }
-];
-
-start_tests();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_getall2.html
+++ b/dom/contacts/tests/test_contacts_getall2.html
@@ -1,120 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=836519
--->
 <head>
-  <title>Mozilla Bug 836519</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836519">Mozilla Bug 836519</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript;version=1.8">
-"use strict";
-let req;
+<script type="application/javascript">
 
-let steps = [
-  clearDatabase,
-  function() {
-    // add a contact
-    createResult1 = new mozContact({});
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function() {
-      next();
-    };
-    req.onerror = onFailure;
-  },
-
-  getOne(),
-  getOne("Retrieving one contact with getAll - cached"),
-
-  clearDatabase,
-  addContacts,
-
-  getAll(),
-  getAll("Retrieving 40 contacts with getAll - cached"),
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_getall2.html";
+}
 
-  function() {
-    ok(true, "Deleting one contact");
-    req = mozContacts.remove(createResult1);
-    req.onsuccess = function() {
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Test cache invalidation");
-    req = mozContacts.getAll({});
-    let count = 0;
-    req.onsuccess = function(event) {
-      ok(true, "on success");
-      if (req.result) {
-        ok(true, "result is valid");
-        count++;
-        req.continue();
-      } else {
-        is(count, 39, "last contact - 39 contacts returned");
-        next();
-      }
-    };
-    req.onerror = onFailure;
-  },
-
-  clearDatabase,
-  addContacts,
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
 
-  function() {
-    ok(true, "Test cache consistency when deleting contact during getAll");
-    req = mozContacts.find({});
-    req.onsuccess = function(e) {
-      let lastContact = e.target.result[e.target.result.length-1];
-      req = mozContacts.getAll({});
-      let count = 0;
-      let firstResult = true;
-      req.onsuccess = function(event) {
-        ok(true, "on success");
-        if (firstResult) {
-          if (req.result) {
-            count++;
-          }
-          let delReq = mozContacts.remove(lastContact);
-          delReq.onsuccess = function() {
-            firstResult = false;
-            req.continue();
-          };
-        } else {
-          if (req.result) {
-            ok(true, "result is valid");
-            count++;
-            req.continue();
-          } else {
-            is(count, 40, "last contact - 40 contacts returned");
-            next();
-          }
-        }
-      };
-    };
-  },
-
-  clearDatabase,
-
-  function() {
-    ok(true, "all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-start_tests();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_international.html
+++ b/dom/contacts/tests/test_contacts_international.html
@@ -1,274 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=815833
--->
 <head>
-  <title>Test for Bug 815833 WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=815833">Mozilla Bug 815833</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
-
-var number1 = {
-  local: "7932012345",
-  international: "+557932012345"
-};
+<script type="application/javascript">
 
-var number2 = {
-  local: "7932012346",
-  international: "+557932012346"
-};
-
-var properties1 = {
-  name: ["Testname1"],
-  tel: [{type: ["work"], value: number1.local, carrier: "testCarrier"} , {type: ["home", "fax"], value: number2.local}],
-};
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_international.html";
+}
 
-var shortNumber = "888";
-var properties2 = {
-  name: ["Testname2"],
-  tel: [{type: ["work"], value: shortNumber, carrier: "testCarrier"}]
-};
-
-var number3 = {
-  local: "7932012345",
-  international: "+557932012345"
-};
-
-var properties3 = {
-  name: ["Testname2"],
-  tel: [{value: number3.international}]
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
 };
 
-var req;
-var createResult1;
-var findResult1;
-var sample_id1;
-
-var steps = [
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact1");
-    createResult1 = new mozContact(properties1);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact2");
-    var createResult2 = new mozContact(properties2);
-    req = navigator.mozContacts.save(createResult2);
-    req.onsuccess = function () {
-      ok(createResult2.id, "The contact now has an ID.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for local number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: number1.local};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for international number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: number1.international};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found exactly 0 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for a short number matching the prefix");
-    var shortNumber = number1.local.substring(0, 3);
-    var options = {filterBy: ["tel"],
-                   filterOp: "equals",
-                   filterValue: shortNumber};
-    req = mozContacts.find(options);
-    req.onsuccess = function() {
-      is(req.result.length, 0, "The prefix short number should not match any contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for a short number matching the suffix");
-    var shortNumber = number1.local.substring(number1.local.length - 3);
-    var options = {filterBy: ["tel"],
-                   filterOp: "equals",
-                   filterValue: shortNumber};
-    req = mozContacts.find(options);
-    req.onsuccess = function() {
-      is(req.result.length, 0, "The suffix short number should not match any contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for a short number matching a contact");
-    var options = {filterBy: ["tel"],
-                   filterOp: "equals",
-                   filterValue: shortNumber};
-    req = mozContacts.find(options);
-    req.onsuccess = function() {
-      is(req.result.length, 1, "Found the contact equally matching the shortNumber.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function() {
-    ok(true, "Modifying number");
-    if (!findResult1) {
-      SpecialPowers.executeSoon(next);
-    } else {
-      findResult1.tel[0].value = number2.local;
-      req = mozContacts.save(findResult1);
-      req.onsuccess = function () {
-        next();
-      };
-    }
-  },
-  function () {
-    ok(true, "Searching for local number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: number1.local};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found exactly 0 contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for local number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: number1.international};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found exactly 0 contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for local number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: number2.local};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for local number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "startsWith",
-                   filterValue: number2.international};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found exactly 1 contact.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a contact with a Brazilian country code");
-    createResult1 = new mozContact(properties3);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for Brazilian number using local number");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: number3.local};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear();
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-SpecialPowers.pushPrefEnv({
-  set: [
-    ["ril.lastKnownSimMcc", "000"]
-  ]
-}, start_tests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_substringmatching.html
+++ b/dom/contacts/tests/test_contacts_substringmatching.html
@@ -1,348 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=877302
--->
 <head>
-  <title>Test for Bug 877302 substring matching for WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877302">Mozilla Bug 877302</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
-
-var substringLength = 8;
-
-var prop = {
-  tel: [{value: "7932012345" }, {value: "7932012346"}]
-};
+<script type="application/javascript">
 
-var prop2 = {
-  tel: [{value: "01187654321" }]
-};
-
-var prop3 = {
-  tel: [{ value: "+43332112346" }]
-};
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_substringmatching.html";
+}
 
-var prop4 = {
-  tel: [{ value: "(0414) 233-9888" }]
-};
-
-var brazilianNumber = {
-  international1: "0041557932012345",
-  international2: "+557932012345"
-};
-
-var prop5 = {
-  tel: [{value: brazilianNumber.international2}]
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
 };
 
-var req;
-var steps = [
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact");
-    createResult1 = new mozContact(prop);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({});
-    req.onsuccess = function () {
-      is(req.result.length, 1, "One contact.");
-      findResult1 = req.result[0];
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 1");
-    var length = prop.tel[0].value.length;
-    var num = prop.tel[0].value.substring(length - substringLength, length);
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      is(findResult1.tel[0].value, "7932012345", "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 2");
-    var length = prop.tel[1].value.length;
-    var num = prop.tel[1].value.substring(length - substringLength, length);
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      is(findResult1.tel[0].value, "7932012345", "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 3");
-    var length = prop.tel[0].value.length;
-    var num = prop.tel[0].value.substring(length - substringLength + 1, length);
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found exactly 0 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 4");
-    var length = prop.tel[0].value.length;
-    var num = prop.tel[0].value.substring(length - substringLength - 1, length);
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact");
-    createResult1 = new mozContact(prop2);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 5");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "87654321"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 6");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "01187654321"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 7");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "909087654321"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 8");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "0411187654321"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 9");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "90411187654321"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 10");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: "+551187654321"};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact");
-    createResult1 = new mozContact(prop3);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    if (!isAndroid) { // Bug 905927
-      ok(true, "Retrieving by substring 1");
-      var length = prop3.tel[0].value.length;
-      var num = prop3.tel[0].value.substring(length - substringLength, length);
-      var options = {filterBy: ["tel"],
-                     filterOp: "match",
-                     filterValue: num};
-      req = mozContacts.find(options);
-      req.onsuccess = function () {
-        is(req.result.length, 0, "Found exactly 0 contacts.");
-        next();
-      };
-      req.onerror = onFailure;
-    } else {
-      SpecialPowers.executeSoon(next);
-    }
-  },
-  function () {
-    ok(true, "Adding contact");
-    createResult1 = new mozContact(prop4);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 1");
-    var num = "(0424) 233-9888"
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding a new contact with a Brazilian country code");
-    createResult1 = new mozContact(prop5);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for international number with prefix");
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: brazilianNumber.international1};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      ok(req.result.length == 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-SpecialPowers.pushPrefEnv({
-  set: [
-    ["dom.phonenumber.substringmatching.BR", substringLength],
-    ["ril.lastKnownSimMcc", "724"]
-  ]
-}, start_tests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_substringmatchingCL.html
+++ b/dom/contacts/tests/test_contacts_substringmatchingCL.html
@@ -1,204 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=877302
--->
 <head>
-  <title>Test for Bug 949537 substring matching for WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=949537">Mozilla Bug 949537</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
+<script type="application/javascript">
 
-var landlineNumber = "+56 2 27654321";
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_substringmatchingCL.html";
+}
 
-var number = {
-  local: "87654321",
-  international: "+56 9 87654321"
-};
-
-var properties = {
-  name: ["Testname2"],
-  tel: [{value: number.international}]
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
 };
 
-var req;
-var steps = [
-  function () {
-    ok(true, "Adding a contact with a Chilean number");
-    createResult1 = new mozContact(properties);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for Chilean number with prefix");
-    req = mozContacts.find({
-      filterBy: ["tel"],
-      filterOp: "match",
-      filterValue: number.international
-    });
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Searching for Chilean number using local number");
-    req = mozContacts.find({
-      filterBy: ["tel"],
-      filterOp: "match",
-      filterValue: number.local
-    });
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found 0 contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-
-  clearDatabase,
-
-  function () {
-    ok(true, "Adding contact with mobile number");
-    createResult1 = new mozContact({tel: [{value: number.international}]});
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({});
-    req.onsuccess = function () {
-      is(req.result.length, 1, "One contact.");
-      findResult1 = req.result[0];
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by last 8 digits");
-    req = mozContacts.find({
-      filterBy: ["tel"],
-      filterOp: "match",
-      filterValue: number.international.slice(-8)
-    });
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      is(findResult1.tel[0].value, number.international, "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by last 9 digits");
-    req = mozContacts.find({
-      filterBy: ["tel"],
-      filterOp: "match",
-      filterValue: number.international.slice(-9)
-    });
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      is(findResult1.tel[0].value, number.international, "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by last 6 digits");
-    req = mozContacts.find({
-      filterBy: ["tel"],
-      filterOp: "match",
-      filterValue: number.international.slice(-6)
-    });
-    req.onsuccess = function () {
-      is(req.result.length, 0, "Found exactly zero contacts.");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-
-  clearDatabase,
-
-  function () {
-    ok(true, "Adding contact with landline number");
-    createResult1 = new mozContact({tel: [{value: landlineNumber}]});
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({});
-    req.onsuccess = function () {
-      is(req.result.length, 1, "One contact.");
-      findResult1 = req.result[0];
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by last 7 digits (local number) with landline calling prefix");
-    req = mozContacts.find({
-      filterBy: ["tel"],
-      filterOp: "match",
-      filterValue: "022" + landlineNumber.slice(-7)
-    });
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      is(findResult1.id, sample_id1, "Same ID");
-      is(findResult1.tel[0].value, landlineNumber, "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-
-  clearDatabase,
-
-  function () {
-    ok(true, "all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-SpecialPowers.pushPrefEnv({
-  set: [
-    ["dom.phonenumber.substringmatching.CL", 8],
-    ["ril.lastKnownSimMcc", "730"]
-  ]
-}, start_tests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_contacts_substringmatchingVE.html
+++ b/dom/contacts/tests/test_contacts_substringmatchingVE.html
@@ -1,132 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=877302
--->
 <head>
-  <title>Test for Bug 877302 substring matching for WebContacts</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877302">Mozilla Bug 877302</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
+<script type="application/javascript">
+
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_contacts_substringmatchingVE.html";
+}
 
-var prop = {
-  tel: [{value: "7932012345" }, {value: "7704143727591"}]
-};
-
-var prop2 = {
-  tel: [{value: "7932012345" }, {value: "+58 212 5551212"}]
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
 };
 
-var req;
-var steps = [
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact");
-    createResult1 = new mozContact(prop);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving all contacts");
-    req = mozContacts.find({});
-    req.onsuccess = function () {
-      is(req.result.length, 1, "One contact.");
-      findResult1 = req.result[0];
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 1");
-    var length = prop.tel[0].value.length;
-    var num = "04143727591"
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      is(findResult1.tel[1].value, "7704143727591", "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Adding contact");
-    createResult1 = new mozContact(prop2);
-    req = navigator.mozContacts.save(createResult1);
-    req.onsuccess = function () {
-      ok(createResult1.id, "The contact now has an ID.");
-      sample_id1 = createResult1.id;
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Retrieving by substring 2");
-    var num = "5551212";
-    var options = {filterBy: ["tel"],
-                   filterOp: "match",
-                   filterValue: num};
-    req = mozContacts.find(options);
-    req.onsuccess = function () {
-      is(req.result.length, 1, "Found exactly 1 contact.");
-      findResult1 = req.result[0];
-      ok(findResult1.id == sample_id1, "Same ID");
-      is(findResult1.tel[1].value, "+58 212 5551212", "Same Value");
-      next();
-    };
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "Deleting database");
-    req = mozContacts.clear()
-    req.onsuccess = function () {
-      ok(true, "Deleted the database");
-      next();
-    }
-    req.onerror = onFailure;
-  },
-  function () {
-    ok(true, "all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-SpecialPowers.pushPrefEnv({
-  set: [
-    ["dom.phonenumber.substringmatching.VE", 7],
-    ["ril.lastKnownSimMcc", "734"]
-  ]
-}, start_tests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_migration.html
+++ b/dom/contacts/tests/test_migration.html
@@ -1,194 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
 <head>
-  <title>Migration tests</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<h1>migration tests</h1>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script type="text/javascript;version=1.8" src="shared.js"></script>
-<script class="testbody" type="text/javascript">
-"use strict";
-
-var backend, contactsCount, allContacts;
-function loadChromeScript() {
-  var url = SimpleTest.getTestFileURL("test_migration_chrome.js");
-  backend = SpecialPowers.loadChromeScript(url);
-}
+<script type="application/javascript">
 
-function addBackendEvents() {
-  backend.addMessageListener("createDB.success", function(count) {
-    contactsCount = count;
-    ok(true, "Created the database");
-    next();
-  });
-  backend.addMessageListener("createDB.error", function(err) {
-    ok(false, err);
-    next();
-  });
-
-  backend.addMessageListener("deleteDB.success", function() {
-    ok(true, "Deleted the database");
-    next();
-  });
-  backend.addMessageListener("deleteDB.error", function(err) {
-    ok(false, err);
-    next();
-  });
-}
-
-function createDB(version) {
-  info("Will create the DB at version " + version);
-  backend.sendAsyncMessage("createDB", version);
-}
-
-function deleteDB() {
-  info("Will delete the DB.");
-  backend.sendAsyncMessage("deleteDB");
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_migration.html";
 }
 
-var steps = [
-  function setupChromeScript() {
-    loadChromeScript();
-    addBackendEvents();
-    next();
-  },
-
-  deleteDB, // let's be sure the DB does not exist yet
-  createDB.bind(null, 12),
-
-  function testAccessMozContacts() {
-    info("Checking we have the right number of contacts: " + contactsCount);
-    var req = mozContacts.getCount();
-    req.onsuccess = function onsuccess() {
-      ok(true, "Could access the mozContacts API");
-      is(this.result, contactsCount, "Contacts count is correct");
-      next();
-    };
-
-    req.onerror = function onerror() {
-      ok(false, "Couldn't access the mozContacts API");
-      next();
-    };
-  },
-
-  function testRetrieveAllContacts() {
-    /* if the migration does not work right, either we'll have an error, or the
-       contacts won't be migrated properly and thus will fail WebIDL conversion,
-       which will manifest as a timeout */
-    info("Checking the contacts are corrected to obey WebIDL constraints.  (upgrades 14 to 17)");
-    var req = mozContacts.find();
-    req.onsuccess = function onsuccess() {
-      if (this.result) {
-        is(this.result.length, contactsCount, "Contacts array length is correct");
-        allContacts = this.result;
-        next();
-      } else {
-        ok(false, "Could access the mozContacts API but got no contacts!");
-        next();
-      }
-    };
-
-    req.onerror = function onerror() {
-      ok(false, "Couldn't access the mozContacts API");
-      next();
-    };
-  },
-
-  function checkNameIndex() {
-    info("Checking name index migration (upgrades 17 to 19).");
-    if (!allContacts) {
-      next();
-    }
-
-    var count = allContacts.length;
-
-    function finishRequest() {
-      count--;
-      if (!count) {
-        next();
-      }
-    }
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: true, context: document},
+    {type: "contacts-write", allow: true, context: document},
+    {type: "contacts-create", allow: true, context: document},
+  ], run_tests);
+};
 
-    allContacts.forEach(function(contact) {
-      var name = contact.name && contact.name[0];
-      if (!name) {
-        count--;
-        return;
-      }
-
-      var req = mozContacts.find({
-        filterBy: ["name"],
-        filterValue: name,
-        filterOp: "equals"
-      });
-
-      req.onsuccess = function onsuccess() {
-        if (this.result) {
-          info("Found contact '" + name + "', checking it's the correct one.");
-          checkContacts(this.result[0], contact);
-        } else {
-          ok(false, "Could not find contact with name '" + name + "'");
-        }
-
-        finishRequest();
-      };
-
-      req.onerror = function onerror() {
-        ok(false, "Error while finding contact with name '" + name + "'!");
-        finishRequest();
-      }
-    });
-
-    if (!count) {
-      ok(false, "No contact had a name, this is unexpected.");
-      next();
-    }
-  },
-
-  function checkSubstringMatching() {
-    var subject = "0004567890"; // the last 7 digits are the same that at least one contact
-    info("Looking for a contact matching " + subject);
-    var req = mozContacts.find({
-      filterValue: subject,
-      filterOp: "match",
-      filterBy: ["tel"],
-      filterLimit: 1
-    });
-
-    req.onsuccess = function onsuccess() {
-      if (this.result && this.result[0]) {
-        ok(true, "Found a contact with number " + this.result[0].tel[0].value);
-      }
-      next();
-    };
-
-    req.onerror = function onerror() {
-      ok(false, "Error while finding contact for substring matching check!");
-      next();
-    };
-  },
-
-  deleteDB,
-
-  function finish() {
-    backend.destroy();
-    info("all done!\n");
-    SimpleTest.finish();
-  }
-];
-
-// this is the Mcc for Brazil, so that we trigger the previous pref
-SpecialPowers.pushPrefEnv({"set": [["dom.phonenumber.substringmatching.BR", 7],
-                                   ["ril.lastKnownSimMcc", "724"]]}, start_tests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/contacts/tests/test_permission_denied.html
+++ b/dom/contacts/tests/test_permission_denied.html
@@ -1,120 +1,29 @@
-<!DOCTYPE html>
+<!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1081873
--->
 <head>
-  <title>Test for Bug 1081873</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081873">Mozilla Bug 1081873</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
+<iframe></iframe>
 <pre id="test">
-<script class="testbody" type="text/javascript">
-
-"use strict";
-
-SpecialPowers.addPermission("contacts-write", false, document);
-SpecialPowers.addPermission("contacts-read", false, document);
-SpecialPowers.addPermission("contacts-create", false, document);
-
-function onUnexpectedSuccess() {
-  ok(false, "Unexpected success");
-  next();
-}
+<script type="application/javascript">
 
-function onExpectedError(event) {
-  is(event.target.error.name, PERMISSION_DENIED, "Expected PERMISSION_DENIED");
-  next();
-}
-
-const PERMISSION_DENIED = "PERMISSION_DENIED";
-
-var index = 0;
-
-function next() {
-  info("Step " + index);
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    var i = index++;
-    steps[i]();
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
+function run_tests() {
+  var iframe = document.querySelector("iframe");
+  iframe.src = "file_permission_denied.html";
 }
 
-var steps = [
-  function() {
-    ok(true, "Add contact without permission");
-    var req = navigator.mozContacts.save(new mozContact({}));
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = onExpectedError;
-  },
-  function() {
-    ok(true, "Find contact without permission");
-    var req = navigator.mozContacts.find({});
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = onExpectedError;
-  },
-  function() {
-    ok(true, "Get all contacts without permission");
-    var req = navigator.mozContacts.getAll();
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = onExpectedError;
-  },
-  function() {
-    ok(true, "Remove contact without permission");
-    var req = navigator.mozContacts.remove("aId");
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = onExpectedError;
-  },
-  function() {
-    ok(true, "Clear contacts without permission");
-    var req = navigator.mozContacts.clear();
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = onExpectedError;
-  },
-  function() {
-    ok(true, "Get revision without permission");
-    var req = navigator.mozContacts.getRevision();
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = onExpectedError;
-  },
-  function() {
-    ok(true, "Get count without permission");
-    var req = navigator.mozContacts.getCount();
-    req.onsuccess = onUnexpectedSuccess;
-    req.onerror = function() {
-      is(req.error.name, PERMISSION_DENIED, "Expected PERMISSION_DENIED");
-      SimpleTest.finish();
-    };
-  }
-];
-
 SimpleTest.waitForExplicitFinish();
-
-const DENY = SpecialPowers.Ci.nsIPermissionManager.DENY_ACTION;
-var interval = setInterval(function() {
-  if (!SpecialPowers.testPermission("contacts-read", DENY, document) ||
-      !SpecialPowers.testPermission("contacts-write", DENY, document) ||
-      !SpecialPowers.testPermission("contacts-create", DENY, document)) {
-    return;
-  }
-  clearInterval(interval);
-  next();
-}, 1000);
+onload = function() {
+  SpecialPowers.pushPermissions([
+    {type: "contacts-read", allow: false, context: document},
+    {type: "contacts-write", allow: false, context: document},
+    {type: "contacts-create", allow: false, context: document},
+  ], run_tests);
+};
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/icc/tests/marionette/test_icc_contact_read.js
+++ b/dom/icc/tests/marionette/test_icc_contact_read.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = "head.js";
+MARIONETTE_CONTEXT = "chrome";
 
 function testReadContacts(aIcc, aType) {
   log("testReadContacts: type=" + aType);
   let iccId = aIcc.iccInfo.iccid;
   return aIcc.readContacts(aType)
     .then((aResult) => {
 
       is(Array.isArray(aResult), true);
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -793,19 +793,19 @@ var interfaceNamesInGlobalScope =
     "MouseEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MouseScrollEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozActivity", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozClirModeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "mozContact",
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    "MozContactChangeEvent",
+    {name: "mozContact", b2g: true, permission: "contacts-read"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MozContactChangeEvent", b2g: true, permission: "contacts-read"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozCSSKeyframeRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozCSSKeyframesRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozEmergencyCbModeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozInputContext", b2g: true, permission: ["input"]},
--- a/dom/webidl/Contacts.webidl
+++ b/dom/webidl/Contacts.webidl
@@ -53,17 +53,18 @@ dictionary ContactProperties {
   sequence<DOMString>?           category;
   sequence<DOMString>?           org;
   sequence<DOMString>?           jobTitle;
   sequence<DOMString>?           note;
   sequence<DOMString>?           key;
 };
 
 [Constructor(optional ContactProperties properties),
- JSImplementation="@mozilla.org/contact;1"]
+ JSImplementation="@mozilla.org/contact;1",
+ CheckAnyPermissions="contacts-read contacts-write contacts-create"]
 interface mozContact {
                  attribute DOMString  id;
         readonly attribute Date?      published;
         readonly attribute Date?      updated;
 
                  attribute Date?      bday;
                  attribute Date?      anniversary;
 
@@ -111,17 +112,18 @@ dictionary ContactFindSortOptions {
 dictionary ContactFindOptions : ContactFindSortOptions {
   DOMString      filterValue;  // e.g. "Tom"
   DOMString      filterOp;     // e.g. "startsWith"
   any            filterBy;     // e.g. ["givenName", "nickname"]
   unsigned long  filterLimit = 0;
 };
 
 [NoInterfaceObject, NavigatorProperty="mozContacts",
- JSImplementation="@mozilla.org/contactManager;1"]
+ JSImplementation="@mozilla.org/contactManager;1",
+ CheckAnyPermissions="contacts-read contacts-write contacts-create"]
 interface ContactManager : EventTarget {
   DOMRequest find(optional ContactFindOptions options);
   DOMCursor  getAll(optional ContactFindSortOptions options);
   DOMRequest clear();
   DOMRequest save(mozContact contact);
   DOMRequest remove((mozContact or DOMString) contactOrId);
   DOMRequest getRevision();
   DOMRequest getCount();
--- a/dom/webidl/MozContactChangeEvent.webidl
+++ b/dom/webidl/MozContactChangeEvent.webidl
@@ -1,15 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
-[Constructor(DOMString type, optional MozContactChangeEventInit eventInitDict)]
+[Constructor(DOMString type, optional MozContactChangeEventInit eventInitDict),
+ CheckAnyPermissions="contacts-read contacts-write contacts-create"]
 interface MozContactChangeEvent : Event
 {
   readonly attribute DOMString? contactID;
   readonly attribute DOMString? reason;
 };
 
 dictionary MozContactChangeEventInit : EventInit
 {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4575,19 +4575,16 @@ pref("dom.sms.requestStatusReport", true
 pref("dom.sms.defaultServiceId", 0);
 // MobileMessage GetMessages/GetThreads read ahead aggressiveness.
 //
 // positive: finite read-ahead entries,
 // 0: don't read ahead unless explicitly requested, (default)
 // negative: read ahead all IDs if possible.
 pref("dom.sms.maxReadAheadEntries", 0);
 
-// WebContacts
-pref("dom.mozContacts.enabled", false);
-
 // WebAlarms
 pref("dom.mozAlarms.enabled", false);
 
 // Push
 
 pref("dom.push.enabled", false);
 
 pref("dom.push.loglevel", "off");