merge fx-team to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 24 Feb 2014 12:54:35 +0100
changeset 170497 1f5b177c7c6235b1e79a07a6eb849eea23d96c05
parent 170491 925d3aa8a9b21bd3d9a7e26b284d48d053ea21c4 (current diff)
parent 170496 57db167189218a31943007f5075c8514767451ec (diff)
child 170524 d9c58306bfbc74c920aa5c995b86b5f8a398352b
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone30.0a1
merge fx-team to mozilla-central
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -44,19 +44,30 @@
 
 <!-- Footer stuff -->
 <!ENTITY prefs.tosLink.label        "Terms of Service">
 <!ENTITY prefs.ppLink.label         "Privacy Policy">
 
 <!-- Firefox Accounts stuff -->
 <!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">
 <!ENTITY determiningAcctStatus.label     "Determining your account status…">
+
+<!-- LOCALIZATION NOTE (signedInUnverified.beforename.label,
+signedInUnverified.aftername.label): these two string are used respectively
+before and after the account email address. Localizers can use one of them, or
+both, to better adapt this sentence to their language.
+-->
 <!ENTITY signedInUnverified.beforename.label "">
 <!ENTITY signedInUnverified.aftername.label "is not verified.">
 
+<!-- LOCALIZATION NOTE (signedInLoginFailure.beforename.label,
+signedInLoginFailure.aftername.label): these two string are used respectively
+before and after the account email address. Localizers can use one of them, or
+both, to better adapt this sentence to their language.
+-->
 <!ENTITY signedInLoginFailure.beforename.label "Please sign in to reconnect">
 <!ENTITY signedInLoginFailure.aftername.label "">
 
 <!ENTITY notSignedIn.label           "You are not signed in.">
 <!ENTITY signIn.label                "Sign in">
 <!ENTITY manage.label                "Manage">
 <!ENTITY disconnect.label            "Disconnect…">
 <!ENTITY verify.label                "Verify Email">
--- a/mobile/android/base/tests/testBrowserProvider.java
+++ b/mobile/android/base/tests/testBrowserProvider.java
@@ -60,27 +60,33 @@ public class testBrowserProvider extends
                                         BrowserContract.Bookmarks.MOBILE_FOLDER_GUID,
                                         BrowserContract.Bookmarks.MENU_FOLDER_GUID,
                                         BrowserContract.Bookmarks.TAGS_FOLDER_GUID,
                                         BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID,
                                         BrowserContract.Bookmarks.UNFILED_FOLDER_GUID,
                                         BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID });
 
         c = mProvider.query(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
-        mAsserter.is(c.getCount(), 7, "All non-special bookmarks and folders were deleted");
+        assertCountIsAndClose(c, 7, "All non-special bookmarks and folders were deleted");
 
         mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
         c = mProvider.query(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
-        mAsserter.is(c.getCount(), 0, "All history entries were deleted");
+        assertCountIsAndClose(c, 0, "All history entries were deleted");
 
-        mProvider.delete(BrowserContract.Favicons.CONTENT_URI, null, null);
-        mAsserter.is(c.getCount(), 0, "All favicons were deleted");
+        mProvider.delete(appendUriParam(BrowserContract.Favicons.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
+        c = mProvider.query(appendUriParam(BrowserContract.Favicons.CONTENT_URI,
+                                           BrowserContract.PARAM_SHOW_DELETED, "1"),
+                                           null, null, null, null);
+        assertCountIsAndClose(c, 0, "All favicons were deleted");
 
-        mProvider.delete(BrowserContract.Thumbnails.CONTENT_URI, null, null);
-        mAsserter.is(c.getCount(), 0, "All thumbnails were deleted");
+        mProvider.delete(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
+        c = mProvider.query(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI,
+                                           BrowserContract.PARAM_SHOW_DELETED, "1"),
+                                           null, null, null, null);
+        assertCountIsAndClose(c, 0, "All thumbnails were deleted");
     }
 
     private ContentValues createBookmark(String title, String url, long parentId,
             int type, int position, String tags, String description, String keyword) throws Exception {
         ContentValues bookmark = new ContentValues();
 
         bookmark.put(BrowserContract.Bookmarks.TITLE, title);
         bookmark.put(BrowserContract.Bookmarks.URL, url);
@@ -321,31 +327,31 @@ public class testBrowserProvider extends
             try {
                 applyResult = mProvider.applyBatch(mOperations);
             } catch (OperationApplicationException ex) {
                 seenException = true;
             }
             mAsserter.is(seenException, false, "Batch updating succeded");
             mOperations.clear();
 
-            // Delte all visits
+            // Delete all visits
             for (int i = 0; i < TESTCOUNT; i++) {
                 builder = ContentProviderOperation.newDelete(BrowserContract.History.CONTENT_URI);
                 builder.withSelection(BrowserContract.History.URL  + " = ?",
                                       new String[] {"http://www.test.org/" + i});
                 builder.withExpectedCount(1);
                 // Queue the operation
                 mOperations.add(builder.build());
             }
             try {
                 applyResult = mProvider.applyBatch(mOperations);
             } catch (OperationApplicationException ex) {
                 seenException = true;
             }
-            mAsserter.is(seenException, false, "Batch deletion succeded");
+            mAsserter.is(seenException, false, "Batch deletion succeeded");
         }
 
         // Force a Constraint error, see if later operations still apply correctly
         public void testApplyBatchErrors() throws Exception {
             ArrayList<ContentProviderOperation> mOperations
                 = new ArrayList<ContentProviderOperation>();
 
             // Test a bunch of inserts with applyBatch
@@ -470,16 +476,18 @@ public class testBrowserProvider extends
                     mAsserter.is(new Integer(id), new Integer(rootId), "The id of places folder is correct");
                 } else if (guid.equals(BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID)) {
                     mAsserter.is(new Integer(id), new Integer(readingListId), "The id of reading list folder is correct");
                 }
 
                 mAsserter.is(new Integer(parentId), new Integer(rootId),
                              "The PARENT of the " + guid + " special folder is correct");
             }
+
+            c.close();
         }
     }
 
     class TestInsertBookmarks extends Test {
         private long insertWithNullCol(String colName) throws Exception {
             ContentValues b = createOneBookmark();
             b.putNull(colName);
             long id = -1;
@@ -544,16 +552,17 @@ public class testBrowserProvider extends
             b.remove(BrowserContract.Bookmarks.TYPE);
             id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
             c = getBookmarkById(id);
 
             mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK),
                          "Inserted bookmark has correct default type");
+            c.close();
         }
     }
 
     class TestInsertBookmarksFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues b = createOneBookmark();
 
@@ -566,83 +575,91 @@ public class testBrowserProvider extends
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
 
             Cursor c = getBookmarkById(id, new String[] { BrowserContract.Bookmarks.FAVICON });
 
             mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Bookmarks.FAVICON)), "UTF8"),
                          favicon, "Inserted bookmark has corresponding favicon image");
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     class TestDeleteBookmarks extends Test {
         private long insertOneBookmark() throws Exception {
             ContentValues b = createOneBookmark();
             long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
 
             Cursor c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
+            c.close();
 
             return id;
         }
 
         @Override
         public void test() throws Exception {
             long id = insertOneBookmark();
 
             int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(id) });
 
             mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
 
             Cursor c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), true, "Deleted bookmark was only marked as deleted");
+            c.close();
 
             deleted = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
                                        BrowserContract.Bookmarks._ID + " = ?",
                                        new String[] { String.valueOf(id) });
 
             mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
 
             c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), false, "Inserted bookmark is now actually deleted");
+            c.close();
 
             id = insertOneBookmark();
 
             deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
             mAsserter.is((deleted == 1), true,
                          "Inserted bookmark was deleted using URI with id");
 
             c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), false,
                          "Inserted bookmark can't be found after deletion using URI with ID");
+            c.close();
 
             if (Build.VERSION.SDK_INT >= 8 &&
                 Build.VERSION.SDK_INT < 16) {
                 ContentValues b = createBookmark("Folder", null, mMobileFolderId,
                         BrowserContract.Bookmarks.TYPE_FOLDER, 0, "folderTags", "folderDescription", "folderKeyword");
 
                 long parentId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
                 c = getBookmarkById(parentId);
                 mAsserter.is(c.moveToFirst(), true, "Inserted bookmarks folder found");
+                c.close();
 
                 b = createBookmark("Example", "http://example.com", parentId,
                         BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
 
                 id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
                 c = getBookmarkById(id);
                 mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
+                c.close();
 
                 deleted = 0;
                 try {
                     Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, parentId);
                     deleted = mProvider.delete(appendUriParam(uri, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
                 } catch(Exception e) {}
 
                 mAsserter.is((deleted == 0), true,
@@ -659,21 +676,23 @@ public class testBrowserProvider extends
             final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL);
             long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
 
             // Insert the favicon into the favicons table
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON"));
 
             Cursor c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
+            c.close();
 
             mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
+            c.close();
         }
     }
 
     class TestUpdateBookmarks extends Test {
         private int updateWithNullCol(long id, String colName) throws Exception {
             ContentValues u = new ContentValues();
             u.putNull(colName);
 
@@ -708,16 +727,17 @@ public class testBrowserProvider extends
             u.put(BrowserContract.Bookmarks.TYPE, BrowserContract.Bookmarks.TYPE_FOLDER);
             u.put(BrowserContract.Bookmarks.POSITION, 10);
 
             int updated = mProvider.update(BrowserContract.Bookmarks.CONTENT_URI, u,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(id) });
 
             mAsserter.is((updated == 1), true, "Inserted bookmark was updated");
+            c.close();
 
             c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), u.getAsString(BrowserContract.Bookmarks.TITLE),
                          "Inserted bookmark has correct title");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
                          "Inserted bookmark has correct URL");
@@ -747,22 +767,24 @@ public class testBrowserProvider extends
             updated = updateWithNullCol(id, BrowserContract.Bookmarks.TYPE);
             mAsserter.is((updated > 0), false,
                          "Should not be able to update bookmark with null type");
 
             u = new ContentValues();
             u.put(BrowserContract.Bookmarks.URL, "http://examples2.com");
 
             updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), u, null, null);
+            c.close();
 
             c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
                          "Updated bookmark has correct URL using URI with id");
+            c.close();
         }
     }
 
     class TestUpdateBookmarksFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues b = createOneBookmark();
 
@@ -779,22 +801,24 @@ public class testBrowserProvider extends
             Cursor c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
 
             ContentValues u = createFaviconEntry(pageUrl, newFavicon);
             mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          newFavicon, "Updated favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     /**
      * Create a folder of one thousand and one bookmarks, then impose an order
      * on them.
      *
      * Verify that the reordering worked by querying.
@@ -820,16 +844,17 @@ public class testBrowserProvider extends
                     mAsserter.is(pos, (long) i, "Position matches sequence.");
                     mAsserter.is(guid, items[i], "GUID matches sequence.");
                 }
                 ++i;
                 c.moveToNext();
             }
 
             mAsserter.is(i, count, "Folder has the right number of children.");
+            c.close();
         }
 
         public static final int NUMBER_OF_CHILDREN = 1001;
         @Override
         public void test() throws Exception {
             // Create the containing folder.
             ContentValues folder = createBookmark("FolderFolder", "", mMobileFolderId,
                                                   BrowserContract.Bookmarks.TYPE_FOLDER, 0, "",
@@ -916,16 +941,17 @@ public class testBrowserProvider extends
 
             id = insertWithNullCol(BrowserContract.History.URL);
             mAsserter.is(new Long(id), new Long(-1),
                          "Should not be able to insert history with null URL");
 
             id = insertWithNullCol(BrowserContract.History.VISITS);
             mAsserter.is(new Long(id), new Long(-1),
                          "Should not be able to insert history with null number of visits");
+            c.close();
         }
     }
 
     class TestInsertHistoryFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -938,32 +964,35 @@ public class testBrowserProvider extends
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
 
             Cursor c = getHistoryEntryById(id, new String[] { BrowserContract.History.FAVICON });
 
             mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.History.FAVICON)), "UTF8"),
                          favicon, "Inserted history entry has corresponding favicon image");
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     class TestDeleteHistory extends Test {
         private long insertOneHistoryEntry() throws Exception {
             ContentValues h = createOneHistoryEntry();
             long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
 
             Cursor c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
+            c.close();
 
             return id;
         }
 
         @Override
         public void test() throws Exception {
             long id = insertOneHistoryEntry();
 
@@ -976,29 +1005,32 @@ public class testBrowserProvider extends
             Cursor c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), true, "Deleted history entry was only marked as deleted");
 
             deleted = mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
                                        BrowserContract.History._ID + " = ?",
                                        new String[] { String.valueOf(id) });
 
             mAsserter.is((deleted == 1), true, "Inserted history entry was deleted");
+            c.close();
 
             c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), false, "Inserted history is now actually deleted");
 
             id = insertOneHistoryEntry();
 
             deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
             mAsserter.is((deleted == 1), true,
                          "Inserted history entry was deleted using URI with id");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), false,
                          "Inserted history entry can't be found after deletion using URI with ID");
+            c.close();
         }
     }
 
     class TestDeleteHistoryFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1007,19 +1039,21 @@ public class testBrowserProvider extends
 
             // Insert the favicon into the favicons table
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON"));
 
             Cursor c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
+            c.close();
         }
     }
 
     class TestUpdateHistory extends Test {
         private int updateWithNullCol(long id, String colName) throws Exception {
             ContentValues u = new ContentValues();
             u.putNull(colName);
 
@@ -1051,16 +1085,17 @@ public class testBrowserProvider extends
             u.put(BrowserContract.History.TITLE, h.getAsString(BrowserContract.History.TITLE) + "CHANGED");
             u.put(BrowserContract.History.URL, h.getAsString(BrowserContract.History.URL) + "/more/stuff");
 
             int updated = mProvider.update(BrowserContract.History.CONTENT_URI, u,
                                            BrowserContract.History._ID + " = ?",
                                            new String[] { String.valueOf(id) });
 
             mAsserter.is((updated == 1), true, "Inserted history entry was updated");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), u.getAsString(BrowserContract.History.TITLE),
                          "Updated history entry has correct title");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
                          "Updated history entry has correct URL");
@@ -1084,22 +1119,24 @@ public class testBrowserProvider extends
             updated = updateWithNullCol(id, BrowserContract.History.VISITS);
             mAsserter.is((updated > 0), false,
                          "Should not be able to update history with null number of visits");
 
             u = new ContentValues();
             u.put(BrowserContract.History.URL, "http://examples2.com");
 
             updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), u, null, null);
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
                          "Updated history entry has correct URL using URI with id");
+            c.close();
         }
     }
 
     class TestUpdateHistoryFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1116,22 +1153,24 @@ public class testBrowserProvider extends
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
 
             ContentValues u = createFaviconEntry(pageUrl, newFavicon);
 
             mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          newFavicon, "Updated favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     class TestUpdateOrInsertHistory extends Test {
         private final String TEST_URL_1 = "http://example.com";
         private final String TEST_URL_2 = "http://example.org";
         private final String TEST_TITLE = "Example";
 
@@ -1190,16 +1229,17 @@ public class testBrowserProvider extends
             values = new ContentValues();
             values.put(BrowserContract.History.DATE_LAST_VISITED, System.currentTimeMillis());
             values.put(BrowserContract.History.TITLE, TEST_TITLE);
 
             updated = mProvider.update(updateOrInsertHistoryUri, values,
                                        BrowserContract.History._ID + " = ?",
                                        new String[] { String.valueOf(id) });
             mAsserter.is((updated == 1), true, "Inserted history entry was updated");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
                          "Updated history entry has correct title");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(2),
                          "Updated history entry has correct number of visits");
@@ -1215,16 +1255,18 @@ public class testBrowserProvider extends
             values.put(BrowserContract.History.VISITS, 10);
 
             updated = mProvider.update(updateOrInsertHistoryUri, values,
                                            BrowserContract.History.URL + " = ?",
                                            new String[] { values.getAsString(BrowserContract.History.URL) });
             mAsserter.is((updated == 1), true, "History entry was inserted");
 
             id = getHistoryEntryIdByUrl(TEST_URL_2);
+            c.close();
+
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "History entry was inserted");
 
             dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
             dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
 
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(10),
                          "Inserted history entry has correct specified number of visits");
@@ -1234,30 +1276,32 @@ public class testBrowserProvider extends
             // Update the history entry, specifying additional visit count
             values = new ContentValues();
             values.put(BrowserContract.History.VISITS, 10);
 
             updated = mProvider.update(updateOrInsertHistoryUri, values,
                                        BrowserContract.History._ID + " = ?",
                                        new String[] { String.valueOf(id) });
             mAsserter.is((updated == 1), true, "Inserted history entry was updated");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
                          "Updated history entry has correct unchanged title");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), TEST_URL_2,
                          "Updated history entry has correct unchanged URL");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(20),
                          "Updated history entry has correct number of visits");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))), new Long(dateCreated),
                          "Updated history entry has same creation date");
             mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
                             "Updated history entry has new modification date");
+            c.close();
 
         }
     }
 
     class TestInsertHistoryThumbnails extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
@@ -1270,16 +1314,17 @@ public class testBrowserProvider extends
             // Insert the thumbnail into the thumbnails table
             mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, thumbnail));
 
             Cursor c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
                          thumbnail, "Inserted thumbnail has corresponding thumbnail image");
+            c.close();
         }
     }
 
     class TestUpdateHistoryThumbnails extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1296,22 +1341,24 @@ public class testBrowserProvider extends
             mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
                          thumbnail, "Inserted thumbnail has corresponding thumbnail image");
 
             ContentValues u = createThumbnailEntry(pageUrl, newThumbnail);
 
             mProvider.update(BrowserContract.Thumbnails.CONTENT_URI, u, null, null);
+            c.close();
 
             c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Updated thumbnail found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
                          newThumbnail, "Updated thumbnail has corresponding thumbnail image");
+            c.close();
         }
     }
 
     class TestDeleteHistoryThumbnails extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1320,19 +1367,21 @@ public class testBrowserProvider extends
 
             // Insert the thumbnail into the thumbnails table
             mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, "THUMBNAIL"));
 
             Cursor c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
 
             mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
+            c.close();
 
             c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), false, "Thumbnail is deleted with last reference to it");
+            c.close();
         }
     }
 
     class TestCombinedView extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE_1 = "Test Page 1";
             final String TITLE_2 = "Test Page 2";
@@ -1430,16 +1479,17 @@ public class testBrowserProvider extends
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(combinedHistoryId),
                          "Combined entry has correct history id");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_3,
                          "Combined entry has correct url");
             mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
                          "Combined entry has correct number of visits");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
                          "Combined entry has correct last visit time");
+            c.close();
         }
     }
 
     class TestCombinedViewDisplay extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE_1 = "Test Page 1";
             final String TITLE_2 = "Test Page 2";
@@ -1490,16 +1540,17 @@ public class testBrowserProvider extends
                 long id = c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID));
 
                 int display = c.getInt(c.getColumnIndex(BrowserContract.Combined.DISPLAY));
                 int expectedDisplay = (id == readingListItemId || id == readingListItemId2 ? BrowserContract.Combined.DISPLAY_READER : BrowserContract.Combined.DISPLAY_NORMAL);
 
                 mAsserter.is(new Integer(display), new Integer(expectedDisplay),
                                  "Combined display column should always be DISPLAY_READER for the reading list item");
             }
+            c.close();
         }
     }
 
     class TestCombinedViewWithDeletedBookmark extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE = "Test Page 1";
             final String URL = "http://example.com";
@@ -1522,23 +1573,25 @@ public class testBrowserProvider extends
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(combinedBookmarkId),
                          "Bookmark id should be set correctly on combined entry");
 
             int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(combinedBookmarkId) });
 
             mAsserter.is((deleted == 1), true, "Inserted combined bookmark was deleted");
+            c.close();
 
             c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
             mAsserter.is(c.getCount(), 1, "1 combined entry found");
 
             mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
                          "Bookmark id should not be set to removed bookmark id");
+            c.close();
         }
     }
 
     class TestCombinedViewWithDeletedReadingListItem extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE = "Test Page 1";
             final String URL = "http://example.com";
@@ -1564,25 +1617,27 @@ public class testBrowserProvider extends
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_READER),
                          "Combined entry should have reader display type");
 
             int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(combinedReadingListItemId) });
 
             mAsserter.is((deleted == 1), true, "Inserted combined reading list item was deleted");
+            c.close();
 
             c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
             mAsserter.is(c.getCount(), 1, "1 combined entry found");
 
             mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
                          "Bookmark id should not be set to removed bookmark id");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_NORMAL),
                          "Combined entry should have reader display type");
+            c.close();
         }
     }
 
     class TestExpireHistory extends Test {
         private void createFakeHistory(long timeShift, int count) {
             // Insert a bunch of very new entries
             ContentValues[] allVals = new ContentValues[count];
             long time = System.currentTimeMillis() - timeShift;
@@ -1603,96 +1658,101 @@ public class testBrowserProvider extends
                 ContentValues cv = new ContentValues();
                 cv.put(BrowserContract.History.DATE_CREATED, time);
                 cv.put(BrowserContract.History.DATE_MODIFIED, time);
                 mProvider.update(BrowserContract.History.CONTENT_URI, cv, BrowserContract.History.URL + " = ?",
                                  new String[] { "http://www.test.org/" + i });
             }
 
             Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), count, count + " history entries found");
+
+            assertCountIsAndClose(c, count, count + " history entries found");
 
             // add thumbnails for each entry
             allVals = new ContentValues[count];
             for (int i = 0; i < count; i++) {
                 allVals[i] = new ContentValues();
                 allVals[i].put(BrowserContract.Thumbnails.DATA, i);
                 allVals[i].put(BrowserContract.Thumbnails.URL, "http://www.test.org/" + i);
             }
 
             inserts = mProvider.bulkInsert(BrowserContract.Thumbnails.CONTENT_URI, allVals);
             mAsserter.is(inserts, count, "Expected number of inserts matches");
 
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), count, count + " thumbnails entries found");
+            assertCountIsAndClose(c, count, count + " thumbnails entries found");
         }
 
         @Override
         public void test() throws Exception {
             final int count = 3000;
             final int thumbCount = 15;
 
             // insert a bunch of new entries
             createFakeHistory(0, count);
 
             // expiring with a normal priority should not delete new entries
             Uri url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
             mProvider.delete(url, null, null);
             Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), count, count + " history entries found");
+            assertCountIsAndClose(c, count, count + " history entries found");
 
             // expiring with a normal priority should delete all but 10 thumbnails
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
 
             ensureEmptyDatabase();
-            // insert a bunch of new entries
+
+            // Insert a bunch of new entries.
             createFakeHistory(0, count);
 
-            // expiring with a aggressive priority should leave 500 entries
+            // Expiring with a aggressive priority should leave 500 entries.
             url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
             mProvider.delete(url, null, null);
+
             c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), 500, "500 history entries found");
+            assertCountIsAndClose(c, 500, "500 history entries found");
 
-            // expiring with a aggressive priority should delete all but 10 thumbnails
+            // Expiring with a aggressive priority should delete all but 10 thumbnails.
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
 
             ensureEmptyDatabase();
-            // insert a bunch of entries with an old time created/modified
+
+            // Insert a bunch of entries with an old time created/modified.
             long time = 1000L * 60L * 60L * 24L * 30L * 3L;
             createFakeHistory(time, count);
 
-            // expiring with an normal priority should remove at most 1000 entries
-            // entries leaving at least 2000
+            // Expiring with an normal priority should remove at most 1000 entries,
+            // entries leaving at least 2000.
             url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
             mProvider.delete(url, null, null);
+
             c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), 2000, "2000 history entries found");
+            assertCountIsAndClose(c, 2000, "2000 history entries found");
 
-            // expiring with a normal priority should delete all but 10 thumbnails
+            // Expiring with a normal priority should delete all but 10 thumbnails.
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
 
             ensureEmptyDatabase();
             // insert a bunch of entries with an old time created/modified
             time = 1000L * 60L * 60L * 24L * 30L * 3L;
             createFakeHistory(time, count);
 
-            // expiring with an agressive priority should remove old
-            // entries leaving at least 500
+            // Expiring with an aggressive priority should remove old
+            // entries, leaving at least 500.
             url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
             mProvider.delete(url, null, null);
             c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), 500, "500 history entries found");
+            assertCountIsAndClose(c, 500, "500 history entries found");
 
             // expiring with an aggressive priority should delete all but 10 thumbnails
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
         }
     }
 
     /*
      * Verify that insert, update, delete, and bulkInsert operations
      * notify the ambient content resolver.  Each operation calls the
      * content resolver notifyChange method synchronously, so it is
      * okay to test sequentially.
@@ -1771,9 +1831,21 @@ public class testBrowserProvider extends
 
             mAsserter.is(Long.valueOf(numBulkInserted),
                          Long.valueOf(1),
                          "Correct number of items are bulkInserted");
 
             ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "bulkInsert");
         }
     }
+
+    /**
+     * Assert that the provided cursor has the expected number of rows,
+     * closing the cursor afterwards.
+     */
+    private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
+        try {
+            mAsserter.is(c.getCount(), expectedCount, message);
+        } finally {
+            c.close();
+        }
+    }
 }
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -78,38 +78,94 @@ this.EXPORTED_SYMBOLS = [ "AddonManager"
 const CATEGORY_PROVIDER_MODULE = "addon-provider-module";
 
 // A list of providers to load by default
 const DEFAULT_PROVIDERS = [
   "resource://gre/modules/addons/XPIProvider.jsm",
   "resource://gre/modules/LightweightThemeManager.jsm"
 ];
 
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  this.__defineGetter__(aName, function logFuncGetter() {
-    Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
-
-    LogManager.getLogger("addons.manager", this);
-    return this[aName];
-  });
-}, this);
+Cu.import("resource://gre/modules/Log.jsm");
+// Configure a logger at the parent 'addons' level to format
+// messages for all the modules under addons.*
+const PARENT_LOGGER_ID = "addons";
+let parentLogger = Log.repository.getLogger(PARENT_LOGGER_ID);
+parentLogger.level = Log.Level.Warn;
+let formatter = new Log.BasicFormatter();
+// Set parent logger (and its children) to append to
+// the Javascript section of the Browser Console
+parentLogger.addAppender(new Log.ConsoleAppender(formatter));
+// Set parent logger (and its children) to
+// also append to standard out
+parentLogger.addAppender(new Log.DumpAppender(formatter));
+
+// Create a new logger (child of 'addons' logger)
+// for use by the Addons Manager
+const LOGGER_ID = "addons.manager";
+let logger = Log.repository.getLogger(LOGGER_ID);
+
+// Provide the ability to enable/disable logging
+// messages at runtime.
+// If the "extensions.logging.enabled" preference is
+// missing or 'false', messages at the WARNING and higher
+// severity should be logged to the JS console and standard error.
+// If "extensions.logging.enabled" is set to 'true', messages
+// at DEBUG and higher should go to JS console and standard error.
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+/**
+ * Preference listener which listens for a change in the
+ * "extensions.logging.enabled" preference and changes the logging level of the
+ * parent 'addons' level logger accordingly.
+ */
+var PrefObserver = {
+    init: function PrefObserver_init() {
+      Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
+      Services.obs.addObserver(this, "xpcom-shutdown", false);
+      this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
+    },
+
+    observe: function PrefObserver_observe(aSubject, aTopic, aData) {
+      if (aTopic == "xpcom-shutdown") {
+        Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
+        Services.obs.removeObserver(this, "xpcom-shutdown");
+      }
+      else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
+        let debugLogEnabled = false;
+        try {
+          debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
+        }
+        catch (e) {
+        }
+        if (debugLogEnabled) {
+          parentLogger.level = Log.Level.Debug;
+        }
+        else {
+          parentLogger.level = Log.Level.Warn;
+        }
+      }
+    }
+};
+
+PrefObserver.init();
 
 /**
  * Calls a callback method consuming any thrown exception. Any parameters after
  * the callback parameter will be passed to the callback.
  *
  * @param  aCallback
  *         The callback method to call
  */
 function safeCall(aCallback, ...aArgs) {
   try {
     aCallback.apply(null, aArgs);
   }
   catch (e) {
-    WARN("Exception calling callback", e);
+    logger.warn("Exception calling callback", e);
   }
 }
 
 /**
  * Calls a method on a provider if it exists and consumes any thrown exception.
  * Any parameters after the dflt parameter are passed to the provider's method.
  *
  * @param  aProvider
@@ -125,17 +181,17 @@ function safeCall(aCallback, ...aArgs) {
 function callProvider(aProvider, aMethod, aDefault, ...aArgs) {
   if (!(aMethod in aProvider))
     return aDefault;
 
   try {
     return aProvider[aMethod].apply(aProvider, aArgs);
   }
   catch (e) {
-    ERROR("Exception calling provider " + aMethod, e);
+    logger.error("Exception calling provider " + aMethod, e);
     return aDefault;
   }
 }
 
 /**
  * Gets the currently selected locale for display.
  * @return  the selected locale or "en-US" if none is selected
  */
@@ -491,17 +547,17 @@ var AddonManagerInternal = {
 
       let oldPlatformVersion = null;
       try {
         oldPlatformVersion = Services.prefs.getCharPref(PREF_EM_LAST_PLATFORM_VERSION);
       }
       catch (e) { }
 
       if (appChanged !== false) {
-        LOG("Application has been upgraded");
+        logger.debug("Application has been upgraded");
         Services.prefs.setCharPref(PREF_EM_LAST_APP_VERSION,
                                    Services.appinfo.version);
         Services.prefs.setCharPref(PREF_EM_LAST_PLATFORM_VERSION,
                                    Services.appinfo.platformVersion);
         Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION,
                                   (appChanged === undefined ? 0 : -1));
       }
 
@@ -553,17 +609,17 @@ var AddonManagerInternal = {
       // Ensure all default providers have had a chance to register themselves
       if (defaultProvidersEnabled) {
         DEFAULT_PROVIDERS.forEach(function(url) {
           try {
             Components.utils.import(url, {});
           }
           catch (e) {
             AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
-            ERROR("Exception loading default provider \"" + url + "\"", e);
+            logger.error("Exception loading default provider \"" + url + "\"", e);
           }
         });
       }
 
       // Load any providers registered in the category manager
       let catman = Cc["@mozilla.org/categorymanager;1"].
                    getService(Ci.nsICategoryManager);
       let entries = catman.enumerateCategory(CATEGORY_PROVIDER_MODULE);
@@ -571,17 +627,17 @@ var AddonManagerInternal = {
         let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
         let url = catman.getCategoryEntry(CATEGORY_PROVIDER_MODULE, entry);
 
         try {
           Components.utils.import(url, {});
         }
         catch (e) {
           AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
-          ERROR("Exception loading provider " + entry + " from category \"" +
+          logger.error("Exception loading provider " + entry + " from category \"" +
                 url + "\"", e);
         }
       }
 
       // Register our shutdown handler with the AsyncShutdown manager
       AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down providers",
                                                    this.shutdown.bind(this));
 
@@ -596,17 +652,17 @@ var AddonManagerInternal = {
         for (let type in this.startupChanges)
           delete this.startupChanges[type];
       }
 
       gStartupComplete = true;
       this.recordTimestamp("AMI_startup_end");
     }
     catch (e) {
-      ERROR("startup failed", e);
+      logger.error("startup failed", e);
       AddonManagerPrivate.recordException("AMI", "startup failed", e);
     }
   },
 
   /**
    * Registers a new AddonProvider.
    *
    * @param  aProvider
@@ -624,17 +680,17 @@ var AddonManagerInternal = {
                                  Cr.NS_ERROR_INVALID_ARG);
 
     this.providers.push(aProvider);
 
     if (aTypes) {
       aTypes.forEach(function(aType) {
         if (!(aType.id in this.types)) {
           if (!VALID_TYPES_REGEXP.test(aType.id)) {
-            WARN("Ignoring invalid type " + aType.id);
+            logger.warn("Ignoring invalid type " + aType.id);
             return;
           }
 
           this.types[aType.id] = {
             type: aType,
             providers: [aProvider]
           };
 
@@ -711,17 +767,17 @@ var AddonManagerInternal = {
 
     let providers = this.providers.slice(0);
     for (let provider of providers) {
       try {
         if (aMethod in provider)
           provider[aMethod].apply(provider, aArgs);
       }
       catch (e) {
-        ERROR("Exception calling provider " + aMethod, e);
+        logger.error("Exception calling provider " + aMethod, e);
       }
     }
   },
 
   /**
    * Calls a method on all registered providers, if the provider implements
    * the method. The called method is expected to return a promise, and
    * callProvidersAsync returns a promise that resolves when every provider
@@ -746,60 +802,60 @@ var AddonManagerInternal = {
         if (aMethod in provider) {
           // Resolve a new promise with the result of the method, to handle both
           // methods that return values (or nothing) and methods that return promises.
           let providerResult = provider[aMethod].apply(provider, aArgs);
           let nextPromise = Promise.resolve(providerResult);
           // Log and swallow the errors from methods that do return promises.
           nextPromise = nextPromise.then(
               null,
-              e => ERROR("Exception calling provider " + aMethod, e));
+              e => logger.error("Exception calling provider " + aMethod, e));
           allProviders.push(nextPromise);
         }
       }
       catch (e) {
-        ERROR("Exception calling provider " + aMethod, e);
+        logger.error("Exception calling provider " + aMethod, e);
       }
     }
     // Because we use promise.then to catch and log all errors above, Promise.all()
     // will never exit early because of a rejection.
     return Promise.all(allProviders);
   },
 
   /**
    * Shuts down the addon manager and all registered providers, this must clean
    * up everything in order for automated tests to fake restarts.
    * @return Promise{null} that resolves when all providers and dependent modules
    *                       have finished shutting down
    */
   shutdown: function AMI_shutdown() {
-    LOG("shutdown");
+    logger.debug("shutdown");
     // Clean up listeners
     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
     Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
     Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
     Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
 
     // Only shut down providers if they've been started. Shut down
     // AddonRepository after providers (if any).
     let shuttingDown = null;
     if (gStarted) {
       shuttingDown = this.callProvidersAsync("shutdown")
         .then(null,
-              err => ERROR("Failure during async provider shutdown", err))
+              err => logger.error("Failure during async provider shutdown", err))
         .then(() => AddonRepository.shutdown());
     }
     else {
       shuttingDown = AddonRepository.shutdown();
     }
 
-    shuttingDown.then(val => LOG("Async provider shutdown done"),
-                      err => ERROR("Failure during AddonRepository shutdown", err))
+      shuttingDown.then(val => logger.debug("Async provider shutdown done"),
+                        err => logger.error("Failure during AddonRepository shutdown", err))
       .then(() => {
         this.managerListeners.splice(0, this.managerListeners.length);
         this.installListeners.splice(0, this.installListeners.length);
         this.addonListeners.splice(0, this.addonListeners.length);
         this.typeListeners.splice(0, this.typeListeners.length);
         for (let type in this.startupChanges)
           delete this.startupChanges[type];
         gStarted = false;
@@ -1083,17 +1139,17 @@ var AddonManagerInternal = {
 
           // If the available version isn't newer than the last installed
           // version then ignore it.
           if (Services.vc.compare(hotfixVersion, update.version) >= 0) {
             notifyComplete();
             return;
           }
 
-          LOG("Downloading hotfix version " + update.version);
+          logger.debug("Downloading hotfix version " + update.version);
           AddonManager.getInstallForURL(update.updateURL,
                                        function BUC_getInstallForURL(aInstall) {
             aInstall.addListener({
               onDownloadEnded: function BUC_onDownloadEnded(aInstall) {
                 try {
                   if (!Services.prefs.getBoolPref(PREF_EM_CERT_CHECKATTRIBUTES))
                     return;
                 }
@@ -1102,17 +1158,17 @@ var AddonManagerInternal = {
                   return;
                 }
 
                 try {
                   CertUtils.validateCert(aInstall.certificate,
                                          CertUtils.readCertPrefs(PREF_EM_HOTFIX_CERTS));
                 }
                 catch (e) {
-                  WARN("The hotfix add-on was not signed by the expected " +
+                  logger.warn("The hotfix add-on was not signed by the expected " +
                        "certificate and so will not be installed.");
                   aInstall.cancel();
                 }
               },
 
               onInstallEnded: function BUC_onInstallEnded(aInstall) {
                 // Remember the last successfully installed version.
                 Services.prefs.setCharPref(PREF_EM_HOTFIX_LASTVERSION,
@@ -1219,17 +1275,17 @@ var AddonManagerInternal = {
 
     let managerListeners = this.managerListeners.slice(0);
     for (let listener of managerListeners) {
       try {
         if (aMethod in listener)
           listener[aMethod].apply(listener, aArgs);
       }
       catch (e) {
-        WARN("AddonManagerListener threw exception when calling " + aMethod, e);
+        logger.warn("AddonManagerListener threw exception when calling " + aMethod, e);
       }
     }
   },
 
   /**
    * Calls all registered InstallListeners with an event. Any parameters after
    * the extraListeners parameter are passed to the listener.
    *
@@ -1263,17 +1319,17 @@ var AddonManagerInternal = {
     for (let listener of listeners) {
       try {
         if (aMethod in listener) {
           if (listener[aMethod].apply(listener, aArgs) === false)
             result = false;
         }
       }
       catch (e) {
-        WARN("InstallListener threw exception when calling " + aMethod, e);
+        logger.warn("InstallListener threw exception when calling " + aMethod, e);
       }
     }
     return result;
   },
 
   /**
    * Calls all registered AddonListeners with an event. Any parameters after
    * the method parameter are passed to the listener.
@@ -1292,17 +1348,17 @@ var AddonManagerInternal = {
 
     let addonListeners = this.addonListeners.slice(0);
     for (let listener of addonListeners) {
       try {
         if (aMethod in listener)
           listener[aMethod].apply(listener, aArgs);
       }
       catch (e) {
-        WARN("AddonListener threw exception when calling " + aMethod, e);
+        logger.warn("AddonListener threw exception when calling " + aMethod, e);
       }
     }
   },
 
   /**
    * Notifies all providers that an add-on has been enabled when that type of
    * add-on only supports a single add-on being enabled at a time. This allows
    * the providers to disable theirs if necessary.
@@ -1671,17 +1727,17 @@ var AddonManagerInternal = {
       throw Components.Exception("aURI must be a nsIURI or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!Array.isArray(aInstalls))
       throw Components.Exception("aInstalls must be an array",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
-      WARN("No web installer available, cancelling all installs");
+      logger.warn("No web installer available, cancelling all installs");
       aInstalls.forEach(function(aInstall) {
         aInstall.cancel();
       });
       return;
     }
 
     try {
       let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
@@ -1705,17 +1761,17 @@ var AddonManagerInternal = {
           aInstall.install();
         });
       }
     }
     catch (e) {
       // In the event that the weblistener throws during instantiation or when
       // calling onWebInstallBlocked or onWebInstallRequested all of the
       // installs should get cancelled.
-      WARN("Failure calling web installer", e);
+      logger.warn("Failure calling web installer", e);
       aInstalls.forEach(function(aInstall) {
         aInstall.cancel();
       });
     }
   },
 
   /**
    * Adds a new InstallListener if the listener is not already registered.
--- a/toolkit/mozapps/extensions/DeferredSave.jsm
+++ b/toolkit/mozapps/extensions/DeferredSave.jsm
@@ -14,16 +14,78 @@ Cu.import("resource://gre/modules/Promis
 // Make it possible to mock out timers for testing
 let MakeTimer = () => Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
 this.EXPORTED_SYMBOLS = ["DeferredSave"];
 
 // If delay parameter is not provided, default is 50 milliseconds.
 const DEFAULT_SAVE_DELAY_MS = 50;
 
+Cu.import("resource://gre/modules/Log.jsm");
+//Configure a logger at the parent 'DeferredSave' level to format
+//messages for all the modules under DeferredSave.*
+const DEFERREDSAVE_PARENT_LOGGER_ID = "DeferredSave";
+let parentLogger = Log.repository.getLogger(DEFERREDSAVE_PARENT_LOGGER_ID);
+parentLogger.level = Log.Level.Warn;
+let formatter = new Log.BasicFormatter();
+//Set parent logger (and its children) to append to
+//the Javascript section of the Browser Console
+parentLogger.addAppender(new Log.ConsoleAppender(formatter));
+//Set parent logger (and its children) to
+//also append to standard out
+parentLogger.addAppender(new Log.DumpAppender(formatter));
+
+//Provide the ability to enable/disable logging
+//messages at runtime.
+//If the "extensions.logging.enabled" preference is
+//missing or 'false', messages at the WARNING and higher
+//severity should be logged to the JS console and standard error.
+//If "extensions.logging.enabled" is set to 'true', messages
+//at DEBUG and higher should go to JS console and standard error.
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+/**
+* Preference listener which listens for a change in the
+* "extensions.logging.enabled" preference and changes the logging level of the
+* parent 'addons' level logger accordingly.
+*/
+var PrefObserver = {
+ init: function PrefObserver_init() {
+   Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
+   Services.obs.addObserver(this, "xpcom-shutdown", false);
+   this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
+ },
+
+ observe: function PrefObserver_observe(aSubject, aTopic, aData) {
+   if (aTopic == "xpcom-shutdown") {
+     Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
+     Services.obs.removeObserver(this, "xpcom-shutdown");
+   }
+   else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
+     let debugLogEnabled = false;
+     try {
+       debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
+     }
+     catch (e) {
+     }
+     if (debugLogEnabled) {
+       parentLogger.level = Log.Level.Debug;
+     }
+     else {
+       parentLogger.level = Log.Level.Warn;
+     }
+   }
+ }
+};
+
+PrefObserver.init();
+
 /**
  * A module to manage deferred, asynchronous writing of data files
  * to disk. Writing is deferred by waiting for a specified delay after
  * a request to save the data, before beginning to write. If more than
  * one save request is received during the delay, all requests are
  * fulfilled by a single write.
  *
  * @constructor
@@ -37,20 +99,21 @@ const DEFAULT_SAVE_DELAY_MS = 50;
  *        If aDataProvider returns a String the data are UTF-8 encoded
  *        and then written to the file.
  * @param [optional] aDelay
  *        The delay in milliseconds between the first saveChanges() call
  *        that marks the data as needing to be saved, and when the DeferredSave
  *        begins writing the data to disk. Default 50 milliseconds.
  */
 this.DeferredSave = function (aPath, aDataProvider, aDelay) {
-  // Set up loggers for this instance of DeferredSave
+  // Create a new logger (child of 'DeferredSave' logger)
+  // for use by this particular instance of DeferredSave object
   let leafName = OS.Path.basename(aPath);
-  Cu.import("resource://gre/modules/addons/AddonLogging.jsm");
-  LogManager.getLogger("DeferredSave/" + leafName, this);
+  let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName;
+  this.logger = Log.repository.getLogger(logger_id);
 
   // @type {Deferred|null}, null when no data needs to be written
   // @resolves with the result of OS.File.writeAtomic when all writes complete
   // @rejects with the error from OS.File.writeAtomic if the write fails,
   //          or with the error from aDataProvider() if that throws.
   this._pending = null;
 
   // @type {Promise}, completes when the in-progress write (if any) completes,
@@ -100,33 +163,33 @@ this.DeferredSave.prototype = {
   },
 
   // Start the pending timer if data is dirty
   _startTimer: function() {
     if (!this._pending) {
       return;
     }
 
-    this.LOG("Starting timer");
+      this.logger.debug("Starting timer");
     if (!this._timer)
       this._timer = MakeTimer();
     this._timer.initWithCallback(() => this._deferredSave(),
                                  this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   /**
    * Mark the current stored data dirty, and schedule a flush to disk
    * @return A Promise<integer> that will be resolved after the data is written to disk;
    *         the promise is resolved with the number of bytes written.
    */
   saveChanges: function() {
-    this.LOG("Save changes");
+      this.logger.debug("Save changes");
     if (!this._pending) {
       if (this.writeInProgress) {
-        this.LOG("Data changed while write in progress");
+          this.logger.debug("Data changed while write in progress");
         this.overlappedSaves++;
       }
       this._pending = Promise.defer();
       // Wait until the most recent write completes or fails (if it hasn't already)
       // and then restart our timer
       this._writing.then(count => this._startTimer(), error => this._startTimer());
     }
     return this._pending.promise;
@@ -140,42 +203,42 @@ this.DeferredSave.prototype = {
 
     // In either the success or the exception handling case, we don't need to handle
     // the error from _writing here; it's already being handled in another then()
     let toSave = null;
     try {
       toSave = this._dataProvider();
     }
     catch(e) {
-      this.ERROR("Deferred save dataProvider failed", e);
+        this.logger.error("Deferred save dataProvider failed", e);
       writing.then(null, error => {})
         .then(count => {
           pending.reject(e);
         });
       return;
     }
 
     writing.then(null, error => {return 0;})
     .then(count => {
-      this.LOG("Starting write");
+        this.logger.debug("Starting write");
       this.totalSaves++;
       this.writeInProgress = true;
 
       OS.File.writeAtomic(this._path, toSave, {tmpPath: this._path + ".tmp"})
       .then(
         result => {
           this._lastError = null;
           this.writeInProgress = false;
-          this.LOG("Write succeeded");
+              this.logger.debug("Write succeeded");
           pending.resolve(result);
         },
         error => {
           this._lastError = error;
           this.writeInProgress = false;
-          this.WARN("Write failed", error);
+              this.logger.warn("Write failed", error);
           pending.reject(error);
         });
     });
   },
 
   /**
    * Immediately save the dirty data to disk, skipping
    * the delay of normal operation. Note that the write
@@ -193,19 +256,19 @@ this.DeferredSave.prototype = {
    *         written. If all in-memory data is clean, completes with the
    *         result of the most recent write.
    */
   flush: function() {
     // If we have pending changes, cancel our timer and set up the write
     // immediately (_deferredSave queues the write for after the most
     // recent write completes, if it hasn't already)
     if (this._pending) {
-      this.LOG("Flush called while data is dirty");
+        this.logger.debug("Flush called while data is dirty");
       if (this._timer) {
         this._timer.cancel();
         this._timer = null;
       }
       this._deferredSave();
     }
 
     return this._writing;
   }
-}
+};
--- a/toolkit/mozapps/extensions/amWebInstallListener.js
+++ b/toolkit/mozapps/extensions/amWebInstallListener.js
@@ -9,39 +9,38 @@
  * confirmation when all the add-ons have been downloaded.
  */
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
+const Cu = Components.utils;
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 
 // Installation can begin from any of these states
 const READY_STATES = [
   AddonManager.STATE_AVAILABLE,
   AddonManager.STATE_DOWNLOAD_FAILED,
   AddonManager.STATE_INSTALL_FAILED,
   AddonManager.STATE_CANCELLED
 ];
 
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  this.__defineGetter__(aName, function logFuncGetter() {
-    Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.weblistener";
 
-    LogManager.getLogger("addons.weblistener", this);
-    return this[aName];
-  });
-}, this);
+// Create a new logger for use by the Addons Web Listener
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 function notifyObservers(aTopic, aWindow, aUri, aInstalls) {
   let info = {
     originatingWindow: aWindow,
     originatingURI: aUri,
     installs: aInstalls,
 
     QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
@@ -124,17 +123,17 @@ Installer.prototype = {
               installs.push(aInstall);
           }, this);
         }
         break;
       case AddonManager.STATE_CANCELLED:
         // Just ignore cancelled downloads
         break;
       default:
-        WARN("Download of " + install.sourceURI.spec + " in unexpected state " +
+        logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
              install.state);
       }
     }
 
     this.isDownloading = false;
     this.downloads = installs;
 
     if (failed.length > 0) {
--- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -56,24 +56,22 @@ const BLANK_DB = function() {
   return {
     addons: new Map(),
     schema: DB_SCHEMA
   };
 }
 
 const TOOLKIT_ID     = "toolkit@mozilla.org";
 
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  this.__defineGetter__(aName, function logFuncGetter() {
-    Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.repository";
 
-    LogManager.getLogger("addons.repository", this);
-    return this[aName];
-  });
-}, this);
+// Create a new logger for use by the Addons Repository
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 // A map between XML keys to AddonSearchResult keys for string values
 // that require no extra parsing from XML
 const STRING_KEY_MAP = {
   name:               "name",
   version:            "version",
   homepage:           "homepageURL",
   support:            "supportURL"
@@ -440,17 +438,17 @@ AddonSearchResult.prototype = {
           case "updateDate":
             json.updateDate = value ? value.getTime() : "";
             break;
 
           default:
             json[property] = value;
         }
       } catch (ex) {
-        WARN("Error writing property value for " + property);
+        logger.warn("Error writing property value for " + property);
       }
     }
 
     for (let [property, value] of Iterator(this._unsupportedProperties)) {
       if (!property.startsWith("_"))
         json[property] = value;
     }
 
@@ -479,17 +477,17 @@ this.AddonRepository = {
     if (!AddonDatabase.databaseOk)
       return false;
 
     let preference = PREF_GETADDONS_CACHE_ENABLED;
     let enabled = false;
     try {
       enabled = Services.prefs.getBoolPref(preference);
     } catch(e) {
-      WARN("cacheEnabled: Couldn't get pref: " + preference);
+      logger.warn("cacheEnabled: Couldn't get pref: " + preference);
     }
 
     return enabled;
   },
 
   // A cache of the add-ons stored in the database
   _addons: null,
 
@@ -634,17 +632,17 @@ this.AddonRepository = {
 
       self._beginGetAddons(aAddons, {
         searchSucceeded: function repopulateCacheInternal_searchSucceeded(aAddons) {
           self._addons = {};
           aAddons.forEach(function(aAddon) { self._addons[aAddon.id] = aAddon; });
           AddonDatabase.repopulate(aAddons, aCallback);
         },
         searchFailed: function repopulateCacheInternal_searchFailed() {
-          WARN("Search failed when repopulating cache");
+          logger.warn("Search failed when repopulating cache");
           if (aCallback)
             aCallback();
         }
       }, aSendPerformance, aTimeout);
     });
   },
 
   /**
@@ -674,17 +672,17 @@ this.AddonRepository = {
       }
 
       self.getAddonsByIDs(aAddons, {
         searchSucceeded: function cacheAddons_searchSucceeded(aAddons) {
           aAddons.forEach(function(aAddon) { self._addons[aAddon.id] = aAddon; });
           AddonDatabase.insertAddons(aAddons, aCallback);
         },
         searchFailed: function cacheAddons_searchFailed() {
-          WARN("Search failed when adding add-ons to cache");
+          logger.warn("Search failed when adding add-ons to cache");
           if (aCallback)
             aCallback();
         }
       });
     });
   },
 
   /**
@@ -1062,17 +1060,17 @@ this.AddonRepository = {
               break;
             case 2:
               addon.type = "theme";
               break;
             case 3:
               addon.type = "dictionary";
               break;
             default:
-              WARN("Unknown type id when parsing addon: " + id);
+              logger.warn("Unknown type id when parsing addon: " + id);
           }
           break;
         case "authors":
           let authorNodes = node.getElementsByTagName("author");
           for (let authorNode of authorNodes) {
             let name = self._getDescendantTextContent(authorNode, "name");
             let link = self._getDescendantTextContent(authorNode, "link");
             if (name == null || link == null)
@@ -1307,17 +1305,17 @@ this.AddonRepository = {
       }
     });
   },
 
   // Parses addon_compatibility nodes, that describe compatibility overrides.
   _parseAddonCompatElement: function AddonRepo_parseAddonCompatElement(aResultObj, aElement) {
     let guid = this._getDescendantTextContent(aElement, "guid");
     if (!guid) {
-        LOG("Compatibility override is missing guid.");
+        logger.debug("Compatibility override is missing guid.");
       return;
     }
 
     let compat = {id: guid};
     compat.hosted = aElement.getAttribute("hosted") != "false";
 
     function findMatchingAppRange(aNodes) {
       let toolkitAppRange = null;
@@ -1343,38 +1341,38 @@ this.AddonRepository = {
       }
       return toolkitAppRange;
     }
 
     function parseRangeNode(aNode) {
       let type = aNode.getAttribute("type");
       // Only "incompatible" (blacklisting) is supported for now.
       if (type != "incompatible") {
-        LOG("Compatibility override of unsupported type found.");
+        logger.debug("Compatibility override of unsupported type found.");
         return null;
       }
 
       let override = new AddonManagerPrivate.AddonCompatibilityOverride(type);
 
       override.minVersion = this._getDirectDescendantTextContent(aNode, "min_version");
       override.maxVersion = this._getDirectDescendantTextContent(aNode, "max_version");
 
       if (!override.minVersion) {
-        LOG("Compatibility override is missing min_version.");
+        logger.debug("Compatibility override is missing min_version.");
         return null;
       }
       if (!override.maxVersion) {
-        LOG("Compatibility override is missing max_version.");
+        logger.debug("Compatibility override is missing max_version.");
         return null;
       }
 
       let appRanges = aNode.querySelectorAll("compatible_applications > application");
       let appRange = findMatchingAppRange.bind(this)(appRanges);
       if (!appRange) {
-        LOG("Compatibility override is missing a valid application range.");
+        logger.debug("Compatibility override is missing a valid application range.");
         return null;
       }
 
       override.appID = appRange.appID;
       override.appMinVersion = appRange.appMinVersion;
       override.appMaxVersion = appRange.appMaxVersion;
 
       return override;
@@ -1402,17 +1400,17 @@ this.AddonRepository = {
       aCallback.searchFailed();
       return;
     }
 
     this._searching = true;
     this._callback = aCallback;
     this._maxResults = aMaxResults;
 
-    LOG("Requesting " + aURI);
+    logger.debug("Requesting " + aURI);
 
     this._request = new XHRequest();
     this._request.mozBackgroundRequest = true;
     this._request.open("GET", aURI, true);
     this._request.overrideMimeType("text/xml");
     if (aTimeout) {
       this._request.timeout = aTimeout;
     }
@@ -1470,17 +1468,17 @@ this.AddonRepository = {
   },
 
   // Create url from preference, returning null if preference does not exist
   _formatURLPref: function AddonRepo_formatURLPref(aPreference, aSubstitutions) {
     let url = null;
     try {
       url = Services.prefs.getCharPref(aPreference);
     } catch(e) {
-      WARN("_formatURLPref: Couldn't get pref: " + aPreference);
+      logger.warn("_formatURLPref: Couldn't get pref: " + aPreference);
       return null;
     }
 
     url = url.replace(/%([A-Z_]+)%/g, function urlSubstitution(aMatch, aKey) {
       return (aKey in aSubstitutions) ? aSubstitutions[aKey] : aMatch;
     });
 
     return Services.urlFormatter.formatURL(url);
@@ -1570,17 +1568,17 @@ var AddonDatabase = {
      schema = parseInt(inputDB.schema, 10);
 
      if (!Number.isInteger(schema) ||
          schema < DB_MIN_JSON_SCHEMA) {
        throw new Error("Invalid schema value.");
      }
 
     } catch (e if e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
-      LOG("No " + FILE_DATABASE + " found.");
+      logger.debug("No " + FILE_DATABASE + " found.");
 
       // Create a blank addons.json file
       this._saveDBToDisk();
 
       let dbSchema = 0;
       try {
         dbSchema = Services.prefs.getIntPref(PREF_GETADDONS_DB_SCHEMA);
       } catch (e) {}
@@ -1599,17 +1597,17 @@ var AddonDatabase = {
         });
 
         Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
       }
 
       return;
 
     } catch (e) {
-      ERROR("Malformed " + FILE_DATABASE + ": " + e);
+      logger.error("Malformed " + FILE_DATABASE + ": " + e);
       this.databaseOk = false;
       return;
 
     } finally {
      cstream.close();
      fstream.close();
     }
 
@@ -1670,17 +1668,17 @@ var AddonDatabase = {
   delete: function AD_delete(aCallback) {
     this.DB = BLANK_DB();
 
     this.Writer.flush()
       .then(null, () => {})
       // shutdown(true) never rejects
       .then(() => this.shutdown(true))
       .then(() => OS.File.remove(this.jsonFile.path, {}))
-      .then(null, error => ERROR("Unable to delete Addon Repository file " +
+      .then(null, error => logger.error("Unable to delete Addon Repository file " +
                                  this.jsonFile.path, error))
       .then(aCallback);
   },
 
   toJSON: function AD_toJSON() {
     let json = {
       schema: this.DB.schema,
       addons: []
@@ -1861,17 +1859,17 @@ var AddonDatabase = {
 
           case "iconURL":
             break;
 
           default:
             addon[expectedProperty] = value;
         }
       } catch (ex) {
-        WARN("Error in parsing property value for " + expectedProperty + " | " + ex);
+        logger.warn("Error in parsing property value for " + expectedProperty + " | " + ex);
       }
 
       // delete property from obj to indicate we've already
       // handled it. The remaining public properties will
       // be stored separately and just passed through to
       // be written back to the DB.
       delete aObj[expectedProperty];
     }
@@ -1905,17 +1903,17 @@ var AddonDatabase = {
    * the DB_BATCH_TIMEOUT_MS timeout.
    *
    * @return Promise A promise that resolves after the
    *                 write to disk has completed.
    */
   _saveDBToDisk: function() {
     return this.Writer.saveChanges().then(
       function() Services.obs.notifyObservers(null, DB_DATA_WRITTEN_TOPIC, null),
-      ERROR);
+      logger.error);
   },
 
   /**
    * Make a developer object from a vanilla
    * JS object from the JSON database
    *
    * @param  aObj
    *         The JS object to use
--- a/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
@@ -1,44 +1,41 @@
 /* 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/. */
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cu = Components.utils;
 
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
-Components.utils.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
 
 const KEY_PROFILEDIR  = "ProfD";
 const FILE_DATABASE   = "addons.sqlite";
 const LAST_DB_SCHEMA   = 4;
 
 // Add-on properties present in the columns of the database
 const PROP_SINGLE = ["id", "type", "name", "version", "creator", "description",
                      "fullDescription", "developerComments", "eula",
                      "homepageURL", "supportURL", "contributionURL",
                      "contributionAmount", "averageRating", "reviewCount",
                      "reviewURL", "totalDownloads", "weeklyDownloads",
                      "dailyUsers", "sourceURI", "repositoryStatus", "size",
                      "updateDate"];
 
-
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  this.__defineGetter__(aName, function logFuncGetter() {
-    Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.repository.sqlmigrator";
 
-    LogManager.getLogger("addons.repository.sqlmigrator", this);
-    return this[aName];
-  });
-}, this);
-
+// Create a new logger for use by the Addons Repository SQL Migrator
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 this.EXPORTED_SYMBOLS = ["AddonRepository_SQLiteMigrator"];
 
 
 this.AddonRepository_SQLiteMigrator = {
 
   /**
    * Migrates data from a previous SQLite version of the
@@ -54,22 +51,22 @@ this.AddonRepository_SQLiteMigrator = {
    */
   migrate: function(aCallback) {
     if (!this._openConnection()) {
       this._closeConnection();
       aCallback([]);
       return false;
     }
 
-    LOG("Importing addon repository from previous " + FILE_DATABASE + " storage.");
+    logger.debug("Importing addon repository from previous " + FILE_DATABASE + " storage.");
 
     this._retrieveStoredData((results) => {
       this._closeConnection();
       let resultArray = [addon for ([,addon] of Iterator(results))];
-      LOG(resultArray.length + " addons imported.")
+      logger.debug(resultArray.length + " addons imported.")
       aCallback(resultArray);
     });
 
     return true;
   },
 
   /**
    * Synchronously opens a new connection to the database file.
@@ -95,51 +92,51 @@ this.AddonRepository_SQLiteMigrator = {
     try {
       this.connection.beginTransaction();
 
       switch (this.connection.schemaVersion) {
         case 0:
           return false;
 
         case 1:
-          LOG("Upgrading database schema to version 2");
+          logger.debug("Upgrading database schema to version 2");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN width INTEGER");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN height INTEGER");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailWidth INTEGER");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailHeight INTEGER");
         case 2:
-          LOG("Upgrading database schema to version 3");
+          logger.debug("Upgrading database schema to version 3");
           this.connection.createTable("compatibility_override",
                                       "addon_internal_id INTEGER, " +
                                       "num INTEGER, " +
                                       "type TEXT, " +
                                       "minVersion TEXT, " +
                                       "maxVersion TEXT, " +
                                       "appID TEXT, " +
                                       "appMinVersion TEXT, " +
                                       "appMaxVersion TEXT, " +
                                       "PRIMARY KEY (addon_internal_id, num)");
         case 3:
-          LOG("Upgrading database schema to version 4");
+          logger.debug("Upgrading database schema to version 4");
           this.connection.createTable("icon",
                                       "addon_internal_id INTEGER, " +
                                       "size INTEGER, " +
                                       "url TEXT, " +
                                       "PRIMARY KEY (addon_internal_id, size)");
           this._createIndices();
           this._createTriggers();
           this.connection.schemaVersion = LAST_DB_SCHEMA;
         case LAST_DB_SCHEMA:
           break;
         default:
           return false;
       }
       this.connection.commitTransaction();
     } catch (e) {
-      ERROR("Failed to open " + FILE_DATABASE + ". Data import will not happen.", e);
+      logger.error("Failed to open " + FILE_DATABASE + ". Data import will not happen.", e);
       this.logSQLError(this.connection.lastError, this.connection.lastErrorString);
       this.connection.rollbackTransaction();
       return false;
     }
 
     return true;
   },
 
@@ -175,17 +172,17 @@ this.AddonRepository_SQLiteMigrator = {
             addons[internal_id] = self._makeAddonFromAsyncRow(row);
           }
         },
 
         handleError: self.asyncErrorLogger,
 
         handleCompletion: function getAllAddons_handleCompletion(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-            ERROR("Error retrieving add-ons from database. Returning empty results");
+            logger.error("Error retrieving add-ons from database. Returning empty results");
             aCallback({});
             return;
           }
 
           getAllDevelopers();
         }
       });
     }
@@ -193,33 +190,33 @@ this.AddonRepository_SQLiteMigrator = {
     // Retrieve all data from the developer table
     function getAllDevelopers() {
       self.getAsyncStatement("getAllDevelopers").executeAsync({
         handleResult: function getAllDevelopers_handleResult(aResults) {
           let row = null;
           while ((row = aResults.getNextRow())) {
             let addon_internal_id = row.getResultByName("addon_internal_id");
             if (!(addon_internal_id in addons)) {
-              WARN("Found a developer not linked to an add-on in database");
+              logger.warn("Found a developer not linked to an add-on in database");
               continue;
             }
 
             let addon = addons[addon_internal_id];
             if (!addon.developers)
               addon.developers = [];
 
             addon.developers.push(self._makeDeveloperFromAsyncRow(row));
           }
         },
 
         handleError: self.asyncErrorLogger,
 
         handleCompletion: function getAllDevelopers_handleCompletion(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-            ERROR("Error retrieving developers from database. Returning empty results");
+            logger.error("Error retrieving developers from database. Returning empty results");
             aCallback({});
             return;
           }
 
           getAllScreenshots();
         }
       });
     }
@@ -227,97 +224,97 @@ this.AddonRepository_SQLiteMigrator = {
     // Retrieve all data from the screenshot table
     function getAllScreenshots() {
       self.getAsyncStatement("getAllScreenshots").executeAsync({
         handleResult: function getAllScreenshots_handleResult(aResults) {
           let row = null;
           while ((row = aResults.getNextRow())) {
             let addon_internal_id = row.getResultByName("addon_internal_id");
             if (!(addon_internal_id in addons)) {
-              WARN("Found a screenshot not linked to an add-on in database");
+              logger.warn("Found a screenshot not linked to an add-on in database");
               continue;
             }
 
             let addon = addons[addon_internal_id];
             if (!addon.screenshots)
               addon.screenshots = [];
             addon.screenshots.push(self._makeScreenshotFromAsyncRow(row));
           }
         },
 
         handleError: self.asyncErrorLogger,
 
         handleCompletion: function getAllScreenshots_handleCompletion(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-            ERROR("Error retrieving screenshots from database. Returning empty results");
+            logger.error("Error retrieving screenshots from database. Returning empty results");
             aCallback({});
             return;
           }
 
           getAllCompatOverrides();
         }
       });
     }
 
     function getAllCompatOverrides() {
       self.getAsyncStatement("getAllCompatOverrides").executeAsync({
         handleResult: function getAllCompatOverrides_handleResult(aResults) {
           let row = null;
           while ((row = aResults.getNextRow())) {
             let addon_internal_id = row.getResultByName("addon_internal_id");
             if (!(addon_internal_id in addons)) {
-              WARN("Found a compatibility override not linked to an add-on in database");
+              logger.warn("Found a compatibility override not linked to an add-on in database");
               continue;
             }
 
             let addon = addons[addon_internal_id];
             if (!addon.compatibilityOverrides)
               addon.compatibilityOverrides = [];
             addon.compatibilityOverrides.push(self._makeCompatOverrideFromAsyncRow(row));
           }
         },
 
         handleError: self.asyncErrorLogger,
 
         handleCompletion: function getAllCompatOverrides_handleCompletion(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-            ERROR("Error retrieving compatibility overrides from database. Returning empty results");
+            logger.error("Error retrieving compatibility overrides from database. Returning empty results");
             aCallback({});
             return;
           }
 
           getAllIcons();
         }
       });
     }
 
     function getAllIcons() {
       self.getAsyncStatement("getAllIcons").executeAsync({
         handleResult: function getAllIcons_handleResult(aResults) {
           let row = null;
           while ((row = aResults.getNextRow())) {
             let addon_internal_id = row.getResultByName("addon_internal_id");
             if (!(addon_internal_id in addons)) {
-              WARN("Found an icon not linked to an add-on in database");
+              logger.warn("Found an icon not linked to an add-on in database");
               continue;
             }
 
             let addon = addons[addon_internal_id];
             let { size, url } = self._makeIconFromAsyncRow(row);
             addon.icons[size] = url;
             if (size == 32)
               addon.iconURL = url;
           }
         },
 
         handleError: self.asyncErrorLogger,
 
         handleCompletion: function getAllIcons_handleCompletion(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-            ERROR("Error retrieving icons from database. Returning empty results");
+            logger.error("Error retrieving icons from database. Returning empty results");
             aCallback({});
             return;
           }
 
           let returnedAddons = {};
           for each (let addon in addons)
             returnedAddons[addon.id] = addon;
           aCallback(returnedAddons);
@@ -344,17 +341,17 @@ this.AddonRepository_SQLiteMigrator = {
   getAsyncStatement: function AD_getAsyncStatement(aKey) {
     if (aKey in this.asyncStatementsCache)
       return this.asyncStatementsCache[aKey];
 
     let sql = this.queries[aKey];
     try {
       return this.asyncStatementsCache[aKey] = this.connection.createAsyncStatement(sql);
     } catch (e) {
-      ERROR("Error creating statement " + aKey + " (" + sql + ")");
+      logger.error("Error creating statement " + aKey + " (" + sql + ")");
       throw Components.Exception("Error creating statement " + aKey + " (" + sql + "): " + e,
                                  e.result);
     }
   },
 
   // The queries used by the database
   queries: {
     getAllAddons: "SELECT internal_id, id, type, name, version, " +
@@ -473,27 +470,27 @@ this.AddonRepository_SQLiteMigrator = {
    * A helper function to log an SQL error.
    *
    * @param  aError
    *         The storage error code associated with the error
    * @param  aErrorString
    *         An error message
    */
   logSQLError: function AD_logSQLError(aError, aErrorString) {
-    ERROR("SQL error " + aError + ": " + aErrorString);
+    logger.error("SQL error " + aError + ": " + aErrorString);
   },
 
   /**
    * A helper function to log any errors that occur during async statements.
    *
    * @param  aError
    *         A mozIStorageError to log
    */
   asyncErrorLogger: function AD_asyncErrorLogger(aError) {
-    ERROR("Async SQL error " + aError.result + ": " + aError.message);
+    logger.error("Async SQL error " + aError.result + ": " + aError.message);
   },
 
   /**
    * Synchronously creates the triggers in the database.
    */
   _createTriggers: function AD__createTriggers() {
     this.connection.executeSimpleSQL("DROP TRIGGER IF EXISTS delete_addon");
     this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " +
--- a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
@@ -39,25 +39,22 @@ XPCOMUtils.defineLazyGetter(this, "CertU
   let certUtils = {};
   Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
   return certUtils;
 });
 
 var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
            getService(Ci.nsIRDFService);
 
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  this.__defineGetter__(aName, function logFuncGetter() {
-    Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.updates";
 
-    LogManager.getLogger("addons.updates", this);
-    return this[aName];
-  });
-}, this);
-
+// Create a new logger for use by the Addons Update Checker
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 /**
  * A serialisation method for RDF data that produces an identical string
  * for matching RDF graphs.
  * The serialisation is not complete, only assertions stemming from a given
  * resource are included, multiple references to the same resource are not
  * permitted, and the RDF prolog and epilog are not included.
  * RDF Blob and Date literals are not supported.
@@ -304,17 +301,17 @@ function parseRDFManifest(aId, aUpdateKe
       throw Components.Exception("The signature for " + aId + " was not created by the add-on's updateKey");
   }
 
   let updates = ds.GetTarget(addonRes, EM_R("updates"), true);
 
   // A missing updates property doesn't count as a failure, just as no avialable
   // update information
   if (!updates) {
-    WARN("Update manifest for " + aId + " did not contain an updates property");
+    logger.warn("Update manifest for " + aId + " did not contain an updates property");
     return [];
   }
 
   if (!(updates instanceof Ci.nsIRDFResource))
     throw Components.Exception("Missing updates property for " + addonRes.Value);
 
   let cu = Cc["@mozilla.org/rdf/container-utils;1"].
            getService(Ci.nsIRDFContainerUtils);
@@ -325,51 +322,51 @@ function parseRDFManifest(aId, aUpdateKe
   let ctr = Cc["@mozilla.org/rdf/container;1"].
             createInstance(Ci.nsIRDFContainer);
   ctr.Init(ds, updates);
   let items = ctr.GetElements();
   while (items.hasMoreElements()) {
     let item = items.getNext().QueryInterface(Ci.nsIRDFResource);
     let version = getProperty(ds, item, "version");
     if (!version) {
-      WARN("Update manifest is missing a required version property.");
+      logger.warn("Update manifest is missing a required version property.");
       continue;
     }
 
-    LOG("Found an update entry for " + aId + " version " + version);
+    logger.debug("Found an update entry for " + aId + " version " + version);
 
     let targetApps = ds.GetTargets(item, EM_R("targetApplication"), true);
     while (targetApps.hasMoreElements()) {
       let targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
 
       let appEntry = {};
       try {
         appEntry.id = getRequiredProperty(ds, targetApp, "id");
         appEntry.minVersion = getRequiredProperty(ds, targetApp, "minVersion");
         appEntry.maxVersion = getRequiredProperty(ds, targetApp, "maxVersion");
       }
       catch (e) {
-        WARN(e);
+        logger.warn(e);
         continue;
       }
 
       let result = {
         id: aId,
         version: version,
         updateURL: getProperty(ds, targetApp, "updateLink"),
         updateHash: getProperty(ds, targetApp, "updateHash"),
         updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
         strictCompatibility: getProperty(ds, targetApp, "strictCompatibility") == "true",
         targetApplications: [appEntry]
       };
 
       if (result.updateURL && AddonManager.checkUpdateSecurity &&
           result.updateURL.substring(0, 6) != "https:" &&
           (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) {
-        WARN("updateLink " + result.updateURL + " is not secure and is not verified" +
+        logger.warn("updateLink " + result.updateURL + " is not secure and is not verified" +
              " by a strong enough hash (needs to be sha1 or stronger).");
         delete result.updateURL;
         delete result.updateHash;
       }
       results.push(result);
     }
   }
   return results;
@@ -396,17 +393,17 @@ function UpdateParser(aId, aUpdateKey, a
 
   let requireBuiltIn = true;
   try {
     requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
   }
   catch (e) {
   }
 
-  LOG("Requesting " + aUrl);
+  logger.debug("Requesting " + aUrl);
   try {
     this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                    createInstance(Ci.nsIXMLHttpRequest);
     this.request.open("GET", this.url, true);
     this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
     this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
     // Prevent the request from writing to cache.
     this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
@@ -414,17 +411,17 @@ function UpdateParser(aId, aUpdateKey, a
     this.request.timeout = TIMEOUT;
     var self = this;
     this.request.addEventListener("load", function loadEventListener(event) { self.onLoad() }, false);
     this.request.addEventListener("error", function errorEventListener(event) { self.onError() }, false);
     this.request.addEventListener("timeout", function timeoutEventListener(event) { self.onTimeout() }, false);
     this.request.send(null);
   }
   catch (e) {
-    ERROR("Failed to request update manifest", e);
+    logger.error("Failed to request update manifest", e);
   }
 }
 
 UpdateParser.prototype = {
   id: null,
   updateKey: null,
   observer: null,
   request: null,
@@ -448,110 +445,110 @@ UpdateParser.prototype = {
       CertUtils.checkCert(request.channel, !requireBuiltIn);
     }
     catch (e) {
       this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
       return;
     }
 
     if (!Components.isSuccessCode(request.status)) {
-      WARN("Request failed: " + this.url + " - " + request.status);
+      logger.warn("Request failed: " + this.url + " - " + request.status);
       this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
       return;
     }
 
     let channel = request.channel;
     if (channel instanceof Ci.nsIHttpChannel && !channel.requestSucceeded) {
-      WARN("Request failed: " + this.url + " - " + channel.responseStatus +
+      logger.warn("Request failed: " + this.url + " - " + channel.responseStatus +
            ": " + channel.responseStatusText);
       this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
       return;
     }
 
     let xml = request.responseXML;
     if (!xml || xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR) {
-      WARN("Update manifest was not valid XML");
+      logger.warn("Update manifest was not valid XML");
       this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
       return;
     }
 
     // We currently only know about RDF update manifests
     if (xml.documentElement.namespaceURI == PREFIX_NS_RDF) {
       let results = null;
 
       try {
         results = parseRDFManifest(this.id, this.updateKey, request);
       }
       catch (e) {
-        WARN(e);
+        logger.warn(e);
         this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
         return;
       }
       if ("onUpdateCheckComplete" in this.observer) {
         try {
           this.observer.onUpdateCheckComplete(results);
         }
         catch (e) {
-          WARN("onUpdateCheckComplete notification failed", e);
+          logger.warn("onUpdateCheckComplete notification failed", e);
         }
       }
       return;
     }
 
-    WARN("Update manifest had an unrecognised namespace: " + xml.documentElement.namespaceURI);
+    logger.warn("Update manifest had an unrecognised namespace: " + xml.documentElement.namespaceURI);
     this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT);
   },
 
   /**
    * Called when the request times out
    */
   onTimeout: function() {
     this.request = null;
-    WARN("Request for " + this.url + " timed out");
+    logger.warn("Request for " + this.url + " timed out");
     this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT);
   },
 
   /**
    * Called when the manifest failed to load.
    */
   onError: function UP_onError() {
     if (!Components.isSuccessCode(this.request.status)) {
-      WARN("Request failed: " + this.url + " - " + this.request.status);
+      logger.warn("Request failed: " + this.url + " - " + this.request.status);
     }
     else if (this.request.channel instanceof Ci.nsIHttpChannel) {
       try {
         if (this.request.channel.requestSucceeded) {
-          WARN("Request failed: " + this.url + " - " +
+          logger.warn("Request failed: " + this.url + " - " +
                this.request.channel.responseStatus + ": " +
                this.request.channel.responseStatusText);
         }
       }
       catch (e) {
-        WARN("HTTP Request failed for an unknown reason");
+        logger.warn("HTTP Request failed for an unknown reason");
       }
     }
     else {
-      WARN("Request failed for an unknown reason");
+      logger.warn("Request failed for an unknown reason");
     }
 
     this.request = null;
 
     this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
   },
 
   /**
    * Helper method to notify the observer that an error occured.
    */
   notifyError: function UP_notifyError(aStatus) {
     if ("onUpdateCheckError" in this.observer) {
       try {
         this.observer.onUpdateCheckError(aStatus);
       }
       catch (e) {
-        WARN("onUpdateCheckError notification failed", e);
+        logger.warn("onUpdateCheckError notification failed", e);
       }
     }
   },
 
   /**
    * Called to cancel an in-progress update check.
    */
   cancel: function UP_cancel() {
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -1,34 +1,33 @@
 /* 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/. */
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = [];
 
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 const URI_EXTENSION_STRINGS  = "chrome://mozapps/locale/extensions/extensions.properties";
 const STRING_TYPE_NAME       = "type.%ID%.name";
 const LIST_UPDATED_TOPIC     = "plugins-list-updated";
 
-for (let name of ["LOG", "WARN", "ERROR"]) {
-  this.__defineGetter__(name, function() {
-    Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.plugins";
 
-    LogManager.getLogger("addons.plugins", this);
-    return this[name];
-  });
-}
+// Create a new logger for use by the Addons Plugin Provider
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 function getIDHashForString(aStr) {
   // return the two-digit hexadecimal code for a byte
   function toHexString(charCode)
     ("0" + charCode.toString(16)).slice(-2);
 
   let hasher = Cc["@mozilla.org/security/hash;1"].
                createInstance(Ci.nsICryptoHash);
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -187,28 +187,22 @@ const MSG_JAR_FLUSH = "AddonJarFlush";
 
 var gGlobalScope = this;
 
 /**
  * Valid IDs fit this pattern.
  */
 var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i;
 
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  Object.defineProperty(this, aName, {
-    get: function logFuncGetter() {
-      Components.utils.import("resource://gre/modules/addons/AddonLogging.jsm");
-
-      LogManager.getLogger("addons.xpi", this);
-      return this[aName];
-    },
-    configurable: true
-  });
-}, this);
-
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.xpi";
+
+// Create a new logger for use by all objects in this Addons XPI Provider module
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 const LAZY_OBJECTS = ["XPIDatabase"];
 
 var gLazyObjectsLoaded = false;
 
 function loadLazyObjects() {
   let scope = {};
   scope.AddonInternal = AddonInternal;
@@ -256,17 +250,17 @@ function findMatchingStaticBlocklistItem
  * @param  aPermissions
  *         The permisions to set
  */
 function setFilePermissions(aFile, aPermissions) {
   try {
     aFile.permissions = aPermissions;
   }
   catch (e) {
-    WARN("Failed to set permissions " + aPermissions.toString(8) + " on " +
+    logger.warn("Failed to set permissions " + aPermissions.toString(8) + " on " +
          aFile.path, e);
   }
 }
 
 /**
  * A safe way to install a file or the contents of a directory to a new
  * directory. The file or directory is moved or copied recursively and if
  * anything fails an attempt is made to rollback the entire operation. The
@@ -290,63 +284,63 @@ SafeInstallOperation.prototype = {
     let newFile = aFile.clone();
     try {
       if (aCopy)
         newFile.copyTo(aTargetDirectory, null);
       else
         newFile.moveTo(aTargetDirectory, null);
     }
     catch (e) {
-      ERROR("Failed to " + (aCopy ? "copy" : "move") + " file " + aFile.path +
+      logger.error("Failed to " + (aCopy ? "copy" : "move") + " file " + aFile.path +
             " to " + aTargetDirectory.path, e);
       throw e;
     }
     this._installedFiles.push({ oldFile: oldFile, newFile: newFile });
   },
 
   _installDirectory: function SIO_installDirectory(aDirectory, aTargetDirectory, aCopy) {
     let newDir = aTargetDirectory.clone();
     newDir.append(aDirectory.leafName);
     try {
       newDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
     }
     catch (e) {
-      ERROR("Failed to create directory " + newDir.path, e);
+      logger.error("Failed to create directory " + newDir.path, e);
       throw e;
     }
     this._createdDirs.push(newDir);
 
     // Use a snapshot of the directory contents to avoid possible issues with
     // iterating over a directory while removing files from it (the YAFFS2
     // embedded filesystem has this issue, see bug 772238), and to remove
     // normal files before their resource forks on OSX (see bug 733436).
     let entries = getDirectoryEntries(aDirectory, true);
     entries.forEach(function(aEntry) {
       try {
         this._installDirEntry(aEntry, newDir, aCopy);
       }
       catch (e) {
-        ERROR("Failed to " + (aCopy ? "copy" : "move") + " entry " +
+        logger.error("Failed to " + (aCopy ? "copy" : "move") + " entry " +
               aEntry.path, e);
         throw e;
       }
     }, this);
 
     // If this is only a copy operation then there is nothing else to do
     if (aCopy)
       return;
 
     // The directory should be empty by this point. If it isn't this will throw
     // and all of the operations will be rolled back
     try {
       setFilePermissions(aDirectory, FileUtils.PERMS_DIRECTORY);
       aDirectory.remove(false);
     }
     catch (e) {
-      ERROR("Failed to remove directory " + aDirectory.path, e);
+      logger.error("Failed to remove directory " + aDirectory.path, e);
       throw e;
     }
 
     // Note we put the directory move in after all the file moves so the
     // directory is recreated before all the files are moved back
     this._installedFiles.push({ oldFile: aDirectory, newFile: newDir });
   },
 
@@ -358,29 +352,29 @@ SafeInstallOperation.prototype = {
     }
     catch (e) {
       // If the file has already gone away then don't worry about it, this can
       // happen on OSX where the resource fork is automatically moved with the
       // data fork for the file. See bug 733436.
       if (e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
         return;
 
-      ERROR("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
+      logger.error("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
             " to " + aTargetDirectory.path);
       throw e;
     }
 
     try {
       if (isDir)
         this._installDirectory(aDirEntry, aTargetDirectory, aCopy);
       else
         this._installFile(aDirEntry, aTargetDirectory, aCopy);
     }
     catch (e) {
-      ERROR("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
+      logger.error("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
             " to " + aTargetDirectory.path);
       throw e;
     }
   },
 
   /**
    * Moves a file or directory into a new directory. If an error occurs then all
    * files that have been moved will be moved back to their original location.
@@ -710,29 +704,29 @@ function loadManifestFromRDF(aUri, aStre
   function readLocale(aDs, aSource, isDefault, aSeenLocales) {
     let locale = { };
     if (!isDefault) {
       locale.locales = [];
       let targets = ds.GetTargets(aSource, EM_R("locale"), true);
       while (targets.hasMoreElements()) {
         let localeName = getRDFValue(targets.getNext());
         if (!localeName) {
-          WARN("Ignoring empty locale in localized properties");
+          logger.warn("Ignoring empty locale in localized properties");
           continue;
         }
         if (aSeenLocales.indexOf(localeName) != -1) {
-          WARN("Ignoring duplicate locale in localized properties");
+          logger.warn("Ignoring duplicate locale in localized properties");
           continue;
         }
         aSeenLocales.push(localeName);
         locale.locales.push(localeName);
       }
 
       if (locale.locales.length == 0) {
-        WARN("Ignoring localized properties with no listed locales");
+        logger.warn("Ignoring localized properties with no listed locales");
         return null;
       }
     }
 
     PROP_LOCALE_SINGLE.forEach(function(aProp) {
       locale[aProp] = getRDFProperty(aDs, aSource, aProp);
     });
 
@@ -859,21 +853,21 @@ function loadManifestFromRDF(aUri, aStre
   while (targets.hasMoreElements()) {
     let target = targets.getNext().QueryInterface(Ci.nsIRDFResource);
     let targetAppInfo = {};
     PROP_TARGETAPP.forEach(function(aProp) {
       targetAppInfo[aProp] = getRDFProperty(ds, target, aProp);
     });
     if (!targetAppInfo.id || !targetAppInfo.minVersion ||
         !targetAppInfo.maxVersion) {
-      WARN("Ignoring invalid targetApplication entry in install manifest");
+      logger.warn("Ignoring invalid targetApplication entry in install manifest");
       continue;
     }
     if (seenApplications.indexOf(targetAppInfo.id) != -1) {
-      WARN("Ignoring duplicate targetApplication entry for " + targetAppInfo.id +
+      logger.warn("Ignoring duplicate targetApplication entry for " + targetAppInfo.id +
            " in install manifest");
       continue;
     }
     seenApplications.push(targetAppInfo.id);
     addon.targetApplications.push(targetAppInfo);
   }
 
   // Note that we don't need to check for duplicate targetPlatform entries since
@@ -1162,23 +1156,23 @@ function saveStreamAsync(aPath, aStream,
 
   let data = Uint8Array(EXTRACTION_BUFFER);
 
   function readFailed(error) {
     try {
       aStream.close();
     }
     catch (e) {
-      ERROR("Failed to close JAR stream for " + aPath);
+      logger.error("Failed to close JAR stream for " + aPath);
     }
 
     aFile.close().then(function() {
       deferred.reject(error);
     }, function(e) {
-      ERROR("Failed to close file for " + aPath);
+      logger.error("Failed to close file for " + aPath);
       deferred.reject(error);
     });
   }
 
   function readData() {
     try {
       let count = Math.min(source.available(), data.byteLength);
       source.readArrayBuffer(count, data.buffer);
@@ -1234,31 +1228,31 @@ function extractFilesAsync(aZipFile, aDi
       let zipentry = zipReader.getEntry(name);
       let path = OS.Path.join(aDir.path, ...name.split("/"));
 
       if (zipentry.isDirectory) {
         try {
           yield OS.File.makeDir(path);
         }
         catch (e) {
-          ERROR("extractFilesAsync: failed to create directory " + path, e);
+          logger.error("extractFilesAsync: failed to create directory " + path, e);
           throw e;
         }
       }
       else {
         let options = { unixMode: zipentry.permissions | FileUtils.PERMS_FILE };
         try {
           let file = yield OS.File.open(path, { truncate: true }, options);
           if (zipentry.realSize == 0)
             yield file.close();
           else
             yield saveStreamAsync(path, zipReader.getInputStream(entryName), file);
         }
         catch (e) {
-          ERROR("extractFilesAsync: failed to extract file " + path, e);
+          logger.error("extractFilesAsync: failed to extract file " + path, e);
           throw e;
         }
       }
     }
 
     zipReader.close();
   }).then(null, (e) => {
     zipReader.close();
@@ -1294,17 +1288,17 @@ function extractFiles(aZipFile, aDir) {
       var entryName = entries.getNext();
       let target = getTargetFile(aDir, entryName);
       if (!target.exists()) {
         try {
           target.create(Ci.nsIFile.DIRECTORY_TYPE,
                         FileUtils.PERMS_DIRECTORY);
         }
         catch (e) {
-          ERROR("extractFiles: failed to create target directory for " +
+          logger.error("extractFiles: failed to create target directory for " +
                 "extraction file = " + target.path, e);
         }
       }
     }
 
     entries = zipReader.findEntries(null);
     while (entries.hasMore()) {
       let entryName = entries.getNext();
@@ -1312,17 +1306,17 @@ function extractFiles(aZipFile, aDir) {
       if (target.exists())
         continue;
 
       zipReader.extract(entryName, target);
       try {
         target.permissions |= FileUtils.PERMS_FILE;
       }
       catch (e) {
-        WARN("Failed to set permissions " + aPermissions.toString(8) + " on " +
+        logger.warn("Failed to set permissions " + aPermissions.toString(8) + " on " +
              target.path, e);
       }
     }
   }
   finally {
     zipReader.close();
   }
 }
@@ -1445,33 +1439,33 @@ function recursiveRemove(aFile) {
                                   : FileUtils.PERMS_FILE);
 
   try {
     aFile.remove(true);
     return;
   }
   catch (e) {
     if (!aFile.isDirectory()) {
-      ERROR("Failed to remove file " + aFile.path, e);
+      logger.error("Failed to remove file " + aFile.path, e);
       throw e;
     }
   }
 
   // Use a snapshot of the directory contents to avoid possible issues with
   // iterating over a directory while removing files from it (the YAFFS2
   // embedded filesystem has this issue, see bug 772238), and to remove
   // normal files before their resource forks on OSX (see bug 733436).
   let entries = getDirectoryEntries(aFile, true);
   entries.forEach(recursiveRemove);
 
   try {
     aFile.remove(true);
   }
   catch (e) {
-    ERROR("Failed to remove empty directory " + aFile.path, e);
+    logger.error("Failed to remove empty directory " + aFile.path, e);
     throw e;
   }
 }
 
 /**
  * Returns the timestamp and leaf file name of the most recently modified
  * entry in a directory,
  * or simply the file's own timestamp if it is not a directory.
@@ -1500,17 +1494,17 @@ function recursiveLastModifiedTime(aFile
           fileName = subName;
         }
       }
       entries.close();
       return [fileName, modTime, totalItems];
     }
   }
   catch (e) {
-    WARN("Problem getting last modified time for " + aFile.path, e);
+    logger.warn("Problem getting last modified time for " + aFile.path, e);
   }
 
   // If the file is something else, just ignore it.
   return ["", 0, 0];
 }
 
 /**
  * Gets a snapshot of directory entries.
@@ -1705,17 +1699,17 @@ function directoryStateDiffers(aState, a
  *         a warning and returns undefined.
  */
 function makeSafe(aFunction) {
   return function(...aArgs) {
     try {
       return aFunction(...aArgs);
     }
     catch(ex) {
-      WARN("XPIProvider callback failed", ex);
+      logger.warn("XPIProvider callback failed", ex);
     }
     return undefined;
   }
 }
 
 var XPIProvider = {
   // An array of known install locations
   installLocations: null,
@@ -1781,17 +1775,17 @@ var XPIProvider = {
   cancelAll: function XPI_cancelAll() {
     // Cancelling one may alter _inProgress, so restart the iterator after each
     while (this._inProgress.size > 0) {
       for (let c of this._inProgress) {
         try {
           c.cancel();
         }
         catch (e) {
-          WARN("Cancel failed", e);
+          logger.warn("Cancel failed", e);
         }
         this._inProgress.delete(c);
       }
     }
   },
 
   /**
    * Adds or updates a URI mapping for an Addon.id.
@@ -1807,17 +1801,17 @@ var XPIProvider = {
       let uri = this._resolveURIToFile(getURIForResourceInFile(aFile, "."));
       if (!uri) {
         throw new Error("Cannot resolve");
       }
       this._ensureURIMappings();
       this._uriMappings[aID] = uri.spec;
     }
     catch (ex) {
-      WARN("Failed to add URI mapping", ex);
+      logger.warn("Failed to add URI mapping", ex);
     }
   },
 
   /**
    * Ensures that the URI to Addon mappings are available.
    *
    * The function will add mappings for all non-bootstrapped but enabled
    * add-ons.
@@ -1922,17 +1916,17 @@ var XPIProvider = {
    * @param  aOldAppVersion
    *         The version of the application last run with this profile or null
    *         if it is a new profile or the version is unknown
    * @param  aOldPlatformVersion
    *         The version of the platform last run with this profile or null
    *         if it is a new profile or the version is unknown
    */
   startup: function XPI_startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
-    LOG("startup");
+    logger.debug("startup");
     this.runPhase = XPI_STARTING;
     this.installs = [];
     this.installLocations = [];
     this.installLocationsByName = {};
     // Hook for tests to detect when saving database at shutdown time fails
     this._shutdownError = null;
     // Clear this at startup for xpcshell test restarts
     this._telemetryDetails = {};
@@ -1943,38 +1937,38 @@ var XPIProvider = {
     AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
 
     function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) {
       try {
         var dir = FileUtils.getDir(aKey, aPaths);
       }
       catch (e) {
         // Some directories aren't defined on some platforms, ignore them
-        LOG("Skipping unavailable install location " + aName);
+        logger.debug("Skipping unavailable install location " + aName);
         return;
       }
 
       try {
         var location = new DirectoryInstallLocation(aName, dir, aScope, aLocked);
       }
       catch (e) {
-        WARN("Failed to add directory install location " + aName, e);
+        logger.warn("Failed to add directory install location " + aName, e);
         return;
       }
 
       XPIProvider.installLocations.push(location);
       XPIProvider.installLocationsByName[location.name] = location;
     }
 
     function addRegistryInstallLocation(aName, aRootkey, aScope) {
       try {
         var location = new WinRegInstallLocation(aName, aRootkey, aScope);
       }
       catch (e) {
-        WARN("Failed to add registry install location " + aName, e);
+        logger.warn("Failed to add registry install location " + aName, e);
         return;
       }
 
       XPIProvider.installLocations.push(location);
       XPIProvider.installLocationsByName[location.name] = location;
     }
 
     try {
@@ -2093,24 +2087,24 @@ var XPIProvider = {
             if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
                             .indexOf(id) !== -1)
               reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
             this.callBootstrapMethod(id, this.bootstrappedAddons[id].version,
                                      this.bootstrappedAddons[id].type, file,
                                      "startup", reason);
           }
           catch (e) {
-            ERROR("Failed to load bootstrap addon " + id + " from " +
+            logger.error("Failed to load bootstrap addon " + id + " from " +
                   this.bootstrappedAddons[id].descriptor, e);
           }
         }
         AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_end");
       }
       catch (e) {
-        ERROR("bootstrap startup failed", e);
+        logger.error("bootstrap startup failed", e);
         AddonManagerPrivate.recordException("XPI-BOOTSTRAP", "startup failed", e);
       }
 
       // Let these shutdown a little earlier when they still have access to most
       // of XPCOM
       Services.obs.addObserver({
         observe: function shutdownObserver(aSubject, aTopic, aData) {
           for (let id in XPIProvider.bootstrappedAddons) {
@@ -2134,29 +2128,29 @@ var XPIProvider = {
       }, "final-ui-startup", false);
 
       AddonManagerPrivate.recordTimestamp("XPI_startup_end");
 
       this.extensionsActive = true;
       this.runPhase = XPI_BEFORE_UI_STARTUP;
     }
     catch (e) {
-      ERROR("startup failed", e);
+      logger.error("startup failed", e);
       AddonManagerPrivate.recordException("XPI", "startup failed", e);
     }
   },
 
   /**
    * Shuts down the database and releases all references.
    * Return: Promise{integer} resolves / rejects with the result of
    *                          flushing the XPI Database if it was loaded,
    *                          0 otherwise.
    */
   shutdown: function XPI_shutdown() {
-    LOG("shutdown");
+    logger.debug("shutdown");
 
     // Stop anything we were doing asynchronously
     this.cancelAll();
 
     this.bootstrappedAddons = {};
     this.bootstrapScopes = {};
     this.enabledAddons = null;
     this.allAppGlobal = true;
@@ -2180,29 +2174,29 @@ var XPIProvider = {
 
     // Remove URI mappings again
     delete this._uriMappings;
 
     if (gLazyObjectsLoaded) {
       let done = XPIDatabase.shutdown();
       done.then(
         ret => {
-          LOG("Notifying XPI shutdown observers");
+          logger.debug("Notifying XPI shutdown observers");
           Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
         },
         err => {
-          LOG("Notifying XPI shutdown observers");
+          logger.debug("Notifying XPI shutdown observers");
           this._shutdownError = err;
           Services.obs.notifyObservers(null, "xpi-provider-shutdown", err);
         }
       );
       return done;
     }
     else {
-      LOG("Notifying XPI shutdown observers");
+      logger.debug("Notifying XPI shutdown observers");
       Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
     }
   },
 
   /**
    * Applies any pending theme change to the preferences.
    */
   applyThemeChange: function XPI_applyThemeChange() {
@@ -2210,21 +2204,21 @@ var XPIProvider = {
       return;
 
     // Tell the Chrome Registry which Skin to select
     try {
       this.selectedSkin = Prefs.getCharPref(PREF_DSS_SKIN_TO_SELECT);
       Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN,
                                  this.selectedSkin);
       Services.prefs.clearUserPref(PREF_DSS_SKIN_TO_SELECT);
-      LOG("Changed skin to " + this.selectedSkin);
+      logger.debug("Changed skin to " + this.selectedSkin);
       this.currentSkin = this.selectedSkin;
     }
     catch (e) {
-      ERROR("Error applying theme change", e);
+      logger.error("Error applying theme change", e);
     }
     Services.prefs.clearUserPref(PREF_DSS_SWITCHPENDING);
   },
 
   /**
    * Shows the "Compatibility Updates" UI
    */
   showUpgradeUI: function XPI_showUpgradeUI() {
@@ -2377,17 +2371,17 @@ var XPIProvider = {
 
       if (stagedXPIDir.exists() && stagedXPIDir.isDirectory()) {
         let entries = stagedXPIDir.directoryEntries
                                   .QueryInterface(Ci.nsIDirectoryEnumerator);
         while (entries.hasMoreElements()) {
           let stageDirEntry = entries.nextFile;
 
           if (!stageDirEntry.isDirectory()) {
-            WARN("Ignoring file in XPI staging directory: " + stageDirEntry.path);
+            logger.warn("Ignoring file in XPI staging directory: " + stageDirEntry.path);
             continue;
           }
 
           // Find the last added XPI file in the directory
           let stagedXPI = null;
           var xpiEntries = stageDirEntry.directoryEntries
                                         .QueryInterface(Ci.nsIDirectoryEnumerator);
           while (xpiEntries.hasMoreElements()) {
@@ -2408,70 +2402,70 @@ var XPIProvider = {
           if (!stagedXPI)
             continue;
 
           let addon = null;
           try {
             addon = loadManifestFromZipFile(stagedXPI);
           }
           catch (e) {
-            ERROR("Unable to read add-on manifest from " + stagedXPI.path, e);
+            logger.error("Unable to read add-on manifest from " + stagedXPI.path, e);
             continue;
           }
 
-          LOG("Migrating staged install of " + addon.id + " in " + aLocation.name);
+          logger.debug("Migrating staged install of " + addon.id + " in " + aLocation.name);
 
           if (addon.unpack || Prefs.getBoolPref(PREF_XPI_UNPACK, false)) {
             let targetDir = stagingDir.clone();
             targetDir.append(addon.id);
             try {
               targetDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
             }
             catch (e) {
-              ERROR("Failed to create staging directory for add-on " + addon.id, e);
+              logger.error("Failed to create staging directory for add-on " + addon.id, e);
               continue;
             }
 
             try {
               extractFiles(stagedXPI, targetDir);
             }
             catch (e) {
-              ERROR("Failed to extract staged XPI for add-on " + addon.id + " in " +
+              logger.error("Failed to extract staged XPI for add-on " + addon.id + " in " +
                     aLocation.name, e);
             }
           }
           else {
             try {
               stagedXPI.moveTo(stagingDir, addon.id + ".xpi");
             }
             catch (e) {
-              ERROR("Failed to move staged XPI for add-on " + addon.id + " in " +
+              logger.error("Failed to move staged XPI for add-on " + addon.id + " in " +
                     aLocation.name, e);
             }
           }
         }
         entries.close();
       }
 
       if (stagedXPIDir.exists()) {
         try {
           recursiveRemove(stagedXPIDir);
         }
         catch (e) {
           // Non-critical, just saves some perf on startup if we clean this up.
-          LOG("Error removing XPI staging dir " + stagedXPIDir.path, e);
+          logger.debug("Error removing XPI staging dir " + stagedXPIDir.path, e);
         }
       }
 
       try {
         if (!stagingDir || !stagingDir.exists() || !stagingDir.isDirectory())
           return;
       }
       catch (e) {
-        WARN("Failed to find staging directory", e);
+        logger.warn("Failed to find staging directory", e);
         return;
       }
 
       let seenFiles = [];
       // Use a snapshot of the directory contents to avoid possible issues with
       // iterating over a directory while removing files from it (the YAFFS2
       // embedded filesystem has this issue, see bug 772238), and to remove
       // normal files before their resource forks on OSX (see bug 733436).
@@ -2491,101 +2485,101 @@ var XPIProvider = {
         }
 
         if (!isDir) {
           if (id.substring(id.length - 4).toLowerCase() == ".xpi") {
             id = id.substring(0, id.length - 4);
           }
           else {
             if (id.substring(id.length - 5).toLowerCase() != ".json") {
-              WARN("Ignoring file: " + stageDirEntry.path);
+              logger.warn("Ignoring file: " + stageDirEntry.path);
               seenFiles.push(stageDirEntry.leafName);
             }
             continue;
           }
         }
 
         // Check that the directory's name is a valid ID.
         if (!gIDTest.test(id)) {
-          WARN("Ignoring directory whose name is not a valid add-on ID: " +
+          logger.warn("Ignoring directory whose name is not a valid add-on ID: " +
                stageDirEntry.path);
           seenFiles.push(stageDirEntry.leafName);
           continue;
         }
 
         changed = true;
 
         if (isDir) {
           // Check if the directory contains an install manifest.
           let manifest = stageDirEntry.clone();
           manifest.append(FILE_INSTALL_MANIFEST);
 
           // If the install manifest doesn't exist uninstall this add-on in this
           // install location.
           if (!manifest.exists()) {
-            LOG("Processing uninstall of " + id + " in " + aLocation.name);
+            logger.debug("Processing uninstall of " + id + " in " + aLocation.name);
             try {
               aLocation.uninstallAddon(id);
               seenFiles.push(stageDirEntry.leafName);
             }
             catch (e) {
-              ERROR("Failed to uninstall add-on " + id + " in " + aLocation.name, e);
+              logger.error("Failed to uninstall add-on " + id + " in " + aLocation.name, e);
             }
             // The file check later will spot the removal and cleanup the database
             continue;
           }
         }
 
         aManifests[aLocation.name][id] = null;
         let existingAddonID = id;
 
         let jsonfile = stagingDir.clone();
         jsonfile.append(id + ".json");
 
         try {
           aManifests[aLocation.name][id] = loadManifestFromFile(stageDirEntry);
         }
         catch (e) {
-          ERROR("Unable to read add-on manifest from " + stageDirEntry.path, e);
+          logger.error("Unable to read add-on manifest from " + stageDirEntry.path, e);
           // This add-on can't be installed so just remove it now
           seenFiles.push(stageDirEntry.leafName);
           seenFiles.push(jsonfile.leafName);
           continue;
         }
 
         // Check for a cached metadata for this add-on, it may contain updated
         // compatibility information
         if (jsonfile.exists()) {
-          LOG("Found updated metadata for " + id + " in " + aLocation.name);
+          logger.debug("Found updated metadata for " + id + " in " + aLocation.name);
           let fis = Cc["@mozilla.org/network/file-input-stream;1"].
                        createInstance(Ci.nsIFileInputStream);
           let json = Cc["@mozilla.org/dom/json;1"].
                      createInstance(Ci.nsIJSON);
 
           try {
             fis.init(jsonfile, -1, 0, 0);
             let metadata = json.decodeFromStream(fis, jsonfile.fileSize);
             aManifests[aLocation.name][id].importMetadata(metadata);
           }
           catch (e) {
             // If some data can't be recovered from the cached metadata then it
             // is unlikely to be a problem big enough to justify throwing away
             // the install, just log and error and continue
-            ERROR("Unable to read metadata from " + jsonfile.path, e);
+            logger.error("Unable to read metadata from " + jsonfile.path, e);
           }
           finally {
             fis.close();
           }
         }
         seenFiles.push(jsonfile.leafName);
 
         existingAddonID = aManifests[aLocation.name][id].existingAddonID || id;
 
         var oldBootstrap = null;
-        LOG("Processing install of " + id + " in " + aLocation.name);
+        logger.debug("Processing install of " + id + " in " + aLocation.name);
         if (existingAddonID in this.bootstrappedAddons) {
           try {
             var existingAddon = aLocation.getLocationForID(existingAddonID);
             if (this.bootstrappedAddons[existingAddonID].descriptor ==
                 existingAddon.persistentDescriptor) {
               oldBootstrap = this.bootstrappedAddons[existingAddonID];
 
               // We'll be replacing a currently active bootstrapped add-on so
@@ -2609,17 +2603,17 @@ var XPIProvider = {
 
         try {
           var addonInstallLocation = aLocation.installAddon(id, stageDirEntry,
                                                             existingAddonID);
           if (aManifests[aLocation.name][id])
             aManifests[aLocation.name][id]._sourceBundle = addonInstallLocation;
         }
         catch (e) {
-          ERROR("Failed to install staged add-on " + id + " in " + aLocation.name,
+          logger.error("Failed to install staged add-on " + id + " in " + aLocation.name,
                 e);
           // Re-create the staged install
           AddonInstall.createStagedInstall(aLocation, stageDirEntry,
                                            aManifests[aLocation.name][id]);
           // Make sure not to delete the cached manifest json file
           seenFiles.pop();
 
           delete aManifests[aLocation.name][id];
@@ -2634,17 +2628,17 @@ var XPIProvider = {
         }
       }
 
       try {
         aLocation.cleanStagingDir(seenFiles);
       }
       catch (e) {
         // Non-critical, just saves some perf on startup if we clean this up.
-        LOG("Error cleaning staging dir " + stagingDir.path, e);
+        logger.debug("Error cleaning staging dir " + stagingDir.path, e);
       }
     }, this);
     return changed;
   },
 
   /**
    * Installs any add-ons located in the extensions directory of the
    * application's distribution specific directory into the profile unless a
@@ -2681,43 +2675,43 @@ var XPIProvider = {
 
       let id = entry.leafName;
 
       if (entry.isFile()) {
         if (id.substring(id.length - 4).toLowerCase() == ".xpi") {
           id = id.substring(0, id.length - 4);
         }
         else {
-          LOG("Ignoring distribution add-on that isn't an XPI: " + entry.path);
+          logger.debug("Ignoring distribution add-on that isn't an XPI: " + entry.path);
           continue;
         }
       }
       else if (!entry.isDirectory()) {
-        LOG("Ignoring distribution add-on that isn't a file or directory: " +
+        logger.debug("Ignoring distribution add-on that isn't a file or directory: " +
             entry.path);
         continue;
       }
 
       if (!gIDTest.test(id)) {
-        LOG("Ignoring distribution add-on whose name is not a valid add-on ID: " +
+        logger.debug("Ignoring distribution add-on whose name is not a valid add-on ID: " +
             entry.path);
         continue;
       }
 
       let addon;
       try {
         addon = loadManifestFromFile(entry);
       }
       catch (e) {
-        WARN("File entry " + entry.path + " contains an invalid add-on", e);
+        logger.warn("File entry " + entry.path + " contains an invalid add-on", e);
         continue;
       }
 
       if (addon.id != id) {
-        WARN("File entry " + entry.path + " contains an add-on with an " +
+        logger.warn("File entry " + entry.path + " contains an add-on with an " +
              "incorrect ID")
         continue;
       }
 
       let existingEntry = null;
       try {
         existingEntry = profileLocation.getLocationForID(id);
       }
@@ -2729,41 +2723,41 @@ var XPIProvider = {
         try {
           existingAddon = loadManifestFromFile(existingEntry);
 
           if (Services.vc.compare(addon.version, existingAddon.version) <= 0)
             continue;
         }
         catch (e) {
           // Bad add-on in the profile so just proceed and install over the top
-          WARN("Profile contains an add-on with a bad or missing install " +
+          logger.warn("Profile contains an add-on with a bad or missing install " +
                "manifest at " + existingEntry.path + ", overwriting", e);
         }
       }
       else if (Prefs.getBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, false)) {
         continue;
       }
 
       // Install the add-on
       try {
         profileLocation.installAddon(id, entry, null, true);
-        LOG("Installed distribution add-on " + id);
+        logger.debug("Installed distribution add-on " + id);
 
         Services.prefs.setBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, true)
 
         // aManifests may contain a copy of a newly installed add-on's manifest
         // and we'll have overwritten that so instead cache our install manifest
         // which will later be put into the database in processFileChanges
         if (!(KEY_APP_PROFILE in aManifests))
           aManifests[KEY_APP_PROFILE] = {};
         aManifests[KEY_APP_PROFILE][id] = addon;
         changed = true;
       }
       catch (e) {
-        ERROR("Failed to install distribution add-on " + entry.path, e);
+        logger.error("Failed to install distribution add-on " + entry.path, e);
       }
     }
 
     entries.close();
 
     return changed;
   },
 
@@ -2809,17 +2803,17 @@ var XPIProvider = {
      *         The AddonInternal as it appeared the last time the application
      *         ran
      * @param  aAddonState
      *         The new state of the add-on
      * @return a boolean indicating if flushing caches is required to complete
      *         changing this add-on
      */
     function updateMetadata(aInstallLocation, aOldAddon, aAddonState) {
-      LOG("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
+      logger.debug("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
 
       // Check if there is an updated install manifest for this add-on
       let newAddon = aManifests[aInstallLocation.name][aOldAddon.id];
 
       try {
         // If not load it
         if (!newAddon) {
           let file = aInstallLocation.getLocationForID(aOldAddon.id);
@@ -2834,22 +2828,22 @@ var XPIProvider = {
         }
 
         // The ID in the manifest that was loaded must match the ID of the old
         // add-on.
         if (newAddon.id != aOldAddon.id)
           throw new Error("Incorrect id in install manifest");
       }
       catch (e) {
-        WARN("Add-on is invalid", e);
+        logger.warn("Add-on is invalid", e);
         XPIDatabase.removeAddonMetadata(aOldAddon);
         if (!aInstallLocation.locked)
           aInstallLocation.uninstallAddon(aOldAddon.id);
         else
-          WARN("Could not uninstall invalid item from locked install location");
+          logger.warn("Could not uninstall invalid item from locked install location");
         // If this was an active add-on then we must force a restart
         if (aOldAddon.active)
           return true;
 
         return false;
       }
 
       // Set the additional properties on the new AddonInternal
@@ -2904,17 +2898,17 @@ var XPIProvider = {
      *         The AddonInternal as it appeared the last time the application
      *         ran
      * @param  aAddonState
      *         The new state of the add-on
      * @return a boolean indicating if flushing caches is required to complete
      *         changing this add-on
      */
     function updateDescriptor(aInstallLocation, aOldAddon, aAddonState) {
-      LOG("Add-on " + aOldAddon.id + " moved to " + aAddonState.descriptor);
+      logger.debug("Add-on " + aOldAddon.id + " moved to " + aAddonState.descriptor);
 
       aOldAddon.descriptor = aAddonState.descriptor;
       aOldAddon.visible = !(aOldAddon.id in visibleAddons);
       XPIDatabase.saveChanges();
 
       if (aOldAddon.visible) {
         visibleAddons[aOldAddon.id] = aOldAddon;
 
@@ -2997,17 +2991,17 @@ var XPIProvider = {
         aOldAddon.appDisabled = !isUsableAddon(aOldAddon);
 
         let isDisabled = isAddonDisabled(aOldAddon);
 
         // If either property has changed update the database.
         if (wasAppDisabled != aOldAddon.appDisabled ||
             wasUserDisabled != aOldAddon.userDisabled ||
             wasSoftDisabled != aOldAddon.softDisabled) {
-          LOG("Add-on " + aOldAddon.id + " changed appDisabled state to " +
+          logger.debug("Add-on " + aOldAddon.id + " changed appDisabled state to " +
               aOldAddon.appDisabled + ", userDisabled state to " +
               aOldAddon.userDisabled + " and softDisabled state to " +
               aOldAddon.softDisabled);
           XPIDatabase.saveChanges();
         }
 
         // If this is a visible add-on and it has changed disabled state then we
         // may need a restart or to update the bootstrap list.
@@ -3044,17 +3038,17 @@ var XPIProvider = {
      * @param  aOldAddon
      *         The AddonInternal as it appeared the last time the application
      *         ran
      * @return a boolean indicating if flushing caches is required to complete
      *         changing this add-on
      */
     function removeMetadata(aOldAddon) {
       // This add-on has disappeared
-      LOG("Add-on " + aOldAddon.id + " removed from " + aOldAddon.location);
+      logger.debug("Add-on " + aOldAddon.id + " removed from " + aOldAddon.location);
       XPIDatabase.removeAddonMetadata(aOldAddon);
 
       // Remember add-ons that were uninstalled during startup
       if (aOldAddon.visible) {
         AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED,
                                              aOldAddon.id);
       }
       else if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
@@ -3091,17 +3085,17 @@ var XPIProvider = {
      *         The new state of the add-on
      * @param  aMigrateData
      *         If during startup the database had to be upgraded this will
      *         contain data that used to be held about this add-on
      * @return a boolean indicating if flushing caches is required to complete
      *         changing this add-on
      */
     function addMetadata(aInstallLocation, aId, aAddonState, aMigrateData) {
-      LOG("New add-on " + aId + " installed in " + aInstallLocation.name);
+      logger.debug("New add-on " + aId + " installed in " + aInstallLocation.name);
 
       let newAddon = null;
       let sameVersion = false;
       // Check the updated manifests lists for the install location, If there
       // is no manifest for the add-on ID then newAddon will be undefined
       if (aInstallLocation.name in aManifests)
         newAddon = aManifests[aInstallLocation.name][aId];
 
@@ -3123,37 +3117,37 @@ var XPIProvider = {
         }
         // The add-on in the manifest should match the add-on ID.
         if (newAddon.id != aId) {
           throw new Error("Invalid addon ID: expected addon ID " + aId +
                           ", found " + newAddon.id + " in manifest");
         }
       }
       catch (e) {
-        WARN("Add-on is invalid", e);
+        logger.warn("Add-on is invalid", e);
 
         // Remove the invalid add-on from the install location if the install
         // location isn't locked, no restart will be necessary
         if (!aInstallLocation.locked)
           aInstallLocation.uninstallAddon(aId);
         else
-          WARN("Could not uninstall invalid item from locked install location");
+          logger.warn("Could not uninstall invalid item from locked install location");
         return false;
       }
 
       // Update the AddonInternal properties.
       newAddon._installLocation = aInstallLocation;
       newAddon.visible = !(newAddon.id in visibleAddons);
       newAddon.installDate = aAddonState.mtime;
       newAddon.updateDate = aAddonState.mtime;
       newAddon.foreignInstall = isDetectedInstall;
 
       if (aMigrateData) {
         // If there is migration data then apply it.
-        LOG("Migrating data from old database");
+        logger.debug("Migrating data from old database");
 
         DB_MIGRATE_METADATA.forEach(function(aProp) {
           // A theme's disabled state is determined by the selected theme
           // preference which is read in loadManifestFromRDF
           if (aProp == "userDisabled" && newAddon.type == "theme")
             return;
 
           if (aProp in aMigrateData)
@@ -3163,17 +3157,17 @@ var XPIProvider = {
         // Force all non-profile add-ons to be foreignInstalls since they can't
         // have been installed through the API
         newAddon.foreignInstall |= aInstallLocation.name != KEY_APP_PROFILE;
 
         // Some properties should only be migrated if the add-on hasn't changed.
         // The version property isn't a perfect check for this but covers the
         // vast majority of cases.
         if (aMigrateData.version == newAddon.version) {
-          LOG("Migrating compatibility info");
+          logger.debug("Migrating compatibility info");
           sameVersion = true;
           if ("targetApplications" in aMigrateData)
             newAddon.applyCompatibilityUpdate(aMigrateData, true);
         }
 
         // Since the DB schema has changed make sure softDisabled is correct
         applyBlocklistChanges(newAddon, newAddon, aOldAppVersion,
                               aOldPlatformVersion);
@@ -3444,32 +3438,32 @@ var XPIProvider = {
    *         if it is a new profile or the version is unknown
    * @param  aOldPlatformVersion
    *         The version of the platform last run with this profile or null
    *         if it is a new profile or the version is unknown
    * @return true if a change requiring a restart was detected
    */
   checkForChanges: function XPI_checkForChanges(aAppChanged, aOldAppVersion,
                                                 aOldPlatformVersion) {
-    LOG("checkForChanges");
+    logger.debug("checkForChanges");
 
     // Keep track of whether and why we need to open and update the database at
     // startup time.
     let updateReasons = [];
     if (aAppChanged) {
       updateReasons.push("appChanged");
     }
 
     // Load the list of bootstrapped add-ons first so processFileChanges can
     // modify it
     try {
       this.bootstrappedAddons = JSON.parse(Prefs.getCharPref(PREF_BOOTSTRAP_ADDONS,
                                            "{}"));
     } catch (e) {
-      WARN("Error parsing enabled bootstrapped extensions cache", e);
+      logger.warn("Error parsing enabled bootstrapped extensions cache", e);
     }
 
     // First install any new add-ons into the locations, if there are any
     // changes then we must update the database with the information in the
     // install locations
     let manifests = {};
     let updated = this.processPendingFileChanges(manifests);
     if (updated) {
@@ -3501,31 +3495,31 @@ var XPIProvider = {
     telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Date.now() - telemetryCaptureTime);
 
     // If the install directory state has changed then we must update the database
     let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, "[]");
     // For a little while, gather telemetry on whether the deep comparison
     // makes a difference
     let newState = JSON.stringify(this.installStates);
     if (cache != newState) {
-      LOG("Directory state JSON differs: cache " + cache + " state " + newState);
+      logger.debug("Directory state JSON differs: cache " + cache + " state " + newState);
       if (directoryStateDiffers(this.installStates, cache)) {
         updateReasons.push("directoryState");
       }
       else {
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_state_badCompare", 1);
       }
     }
 
     // If the schema appears to have changed then we should update the database
     if (DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
       // If we don't have any add-ons, just update the pref, since we don't need to
       // write the database
       if (this.installStates.length == 0) {
-        LOG("Empty XPI database, setting schema version preference to " + DB_SCHEMA);
+        logger.debug("Empty XPI database, setting schema version preference to " + DB_SCHEMA);
         Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA);
       }
       else {
         updateReasons.push("schemaChanged");
       }
     }
 
     // If the database doesn't exist and there are add-ons installed then we
@@ -3544,17 +3538,17 @@ var XPIProvider = {
         for (let id in aInstallLocationState.addons) {
           let pos = bootstrapDescriptors.indexOf(aInstallLocationState.addons[id].descriptor);
           if (pos != -1)
             bootstrapDescriptors.splice(pos, 1);
         }
       });
 
       if (bootstrapDescriptors.length > 0) {
-        WARN("Bootstrap state is invalid (missing add-ons: " + bootstrapDescriptors.toSource() + ")");
+        logger.warn("Bootstrap state is invalid (missing add-ons: " + bootstrapDescriptors.toSource() + ")");
         updateReasons.push("missingBootstrapAddon");
       }
     }
 
     // Catch and log any errors during the main startup
     try {
       let extensionListChanged = false;
       // If the database needs to be updated then open it and then update it
@@ -3564,17 +3558,17 @@ var XPIProvider = {
         XPIDatabase.syncLoadDB(false);
         try {
           extensionListChanged = this.processFileChanges(this.installStates, manifests,
                                                          aAppChanged,
                                                          aOldAppVersion,
                                                          aOldPlatformVersion);
         }
         catch (e) {
-          ERROR("Failed to process extension changes at startup", e);
+          logger.error("Failed to process extension changes at startup", e);
         }
       }
 
       if (aAppChanged) {
         // When upgrading the app and using a custom skin make sure it is still
         // compatible otherwise switch back the default
         if (this.currentSkin != this.defaultSkin) {
           let oldSkin = XPIDatabase.getVisibleAddonForInternalName(this.currentSkin);
@@ -3585,43 +3579,43 @@ var XPIProvider = {
         // When upgrading remove the old extensions cache to force older
         // versions to rescan the entire list of extensions
         try {
           let oldCache = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_CACHE], true);
           if (oldCache.exists())
             oldCache.remove(true);
         }
         catch (e) {
-          WARN("Unable to remove old extension cache " + oldCache.path, e);
+          logger.warn("Unable to remove old extension cache " + oldCache.path, e);
         }
       }
 
       // If the application crashed before completing any pending operations then
       // we should perform them now.
       if (extensionListChanged || hasPendingChanges) {
-        LOG("Updating database with changes to installed add-ons");
+        logger.debug("Updating database with changes to installed add-ons");
         XPIDatabase.updateActiveAddons();
         Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
                                    !XPIDatabase.writeAddonsList());
         Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
                                    JSON.stringify(this.bootstrappedAddons));
         return true;
       }
 
-      LOG("No changes found");
+      logger.debug("No changes found");
     }
     catch (e) {
-      ERROR("Error during startup file checks", e);
+      logger.error("Error during startup file checks", e);
     }
 
     // Check that the add-ons list still exists
     let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
                                        true);
     if (addonsList.exists() == (this.installStates.length == 0)) {
-      LOG("Add-ons list is invalid, rebuilding");
+      logger.debug("Add-ons list is invalid, rebuilding");
       XPIDatabase.writeAddonsList();
     }
 
     return false;
   },
 
   /**
    * Called to test whether this provider supports installing a particular
@@ -3950,38 +3944,38 @@ var XPIProvider = {
     });
   },
 
   /**
    * When the previously selected theme is removed this method will be called
    * to enable the default theme.
    */
   enableDefaultTheme: function XPI_enableDefaultTheme() {
-    LOG("Activating default theme");
+    logger.debug("Activating default theme");
     let addon = XPIDatabase.getVisibleAddonForInternalName(this.defaultSkin);
     if (addon) {
       if (addon.userDisabled) {
         this.updateAddonDisabledState(addon, false);
       }
       else if (!this.extensionsActive) {
         // During startup we may end up trying to enable the default theme when
         // the database thinks it is already enabled (see f.e. bug 638847). In
         // this case just force the theme preferences to be correct
         Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN,
                                    addon.internalName);
         this.currentSkin = this.selectedSkin = addon.internalName;
         Prefs.clearUserPref(PREF_DSS_SKIN_TO_SELECT);
         Prefs.clearUserPref(PREF_DSS_SWITCHPENDING);
       }
       else {
-        WARN("Attempting to activate an already active default theme");
+        logger.warn("Attempting to activate an already active default theme");
       }
     }
     else {
-      WARN("Unable to activate the default theme");
+      logger.warn("Unable to activate the default theme");
     }
   },
 
   /**
    * Notified when a preference we're interested in has changed.
    *
    * @see nsIObserver
    */
@@ -4187,26 +4181,26 @@ var XPIProvider = {
     this.addAddonsToCrashReporter();
 
     // Locales only contain chrome and can't have bootstrap scripts
     if (aType == "locale") {
       this.bootstrapScopes[aId] = null;
       return;
     }
 
-    LOG("Loading bootstrap scope from " + aFile.path);
+    logger.debug("Loading bootstrap scope from " + aFile.path);
 
     let principal = Cc["@mozilla.org/systemprincipal;1"].
                     createInstance(Ci.nsIPrincipal);
 
     if (!aFile.exists()) {
       this.bootstrapScopes[aId] =
         new Cu.Sandbox(principal, {sandboxName: aFile.path,
                                    wantGlobalProperties: ["indexedDB"]});
-      ERROR("Attempted to load bootstrap scope from missing directory " + aFile.path);
+      logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
       return;
     }
 
     let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
     this.bootstrapScopes[aId] =
       new Cu.Sandbox(principal, {sandboxName: uri,
                                  wantGlobalProperties: ["indexedDB"]});
 
@@ -4237,17 +4231,17 @@ var XPIProvider = {
         this.bootstrapScopes[aId].__SCRIPT_URI_SPEC__ = uri;
       }
       Components.utils.evalInSandbox(
         "Components.classes['@mozilla.org/moz/jssubscript-loader;1'] \
                    .createInstance(Components.interfaces.mozIJSSubScriptLoader) \
                    .loadSubScript(__SCRIPT_URI_SPEC__);", this.bootstrapScopes[aId], "ECMAv5");
     }
     catch (e) {
-      WARN("Error loading bootstrap.js for " + aId, e);
+      logger.warn("Error loading bootstrap.js for " + aId, e);
     }
   },
 
   /**
    * Unloads a bootstrap scope by dropping all references to it and then
    * updating the list of active add-ons with the crash reporter.
    *
    * @param  aId
@@ -4282,59 +4276,59 @@ var XPIProvider = {
   callBootstrapMethod: function XPI_callBootstrapMethod(aId, aVersion, aType, aFile,
                                                         aMethod, aReason, aExtraParams) {
     // Never call any bootstrap methods in safe mode
     if (Services.appinfo.inSafeMode)
       return;
 
     let timeStart = new Date();
     if (aMethod == "startup") {
-      LOG("Registering manifest for " + aFile.path);
+      logger.debug("Registering manifest for " + aFile.path);
       Components.manager.addBootstrappedManifestLocation(aFile);
     }
 
     try {
       // Load the scope if it hasn't already been loaded
       if (!(aId in this.bootstrapScopes))
         this.loadBootstrapScope(aId, aFile, aVersion, aType);
 
       // Nothing to call for locales
       if (aType == "locale")
         return;
 
       if (!(aMethod in this.bootstrapScopes[aId])) {
-        WARN("Add-on " + aId + " is missing bootstrap method " + aMethod);
+        logger.warn("Add-on " + aId + " is missing bootstrap method " + aMethod);
         return;
       }
 
       let params = {
         id: aId,
         version: aVersion,
         installPath: aFile.clone(),
         resourceURI: getURIForResourceInFile(aFile, "")
       };
 
       if (aExtraParams) {
         for (let key in aExtraParams) {
           params[key] = aExtraParams[key];
         }
       }
 
-      LOG("Calling bootstrap method " + aMethod + " on " + aId + " version " +
+      logger.debug("Calling bootstrap method " + aMethod + " on " + aId + " version " +
           aVersion);
       try {
         this.bootstrapScopes[aId][aMethod](params, aReason);
       }
       catch (e) {
-        WARN("Exception running bootstrap method " + aMethod + " on " + aId, e);
+        logger.warn("Exception running bootstrap method " + aMethod + " on " + aId, e);
       }
     }
     finally {
       if (aMethod == "shutdown" && aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
-        LOG("Removing manifest for " + aFile.path);
+        logger.debug("Removing manifest for " + aFile.path);
         Components.manager.removeBootstrappedManifestLocation(aFile);
       }
       this.setTelemetry(aId, aMethod + "_MS", new Date() - timeStart);
     }
   },
 
   /**
    * Updates the disabled state for an add-on. Its appDisabled property will be
@@ -4468,17 +4462,17 @@ var XPIProvider = {
 
     if (aAddon._installLocation.locked)
       throw new Error("Cannot uninstall addons from locked install locations");
 
     if ("_hasResourceCache" in aAddon)
       aAddon._hasResourceCache = new Map();
 
     if (aAddon._updateCheck) {
-      LOG("Cancel in-progress update check for " + aAddon.id);
+      logger.debug("Cancel in-progress update check for " + aAddon.id);
       aAddon._updateCheck.cancel();
     }
 
     // Inactive add-ons don't require a restart to uninstall
     let requiresRestart = this.uninstallRequiresRestart(aAddon);
 
     if (requiresRestart) {
       // We create an empty directory in the staging directory to indicate that
@@ -4653,18 +4647,18 @@ function AddonInstall(aInstallLocation, 
   this.existingAddon = aExistingAddon;
   this.error = 0;
   if (aLoadGroup)
     this.window = aLoadGroup.notificationCallbacks
                             .getInterface(Ci.nsIDOMWindow);
   else
     this.window = null;
 
-  this.WARN = WARN;
-  this.LOG = LOG;
+  // Giving each instance of AddonInstall a reference to the logger.
+  this.logger = logger;
 }
 
 AddonInstall.prototype = {
   installLocation: null,
   wrapper: null,
   stream: null,
   crypto: null,
   originalHash: null,
@@ -4725,17 +4719,17 @@ AddonInstall.prototype = {
    * @param  aCallback
    *         The callback to pass the initialised AddonInstall to
    */
   initLocalInstall: function AI_initLocalInstall(aCallback) {
     aCallback = makeSafe(aCallback);
     this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file;
 
     if (!this.file.exists()) {
-      WARN("XPI file " + this.file.path + " does not exist");
+      logger.warn("XPI file " + this.file.path + " does not exist");
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_NETWORK_FAILURE;
       aCallback(this);
       return;
     }
 
     this.state = AddonManager.STATE_DOWNLOADED;
     this.progress = this.file.fileSize;
@@ -4743,30 +4737,30 @@ AddonInstall.prototype = {
 
     if (this.hash) {
       let crypto = Cc["@mozilla.org/security/hash;1"].
                    createInstance(Ci.nsICryptoHash);
       try {
         crypto.initWithString(this.hash.algorithm);
       }
       catch (e) {
-        WARN("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
+        logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
         this.state = AddonManager.STATE_DOWNLOAD_FAILED;
         this.error = AddonManager.ERROR_INCORRECT_HASH;
         aCallback(this);
         return;
       }
 
       let fis = Cc["@mozilla.org/network/file-input-stream;1"].
                 createInstance(Ci.nsIFileInputStream);
       fis.init(this.file, -1, -1, false);
       crypto.updateFromStream(fis, this.file.fileSize);
       let calculatedHash = getHashStringForCrypto(crypto);
       if (calculatedHash != this.hash.data) {
-        WARN("File hash (" + calculatedHash + ") did not match provided hash (" +
+        logger.warn("File hash (" + calculatedHash + ") did not match provided hash (" +
              this.hash.data + ")");
         this.state = AddonManager.STATE_DOWNLOAD_FAILED;
         this.error = AddonManager.ERROR_INCORRECT_HASH;
         aCallback(this);
         return;
       }
     }
 
@@ -4802,17 +4796,17 @@ AddonInstall.prototype = {
                                                      self.wrapper);
 
             aCallback(self);
           }
         });
       });
     }
     catch (e) {
-      WARN("Invalid XPI", e);
+      logger.warn("Invalid XPI", e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_CORRUPT_FILE;
       aCallback(this);
       return;
     }
   },
 
   /**
@@ -4888,25 +4882,25 @@ AddonInstall.prototype = {
    */
   cancel: function AI_cancel() {
     switch (this.state) {
     case AddonManager.STATE_DOWNLOADING:
       if (this.channel)
         this.channel.cancel(Cr.NS_BINDING_ABORTED);
     case AddonManager.STATE_AVAILABLE:
     case AddonManager.STATE_DOWNLOADED:
-      LOG("Cancelling download of " + this.sourceURI.spec);
+      logger.debug("Cancelling download of " + this.sourceURI.spec);
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
                                                this.listeners, this.wrapper);
       this.removeTemporaryFile();
       break;
     case AddonManager.STATE_INSTALLED:
-      LOG("Cancelling install of " + this.addon.id);
+      logger.debug("Cancelling install of " + this.addon.id);
       let xpi = this.installLocation.getStagingDir();
       xpi.append(this.addon.id + ".xpi");
       flushJarCache(xpi);
       this.installLocation.cleanStagingDir([this.addon.id, this.addon.id + ".xpi",
                                             this.addon.id + ".json"]);
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
 
@@ -4951,28 +4945,28 @@ AddonInstall.prototype = {
   },
 
   /**
    * Removes the temporary file owned by this AddonInstall if there is one.
    */
   removeTemporaryFile: function AI_removeTemporaryFile() {
     // Only proceed if this AddonInstall owns its XPI file
     if (!this.ownsTempFile) {
-      this.LOG("removeTemporaryFile: " + this.sourceURI.spec + " does not own temp file");
+      this.logger.debug("removeTemporaryFile: " + this.sourceURI.spec + " does not own temp file");
       return;
     }
 
     try {
-      this.LOG("removeTemporaryFile: " + this.sourceURI.spec + " removing temp file " +
+      this.logger.debug("removeTemporaryFile: " + this.sourceURI.spec + " removing temp file " +
           this.file.path);
       this.file.remove(true);
       this.ownsTempFile = false;
     }
     catch (e) {
-      this.WARN("Failed to remove temporary file " + this.file.path + " for addon " +
+      this.logger.warn("Failed to remove temporary file " + this.file.path + " for addon " +
           this.sourceURI.spec,
           e);
     }
   },
 
   /**
    * Updates the sourceURI and releaseNotesURI values on the Addon being
    * installed by this AddonInstall instance.
@@ -5004,17 +4998,17 @@ AddonInstall.prototype = {
     while (entries.hasMore()) {
       let entryName = entries.getNext();
       var target = getTemporaryFile();
       try {
         aZipReader.extract(entryName, target);
         files.push(target);
       }
       catch (e) {
-        WARN("Failed to extract " + entryName + " from multi-package " +
+        logger.warn("Failed to extract " + entryName + " from multi-package " +
              "XPI", e);
         target.remove(false);
       }
     }
 
     aZipReader.close();
 
     if (files.length == 0) {
@@ -5030,17 +5024,17 @@ AddonInstall.prototype = {
       this.removeTemporaryFile();
       this.file = files.shift();
       this.ownsTempFile = true;
       try {
         addon = loadManifestFromZipFile(this.file);
         break;
       }
       catch (e) {
-        WARN(this.file.leafName + " cannot be installed from multi-package " +
+        logger.warn(this.file.leafName + " cannot be installed from multi-package " +
              "XPI", e);
       }
     }
 
     if (!addon) {
       // No valid add-on was found
       makeSafe(aCallback)();
       return;
@@ -5141,17 +5135,17 @@ AddonInstall.prototype = {
     }
     catch (e) {
       zipreader.close();
       throw e;
     }
 
     let principal = zipreader.getCertificatePrincipal(null);
     if (principal && principal.hasCertificate) {
-      LOG("Verifying XPI signature");
+      logger.debug("Verifying XPI signature");
       if (verifyZipSigning(zipreader, principal)) {
         let x509 = principal.certificate;
         if (x509 instanceof Ci.nsIX509Cert)
           this.certificate = x509;
         if (this.certificate && this.certificate.commonName.length > 0)
           this.certName = this.certificate.commonName;
         else
           this.certName = principal.prettyName;
@@ -5202,32 +5196,32 @@ AddonInstall.prototype = {
 
   /**
    * Starts downloading the add-on's XPI file.
    */
   startDownload: function AI_startDownload() {
     this.state = AddonManager.STATE_DOWNLOADING;
     if (!AddonManagerPrivate.callInstallListeners("onDownloadStarted",
                                                   this.listeners, this.wrapper)) {
-      LOG("onDownloadStarted listeners cancelled installation of addon " + this.sourceURI.spec);
+      logger.debug("onDownloadStarted listeners cancelled installation of addon " + this.sourceURI.spec);
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
                                                this.listeners, this.wrapper)
       return;
     }
 
     // If a listener changed our state then do not proceed with the download
     if (this.state != AddonManager.STATE_DOWNLOADING)
       return;
 
     if (this.channel) {
       // A previous download attempt hasn't finished cleaning up yet, signal
       // that it should restart when complete
-      LOG("Waiting for previous download to complete");
+      logger.debug("Waiting for previous download to complete");
       this.restartDownload = true;
       return;
     }
 
     this.openChannel();
   },
 
   openChannel: function AI_openChannel() {
@@ -5237,17 +5231,17 @@ AddonInstall.prototype = {
       this.file = getTemporaryFile();
       this.ownsTempFile = true;
       this.stream = Cc["@mozilla.org/network/file-output-stream;1"].
                     createInstance(Ci.nsIFileOutputStream);
       this.stream.init(this.file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
                        FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE, 0);
     }
     catch (e) {
-      WARN("Failed to start download for addon " + this.sourceURI.spec, e);
+      logger.warn("Failed to start download for addon " + this.sourceURI.spec, e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_FILE_ACCESS;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadFailed",
                                                this.listeners, this.wrapper);
       return;
     }
 
@@ -5263,17 +5257,17 @@ AddonInstall.prototype = {
       this.channel.notificationCallbacks = this;
       if (this.channel instanceof Ci.nsIHttpChannelInternal)
         this.channel.forceAllowThirdPartyCookie = true;
       this.channel.asyncOpen(listener, null);
 
       Services.obs.addObserver(this, "network:offline-about-to-go-offline", false);
     }
     catch (e) {
-      WARN("Failed to start download for addon " + this.sourceURI.spec, e);
+      logger.warn("Failed to start download for addon " + this.sourceURI.spec, e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_NETWORK_FAILURE;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadFailed",
                                                this.listeners, this.wrapper);
     }
   },
 
@@ -5331,17 +5325,17 @@ AddonInstall.prototype = {
   onStartRequest: function AI_onStartRequest(aRequest, aContext) {
     this.crypto = Cc["@mozilla.org/security/hash;1"].
                   createInstance(Ci.nsICryptoHash);
     if (this.hash) {
       try {
         this.crypto.initWithString(this.hash.algorithm);
       }
       catch (e) {
-        WARN("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
+        logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
         this.state = AddonManager.STATE_DOWNLOAD_FAILED;
         this.error = AddonManager.ERROR_INCORRECT_HASH;
         XPIProvider.removeActiveInstall(this);
         AddonManagerPrivate.callInstallListeners("onDownloadFailed",
                                                  this.listeners, this.wrapper);
         aRequest.cancel(Cr.NS_BINDING_ABORTED);
         return;
       }
@@ -5354,17 +5348,17 @@ AddonInstall.prototype = {
 
     this.progress = 0;
     if (aRequest instanceof Ci.nsIChannel) {
       try {
         this.maxProgress = aRequest.contentLength;
       }
       catch (e) {
       }
-      LOG("Download started for " + this.sourceURI.spec + " to file " +
+      logger.debug("Download started for " + this.sourceURI.spec + " to file " +
           this.file.path);
     }
   },
 
   /**
    * The download is complete.
    *
    * @see nsIStreamListener
@@ -5378,17 +5372,17 @@ AddonInstall.prototype = {
     // If the download was cancelled then all events will have already been sent
     if (aStatus == Cr.NS_BINDING_ABORTED) {
       this.removeTemporaryFile();
       if (this.restartDownload)
         this.openChannel();
       return;
     }
 
-    LOG("Download of " + this.sourceURI.spec + " completed.");
+    logger.debug("Download of " + this.sourceURI.spec + " completed.");
 
     if (Components.isSuccessCode(aStatus)) {
       if (!(aRequest instanceof Ci.nsIHttpChannel) || aRequest.requestSucceeded) {
         if (!this.hash && (aRequest instanceof Ci.nsIChannel)) {
           try {
             checkCert(aRequest,
                       !Prefs.getBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, true));
           }
@@ -5446,31 +5440,31 @@ AddonInstall.prototype = {
    * Notify listeners that the download failed.
    *
    * @param  aReason
    *         Something to log about the failure
    * @param  error
    *         The error code to pass to the listeners
    */
   downloadFailed: function AI_downloadFailed(aReason, aError) {
-    WARN("Download failed", aError);
+    logger.warn("Download failed", aError);
     this.state = AddonManager.STATE_DOWNLOAD_FAILED;
     this.error = aReason;
     XPIProvider.removeActiveInstall(this);
     AddonManagerPrivate.callInstallListeners("onDownloadFailed", this.listeners,
                                              this.wrapper);
 
     // If the listener hasn't restarted the download then remove any temporary
     // file
     if (this.state == AddonManager.STATE_DOWNLOAD_FAILED) {
-      LOG("downloadFailed: removing temp file for " + this.sourceURI.spec);
+      logger.debug("downloadFailed: removing temp file for " + this.sourceURI.spec);
       this.removeTemporaryFile();
     }
     else
-      LOG("downloadFailed: listener changed AddonInstall state for " +
+      logger.debug("downloadFailed: listener changed AddonInstall state for " +
           this.sourceURI.spec + " to " + this.state);
   },
 
   /**
    * Notify listeners that the download completed.
    */
   downloadCompleted: function AI_downloadCompleted() {
     let self = this;
@@ -5533,40 +5527,40 @@ AddonInstall.prototype = {
           aInstall.addon.id == this.addon.id)
         aInstall.cancel();
     }, this);
 
     let isUpgrade = this.existingAddon &&
                     this.existingAddon._installLocation == this.installLocation;
     let requiresRestart = XPIProvider.installRequiresRestart(this.addon);
 
-    LOG("Starting install of " + this.sourceURI.spec);
+    logger.debug("Starting install of " + this.sourceURI.spec);
     AddonManagerPrivate.callAddonListeners("onInstalling",
                                            createWrapper(this.addon),
                                            requiresRestart);
 
     let stagingDir = this.installLocation.getStagingDir();
     let stagedAddon = stagingDir.clone();
 
     Task.spawn((function() {
       let installedUnpacked = 0;
       yield this.installLocation.requestStagingDir();
 
       // First stage the file regardless of whether restarting is necessary
       if (this.addon.unpack || Prefs.getBoolPref(PREF_XPI_UNPACK, false)) {
-        LOG("Addon " + this.addon.id + " will be installed as " +
+        logger.debug("Addon " + this.addon.id + " will be installed as " +
             "an unpacked directory");
         stagedAddon.append(this.addon.id);
         yield removeAsync(stagedAddon);
         yield OS.File.makeDir(stagedAddon.path);
         yield extractFilesAsync(this.file, stagedAddon);
         installedUnpacked = 1;
       }
       else {
-        LOG("Addon " + this.addon.id + " will be installed as " +
+        logger.debug("Addon " + this.addon.id + " will be installed as " +
             "a packed xpi");
         stagedAddon.append(this.addon.id + ".xpi");
         yield removeAsync(stagedAddon);
         yield OS.File.copy(this.file.path, stagedAddon.path);
       }
 
       if (requiresRestart) {
         // Point the add-on to its extracted files as the xpi may get deleted
@@ -5589,17 +5583,17 @@ AddonInstall.prototype = {
           converter.init(stream, "UTF-8", 0, 0x0000);
           converter.writeString(JSON.stringify(this.addon));
         }
         finally {
           converter.close();
           stream.close();
         }
 
-        LOG("Staged install of " + this.sourceURI.spec + " ready; waiting for restart.");
+        logger.debug("Staged install of " + this.sourceURI.spec + " ready; waiting for restart.");
         this.state = AddonManager.STATE_INSTALLED;
         if (isUpgrade) {
           delete this.existingAddon.pendingUpgrade;
           this.existingAddon.pendingUpgrade = this.addon;
         }
         AddonManagerPrivate.callInstallListeners("onInstallEnded",
                                                  this.listeners, this.wrapper,
                                                  createWrapper(this.addon));
@@ -5677,17 +5671,17 @@ AddonInstall.prototype = {
           XPIProvider.callBootstrapMethod(this.addon.id, this.addon.version,
                                           this.addon.type, file, "install",
                                           reason, extraParams);
         }
 
         AddonManagerPrivate.callAddonListeners("onInstalled",
                                                createWrapper(this.addon));
 
-        LOG("Install of " + this.sourceURI.spec + " completed.");
+        logger.debug("Install of " + this.sourceURI.spec + " completed.");
         this.state = AddonManager.STATE_INSTALLED;
         AddonManagerPrivate.callInstallListeners("onInstallEnded",
                                                  this.listeners, this.wrapper,
                                                  createWrapper(this.addon));
 
         if (this.addon.bootstrap) {
           if (this.addon.active) {
             XPIProvider.callBootstrapMethod(this.addon.id, this.addon.version,
@@ -5706,17 +5700,17 @@ AddonInstall.prototype = {
         XPIProvider.setTelemetry(this.addon.id, "scan_items", scanItems);
         let loc = this.addon.defaultLocale;
         if (loc) {
           XPIProvider.setTelemetry(this.addon.id, "name", loc.name);
           XPIProvider.setTelemetry(this.addon.id, "creator", loc.creator);
         }
       }
     }).bind(this)).then(null, (e) => {
-      WARN("Failed to install " + this.file.path + " from " + this.sourceURI.spec, e);
+      logger.warn("Failed to install " + this.file.path + " from " + this.sourceURI.spec, e);
       if (stagedAddon.exists())
         recursiveRemove(stagedAddon);
       this.state = AddonManager.STATE_INSTALL_FAILED;
       this.error = AddonManager.ERROR_FILE_ACCESS;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callAddonListeners("onOperationCancelled",
                                              createWrapper(this.addon));
       AddonManagerPrivate.callInstallListeners("onInstallFailed",
@@ -5771,17 +5765,17 @@ AddonInstall.createInstall = function AI
   let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
   let url = Services.io.newFileURI(aFile);
 
   try {
     let install = new AddonInstall(location, url);
     install.initLocalInstall(aCallback);
   }
   catch(e) {
-    ERROR("Error creating install", e);
+    logger.error("Error creating install", e);
     makeSafe(aCallback)(null);
   }
 };
 
 /**
  * Creates a new AddonInstall to download and install a URL.
  *
  * @param  aCallback
@@ -5962,17 +5956,17 @@ UpdateChecker.prototype = {
   callListener: function UC_callListener(aMethod, ...aArgs) {
     if (!(aMethod in this.listener))
       return;
 
     try {
       this.listener[aMethod].apply(this.listener, aArgs);
     }
     catch (e) {
-      WARN("Exception calling UpdateListener method " + aMethod, e);
+      logger.warn("Exception calling UpdateListener method " + aMethod, e);
     }
   },
 
   /**
    * Called when AddonUpdateChecker completes the update check
    *
    * @param  updates
    *         The list of update details for the add-on
@@ -6893,31 +6887,31 @@ DirectoryInstallLocation.prototype = {
       try {
         linkedDirectory.initWithPath(line.value);
       }
       catch (e) {
         linkedDirectory.setRelativeDescriptor(aFile.parent, line.value);
       }
 
       if (!linkedDirectory.exists()) {
-        WARN("File pointer " + aFile.path + " points to " + linkedDirectory.path +
+        logger.warn("File pointer " + aFile.path + " points to " + linkedDirectory.path +
              " which does not exist");
         return null;
       }
 
       if (!linkedDirectory.isDirectory()) {
-        WARN("File pointer " + aFile.path + " points to " + linkedDirectory.path +
+        logger.warn("File pointer " + aFile.path + " points to " + linkedDirectory.path +
              " which is not a directory");
         return null;
       }
 
       return linkedDirectory;
     }
 
-    WARN("File pointer " + aFile.path + " does not contain a path");
+    logger.warn("File pointer " + aFile.path + " does not contain a path");
     return null;
   },
 
   /**
    * Finds all the add-ons installed in this location.
    */
   _readAddons: function DirInstallLocation__readAddons() {
     // Use a snapshot of the directory contents to avoid possible issues with
@@ -6933,30 +6927,30 @@ DirectoryInstallLocation.prototype = {
       let directLoad = false;
       if (entry.isFile() &&
           id.substring(id.length - 4).toLowerCase() == ".xpi") {
         directLoad = true;
         id = id.substring(0, id.length - 4);
       }
 
       if (!gIDTest.test(id)) {
-        LOG("Ignoring file entry whose name is not a valid add-on ID: " +
+        logger.debug("Ignoring file entry whose name is not a valid add-on ID: " +
              entry.path);
         continue;
       }
 
       if (entry.isFile() && !directLoad) {
         let newEntry = this._readDirectoryFromFile(entry);
         if (!newEntry) {
-          LOG("Deleting stale pointer file " + entry.path);
+          logger.debug("Deleting stale pointer file " + entry.path);
           try {
             entry.remove(true);
           }
           catch (e) {
-            WARN("Failed to remove stale pointer file " + entry.path, e);
+            logger.warn("Failed to remove stale pointer file " + entry.path, e);
             // Failing to remove the stale pointer file is ignorable
           }
           continue;
         }
 
         entry = newEntry;
         this._linkedAddons.push(id);
       }
@@ -7009,17 +7003,17 @@ DirectoryInstallLocation.prototype = {
     if (this._stagingDirPromise)
       return this._stagingDirPromise;
 
     OS.File.makeDir(this._directory.path);
     let stagepath = OS.Path.join(this._directory.path, DIR_STAGE);
     return this._stagingDirPromise = OS.File.makeDir(stagepath).then(null, (e) => {
       if (e instanceof OS.File.Error && e.becauseExists)
         return;
-      ERROR("Failed to create staging directory", e);
+      logger.error("Failed to create staging directory", e);
       throw e;
     });
   },
 
   releaseStagingDir: function() {
     this._stagingDirLock--;
 
     if (this._stagingDirLock == 0) {
@@ -7059,17 +7053,17 @@ DirectoryInstallLocation.prototype = {
       dirEntries.close();
     }
 
     try {
       setFilePermissions(dir, FileUtils.PERMS_DIRECTORY);
       dir.remove(false);
     }
     catch (e) {
-      WARN("Failed to remove staging dir", e);
+      logger.warn("Failed to remove staging dir", e);
       // Failing to remove the staging directory is ignorable
     }
   },
 
   /**
    * Gets the directory used by old versions for staging XPI and JAR files ready
    * to be installed.
    *
@@ -7154,26 +7148,26 @@ DirectoryInstallLocation.prototype = {
     }
     finally {
       // It isn't ideal if this cleanup fails but it isn't worth rolling back
       // the install because of it.
       try {
         recursiveRemove(trashDir);
       }
       catch (e) {
-        WARN("Failed to remove trash directory when installing " + aId, e);
+        logger.warn("Failed to remove trash directory when installing " + aId, e);
       }
     }
 
     let newFile = this._directory.clone();
     newFile.append(aSource.leafName);
     try {
       newFile.lastModifiedTime = Date.now();
     } catch (e)  {
-      WARN("failed to set lastModifiedTime on " + newFile.path, e);
+      logger.warn("failed to set lastModifiedTime on " + newFile.path, e);
     }
     this._FileToIDMap[newFile.path] = aId;
     this._IDToFileMap[aId] = newFile;
 
     if (aExistingAddonID && aExistingAddonID != aId &&
         aExistingAddonID in this._IDToFileMap) {
       delete this._FileToIDMap[this._IDToFileMap[aExistingAddonID]];
       delete this._IDToFileMap[aExistingAddonID];
@@ -7187,55 +7181,55 @@ DirectoryInstallLocation.prototype = {
    *
    * @param  aId
    *         The ID of the add-on to uninstall
    * @throws if the ID does not match any of the add-ons installed
    */
   uninstallAddon: function DirInstallLocation_uninstallAddon(aId) {
     let file = this._IDToFileMap[aId];
     if (!file) {
-      WARN("Attempted to remove " + aId + " from " +
+      logger.warn("Attempted to remove " + aId + " from " +
            this._name + " but it was already gone");
       return;
     }
 
     file = this._directory.clone();
     file.append(aId);
     if (!file.exists())
       file.leafName += ".xpi";
 
     if (!file.exists()) {
-      WARN("Attempted to remove " + aId + " from " +
+      logger.warn("Attempted to remove " + aId + " from " +
            this._name + " but it was already gone");
 
       delete this._FileToIDMap[file.path];
       delete this._IDToFileMap[aId];
       return;
     }
 
     let trashDir = this.getTrashDir();
 
     if (file.leafName != aId) {
-      LOG("uninstallAddon: flushing jar cache " + file.path + " for addon " + aId);
+      logger.debug("uninstallAddon: flushing jar cache " + file.path + " for addon " + aId);
       flushJarCache(file);
     }
 
     let transaction = new SafeInstallOperation();
 
     try {
       transaction.move(file, trashDir);
     }
     finally {
       // It isn't ideal if this cleanup fails, but it is probably better than
       // rolling back the uninstall at this point
       try {
         recursiveRemove(trashDir);
       }
       catch (e) {
-        WARN("Failed to remove trash directory when uninstalling " + aId, e);
+        logger.warn("Failed to remove trash directory when uninstalling " + aId, e);
       }
     }
 
     delete this._FileToIDMap[file.path];
     delete this._IDToFileMap[aId];
   },
 
   /**
@@ -7355,17 +7349,17 @@ WinRegInstallLocation.prototype = {
     for (let i = 0; i < count; ++i) {
       let id = aKey.getValueName(i);
 
       let file = Cc["@mozilla.org/file/local;1"].
                 createInstance(Ci.nsIFile);
       file.initWithPath(aKey.readStringValue(id));
 
       if (!file.exists()) {
-        WARN("Ignoring missing add-on in " + file.path);
+        logger.warn("Ignoring missing add-on in " + file.path);
         continue;
       }
 
       this._IDToFileMap[id] = file;
       this._FileToIDMap[file.path] = id;
     }
   },
 
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -19,28 +19,22 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
                                   "resource://gre/modules/DeferredSave.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 
-["LOG", "WARN", "ERROR"].forEach(function(aName) {
-  Object.defineProperty(this, aName, {
-    get: function logFuncGetter () {
-      Cu.import("resource://gre/modules/addons/AddonLogging.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+const LOGGER_ID = "addons.xpi-utils";
 
-      LogManager.getLogger("addons.xpi-utils", this);
-      return this[aName];
-    },
-    configurable: true
-  });
-}, this);
-
+// Create a new logger for use by the Addons XPI Provider Utils
+// (Requires AddonManager.jsm)
+let logger = Log.repository.getLogger(LOGGER_ID);
 
 const KEY_PROFILEDIR                  = "ProfD";
 const FILE_DATABASE                   = "extensions.sqlite";
 const FILE_JSON_DB                    = "extensions.json";
 const FILE_OLD_DATABASE               = "extensions.rdf";
 const FILE_XPI_ADDONS_LIST            = "extensions.ini";
 
 // The value for this is in Makefile.in
@@ -147,17 +141,17 @@ function getRepositoryAddon(aAddon, aCal
  * Wrap an API-supplied function in an exception handler to make it safe to call
  */
 function makeSafe(aCallback) {
   return function(...aArgs) {
     try {
       aCallback(...aArgs);
     }
     catch(ex) {
-      WARN("XPI Database callback failed", ex);
+      logger.warn("XPI Database callback failed", ex);
     }
   }
 }
 
 /**
  * A helper method to asynchronously call a function on an array
  * of objects, calling a callback when function(x) has been gathered
  * for every element of the array.
@@ -190,17 +184,17 @@ function asyncMap(aObjects, aMethod, aCa
 
   aObjects.map(function asyncMap_each(aObject, aIndex, aArray) {
     try {
       aMethod(aObject, function asyncMap_callback(aResult) {
         asyncMap_gotValue(aIndex, aResult);
       });
     }
     catch (e) {
-      WARN("Async map function failed", e);
+      logger.warn("Async map function failed", e);
       asyncMap_gotValue(aIndex, undefined);
     }
   });
 }
 
 /**
  * A generator to synchronously return result rows from an mozIStorageStatement.
  *
@@ -222,17 +216,17 @@ function resultRows(aStatement) {
  * A helper function to log an SQL error.
  *
  * @param  aError
  *         The storage error code associated with the error
  * @param  aErrorString
  *         An error message
  */
 function logSQLError(aError, aErrorString) {
-  ERROR("SQL error " + aError + ": " + aErrorString);
+  logger.error("SQL error " + aError + ": " + aErrorString);
 }
 
 /**
  * A helper function to log any errors that occur during async statements.
  *
  * @param  aError
  *         A mozIStorageError to log
  */
@@ -437,25 +431,25 @@ this.XPIDatabase = {
 
     let promise = this._deferredSave.saveChanges();
     if (!this._schemaVersionSet) {
       this._schemaVersionSet = true;
       promise.then(
         count => {
           // Update the XPIDB schema version preference the first time we successfully
           // save the database.
-          LOG("XPI Database saved, setting schema version preference to " + DB_SCHEMA);
+          logger.debug("XPI Database saved, setting schema version preference to " + DB_SCHEMA);
           Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA);
           // Reading the DB worked once, so we don't need the load error
           this._loadError = null;
         },
         error => {
           // Need to try setting the schema version again later
           this._schemaVersionSet = false;
-          WARN("Failed to save XPI database", error);
+          logger.warn("Failed to save XPI database", error);
           // this._deferredSave.lastError has the most recent error so we don't
           // need this any more
           this._loadError = null;
         });
     }
   },
 
   flush: function() {
@@ -497,20 +491,20 @@ this.XPIDatabase = {
   getMigrateDataFromSQLITE: function XPIDB_getMigrateDataFromSQLITE() {
     let connection = null;
     let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
     // Attempt to open the database
     try {
       connection = Services.storage.openUnsharedDatabase(dbfile);
     }
     catch (e) {
-      WARN("Failed to open sqlite database " + dbfile.path + " for upgrade", e);
+      logger.warn("Failed to open sqlite database " + dbfile.path + " for upgrade", e);
       return null;
     }
-    LOG("Migrating data from sqlite");
+    logger.debug("Migrating data from sqlite");
     let migrateData = this.getMigrateDataFromDatabase(connection);
     connection.close();
     return migrateData;
   },
 
   /**
    * Synchronously opens and reads the database file, upgrading from old
    * databases or making a new DB if needed.
@@ -530,17 +524,17 @@ this.XPIDatabase = {
    *         (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
   syncLoadDB: function XPIDB_syncLoadDB(aRebuildOnError) {
     this.migrateData = null;
     let fstream = null;
     let data = "";
     try {
       let readTimer = AddonManagerPrivate.simpleTimer("XPIDB_syncRead_MS");
-      LOG("Opening XPI database " + this.jsonFile.path);
+      logger.debug("Opening XPI database " + this.jsonFile.path);
       fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
               createInstance(Components.interfaces.nsIFileInputStream);
       fstream.init(this.jsonFile, -1, 0, 0);
       let cstream = null;
       try {
         cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
                 createInstance(Components.interfaces.nsIConverterInputStream);
         cstream.init(fstream, "UTF-8", 0, 0);
@@ -550,17 +544,17 @@ this.XPIDatabase = {
             read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
             data += str.value;
           } while (read != 0);
         }
         readTimer.done();
         this.parseDB(data, aRebuildOnError);
       }
       catch(e) {
-        ERROR("Failed to load XPI JSON data from profile", e);
+        logger.error("Failed to load XPI JSON data from profile", e);
         let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
         this.rebuildDatabase(aRebuildOnError);
         rebuildTimer.done();
       }
       finally {
         if (cstream)
           cstream.close();
       }
@@ -596,57 +590,57 @@ this.XPIDatabase = {
     let parseTimer = AddonManagerPrivate.simpleTimer("XPIDB_parseDB_MS");
     try {
       // dump("Loaded JSON:\n" + aData + "\n");
       let inputAddons = JSON.parse(aData);
       // Now do some sanity checks on our JSON db
       if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
         parseTimer.done();
         // Content of JSON file is bad, need to rebuild from scratch
-        ERROR("bad JSON file contents");
+        logger.error("bad JSON file contents");
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "badJSON");
         let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildBadJSON_MS");
         this.rebuildDatabase(aRebuildOnError);
         rebuildTimer.done();
         return;
       }
       if (inputAddons.schemaVersion != DB_SCHEMA) {
         // Handle mismatched JSON schema version. For now, we assume
         // compatibility for JSON data, though we throw away any fields we
         // don't know about (bug 902956)
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError",
                                                 "schemaMismatch-" + inputAddons.schemaVersion);
-        LOG("JSON schema mismatch: expected " + DB_SCHEMA +
+        logger.debug("JSON schema mismatch: expected " + DB_SCHEMA +
             ", actual " + inputAddons.schemaVersion);
         // When we rev the schema of the JSON database, we need to make sure we
         // force the DB to save so that the DB_SCHEMA value in the JSON file and
         // the preference are updated.
       }
       // If we got here, we probably have good data
       // Make AddonInternal instances from the loaded data and save them
       let addonDB = new Map();
       for (let loadedAddon of inputAddons.addons) {
         let newAddon = new DBAddonInternal(loadedAddon);
         addonDB.set(newAddon._key, newAddon);
       };
       parseTimer.done();
       this.addonDB = addonDB;
-      LOG("Successfully read XPI database");
+      logger.debug("Successfully read XPI database");
       this.initialized = true;
     }
     catch(e) {
       // If we catch and log a SyntaxError from the JSON
       // parser, the xpcshell test harness fails the test for us: bug 870828
       parseTimer.done();
       if (e.name == "SyntaxError") {
-        ERROR("Syntax error parsing saved XPI JSON data");
+        logger.error("Syntax error parsing saved XPI JSON data");
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "syntax");
       }
       else {
-        ERROR("Failed to load XPI JSON data from profile", e);
+        logger.error("Failed to load XPI JSON data from profile", e);
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "other");
       }
       let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
       this.rebuildDatabase(aRebuildOnError);
       rebuildTimer.done();
     }
   },
 
@@ -654,17 +648,17 @@ this.XPIDatabase = {
    * Upgrade database from earlier (sqlite or RDF) version if available
    */
   upgradeDB: function(aRebuildOnError) {
     let upgradeTimer = AddonManagerPrivate.simpleTimer("XPIDB_upgradeDB_MS");
     try {
       let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA);
       if (schemaVersion <= LAST_SQLITE_DB_SCHEMA) {
         // we should have an older SQLITE database
-        LOG("Attempting to upgrade from SQLITE database");
+        logger.debug("Attempting to upgrade from SQLITE database");
         this.migrateData = this.getMigrateDataFromSQLITE();
       }
       else {
         // we've upgraded before but the JSON file is gone, fall through
         // and rebuild from scratch
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "dbMissing");
       }
     }
@@ -679,17 +673,17 @@ this.XPIDatabase = {
   },
 
   /**
    * Reconstruct when the DB file exists but is unreadable
    * (for example because read permission is denied)
    */
   rebuildUnreadableDB: function(aError, aRebuildOnError) {
     let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildUnreadableDB_MS");
-    WARN("Extensions database " + this.jsonFile.path +
+    logger.warn("Extensions database " + this.jsonFile.path +
         " exists but is not readable; rebuilding", aError);
     // Remember the error message until we try and write at least once, so
     // we know at shutdown time that there was a problem
     this._loadError = aError;
     AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "unreadable");
     this.rebuildDatabase(aRebuildOnError);
     rebuildTimer.done();
   },
@@ -703,42 +697,42 @@ this.XPIDatabase = {
    *         in this.addonDB; never rejects.
    */
   asyncLoadDB: function XPIDB_asyncLoadDB() {
     // Already started (and possibly finished) loading
     if (this._dbPromise) {
       return this._dbPromise;
     }
 
-    LOG("Starting async load of XPI database " + this.jsonFile.path);
+    logger.debug("Starting async load of XPI database " + this.jsonFile.path);
     AddonManagerPrivate.recordSimpleMeasure("XPIDB_async_load", XPIProvider.runPhase);
     let readOptions = {
       outExecutionDuration: 0
     };
     return this._dbPromise = OS.File.read(this.jsonFile.path, null, readOptions).then(
       byteArray => {
-        LOG("Async JSON file read took " + readOptions.outExecutionDuration + " MS");
+        logger.debug("Async JSON file read took " + readOptions.outExecutionDuration + " MS");
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_asyncRead_MS",
           readOptions.outExecutionDuration);
         if (this._addonDB) {
-          LOG("Synchronous load completed while waiting for async load");
+          logger.debug("Synchronous load completed while waiting for async load");
           return this.addonDB;
         }
-        LOG("Finished async read of XPI database, parsing...");
+        logger.debug("Finished async read of XPI database, parsing...");
         let decodeTimer = AddonManagerPrivate.simpleTimer("XPIDB_decode_MS");
         let decoder = new TextDecoder();
         let data = decoder.decode(byteArray);
         decodeTimer.done();
         this.parseDB(data, true);
         return this.addonDB;
       })
     .then(null,
       error => {
         if (this._addonDB) {
-          LOG("Synchronous load completed while waiting for async load");
+          logger.debug("Synchronous load completed while waiting for async load");
           return this.addonDB;
         }
         if (error.becauseNoSuchFile) {
           this.upgradeDB(true);
         }
         else {
           // it's there but unreadable
           this.rebuildUnreadableDB(error, true);
@@ -757,32 +751,32 @@ this.XPIDatabase = {
    *         (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
   rebuildDatabase: function XIPDB_rebuildDatabase(aRebuildOnError) {
     this.addonDB = new Map();
     this.initialized = true;
 
     if (XPIProvider.installStates && XPIProvider.installStates.length == 0) {
       // No extensions installed, so we're done
-      LOG("Rebuilding XPI database with no extensions");
+      logger.debug("Rebuilding XPI database with no extensions");
       return;
     }
 
     // If there is no migration data then load the list of add-on directories
     // that were active during the last run
     if (!this.migrateData)
       this.activeBundles = this.getActiveBundles();
 
     if (aRebuildOnError) {
-      WARN("Rebuilding add-ons database from installed extensions.");
+      logger.warn("Rebuilding add-ons database from installed extensions.");
       try {
         XPIProvider.processFileChanges(XPIProvider.installStates, {}, false);
       }
       catch (e) {
-        ERROR("Failed to rebuild XPI database from installed extensions", e);
+        logger.error("Failed to rebuild XPI database from installed extensions", e);
       }
       // Make sure to update the active add-ons and add-ons list on shutdown
       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
     }
   },
 
   /**
    * Gets the list of file descriptors of active extension directories or XPI
@@ -809,17 +803,17 @@ this.XPIDatabase = {
                          .getService(Ci.nsIINIParserFactory);
       let parser = iniFactory.createINIParser(addonsList);
       let keys = parser.getKeys("ExtensionDirs");
 
       while (keys.hasMore())
         bundles.push(parser.getString("ExtensionDirs", keys.getNext()));
     }
     catch (e) {
-      WARN("Failed to parse extensions.ini", e);
+      logger.warn("Failed to parse extensions.ini", e);
       return null;
     }
 
     // Also include the list of active bootstrapped extensions
     for (let id in XPIProvider.bootstrappedAddons)
       bundles.push(XPIProvider.bootstrappedAddons[id].descriptor);
 
     return bundles;
@@ -833,17 +827,17 @@ this.XPIDatabase = {
    */
   getMigrateDataFromRDF: function XPIDB_getMigrateDataFromRDF(aDbWasMissing) {
 
     // Migrate data from extensions.rdf
     let rdffile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_DATABASE], true);
     if (!rdffile.exists())
       return null;
 
-    LOG("Migrating data from " + FILE_OLD_DATABASE);
+    logger.debug("Migrating data from " + FILE_OLD_DATABASE);
     let migrateData = {};
 
     try {
       let ds = gRDF.GetDataSourceBlocking(Services.io.newFileURI(rdffile).spec);
       let root = Cc["@mozilla.org/rdf/container;1"].
                  createInstance(Ci.nsIRDFContainer);
       root.Init(ds, gRDF.GetResource(RDFURI_ITEM_ROOT));
       let elements = root.GetElements();
@@ -885,17 +879,17 @@ this.XPIDatabase = {
               appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion");
             }
             migrateData[location][id].targetApplications.push(appInfo);
           }
         }
       }
     }
     catch (e) {
-      WARN("Error reading " + FILE_OLD_DATABASE, e);
+      logger.warn("Error reading " + FILE_OLD_DATABASE, e);
       migrateData = null;
     }
 
     return migrateData;
   },
 
   /**
    * Retrieves migration data from a database that has an older or newer schema.
@@ -925,17 +919,17 @@ this.XPIDatabase = {
           props.push(row.name);
         }
         else if (DB_BOOL_METADATA.indexOf(row.name) != -1) {
           props.push(row.name);
         }
       }
 
       if (reqCount < REQUIRED.length) {
-        ERROR("Unable to read anything useful from the database");
+        logger.error("Unable to read anything useful from the database");
         return null;
       }
       stmt.finalize();
 
       stmt = aConnection.createStatement("SELECT " + props.join(",") + " FROM addon");
       for (let row in resultRows(stmt)) {
         if (!(row.location in migrateData))
           migrateData[row.location] = {};
@@ -970,17 +964,17 @@ this.XPIDatabase = {
               maxVersion: row.maxVersion
             });
           }
         }
       }
     }
     catch (e) {
       // An error here means the schema is too different to read
-      ERROR("Error migrating data", e);
+      logger.error("Error migrating data", e);
       return null;
     }
     finally {
       if (taStmt)
         taStmt.finalize();
       if (stmt)
         stmt.finalize();
     }
@@ -990,17 +984,17 @@ this.XPIDatabase = {
 
   /**
    * Shuts down the database connection and releases all cached objects.
    * Return: Promise{integer} resolves / rejects with the result of the DB
    *                          flush after the database is flushed and
    *                          all cleanup is done
    */
   shutdown: function XPIDB_shutdown() {
-    LOG("shutdown");
+    logger.debug("shutdown");
     if (this.initialized) {
       // If our last database I/O had an error, try one last time to save.
       if (this.lastError)
         this.saveChanges();
 
       this.initialized = false;
 
       if (this._deferredSave) {
@@ -1011,17 +1005,17 @@ this.XPIDatabase = {
         AddonManagerPrivate.recordSimpleMeasure(
             "XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0);
       }
 
       // Return a promise that any pending writes of the DB are complete and we
       // are finished cleaning up
       let flushPromise = this.flush();
       flushPromise.then(null, error => {
-          ERROR("Flush of XPI database failed", error);
+          logger.error("Flush of XPI database failed", error);
           AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
           // If our last attempt to read or write the DB failed, force a new
           // extensions.ini to be written to disk on the next startup
           Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
         })
         .then(count => {
           // Clear out the cached addons data loaded from JSON
           delete this.addonDB;
@@ -1068,17 +1062,17 @@ this.XPIDatabase = {
   getAddonList: function(aFilter, aCallback) {
     this.asyncLoadDB().then(
       addonDB => {
         let addonList = _filterDB(addonDB, aFilter);
         asyncMap(addonList, getRepositoryAddon, makeSafe(aCallback));
       })
     .then(null,
         error => {
-          ERROR("getAddonList failed", e);
+          logger.error("getAddonList failed", e);
           makeSafe(aCallback)([]);
         });
   },
 
   /**
    * (Possibly asynchronously) get the first addon that matches the filter function
    * @param  aFilter
    *         Function that takes an addon instance and returns
@@ -1088,17 +1082,17 @@ this.XPIDatabase = {
    */
   getAddon: function(aFilter, aCallback) {
     return this.asyncLoadDB().then(
       addonDB => {
         getRepositoryAddon(_findAddon(addonDB, aFilter), makeSafe(aCallback));
       })
     .then(null,
         error => {
-          ERROR("getAddon failed", e);
+          logger.error("getAddon failed", e);
           makeSafe(aCallback)(null);
         });
   },
 
   /**
    * Synchronously reads all the add-ons in a particular install location.
    * Always called with the addon database already loaded.
    *
@@ -1162,34 +1156,34 @@ this.XPIDatabase = {
    *         The type of add-on to retrieve
    * @return an array of DBAddonInternals
    */
   getAddonsByType: function XPIDB_getAddonsByType(aType) {
     if (!this.addonDB) {
       // jank-tastic! Must synchronously load DB if the theme switches from
       // an XPI theme to a lightweight theme before the DB has loaded,
       // because we're called from sync XPIProvider.addonChanged
-      WARN("Synchronous load of XPI database due to getAddonsByType(" + aType + ")");
+      logger.warn("Synchronous load of XPI database due to getAddonsByType(" + aType + ")");
       AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_byType", XPIProvider.runPhase);
       this.syncLoadDB(true);
     }
     return _filterDB(this.addonDB, aAddon => (aAddon.type == aType));
   },
 
   /**
    * Synchronously gets an add-on with a particular internalName.
    *
    * @param  aInternalName
    *         The internalName of the add-on to retrieve
    * @return a DBAddonInternal
    */
   getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) {
     if (!this.addonDB) {
       // This may be called when the DB hasn't otherwise been loaded
-      WARN("Synchronous load of XPI database due to getVisibleAddonForInternalName");
+      logger.warn("Synchronous load of XPI database due to getVisibleAddonForInternalName");
       AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_forInternalName",
           XPIProvider.runPhase);
       this.syncLoadDB(true);
     }
     
     return _findAddon(this.addonDB,
                       aAddon => aAddon.visible &&
                                 (aAddon.internalName == aInternalName));
@@ -1316,20 +1310,20 @@ this.XPIDatabase = {
    * instances with the same ID as not visible.
    *
    * @param  aAddon
    *         The DBAddonInternal to make visible
    * @param  callback
    *         A callback to pass the DBAddonInternal to
    */
   makeAddonVisible: function XPIDB_makeAddonVisible(aAddon) {
-    LOG("Make addon " + aAddon._key + " visible");
+    logger.debug("Make addon " + aAddon._key + " visible");
     for (let [, otherAddon] of this.addonDB) {
       if ((otherAddon.id == aAddon.id) && (otherAddon._key != aAddon._key)) {
-        LOG("Hide addon " + otherAddon._key);
+        logger.debug("Hide addon " + otherAddon._key);
         otherAddon.visible = false;
       }
     }
     aAddon.visible = true;
     this.saveChanges();
   },
 
   /**
@@ -1373,34 +1367,34 @@ this.XPIDatabase = {
 
   /**
    * Synchronously updates an add-on's active flag in the database.
    *
    * @param  aAddon
    *         The DBAddonInternal to update
    */
   updateAddonActive: function XPIDB_updateAddonActive(aAddon, aActive) {
-    LOG("Updating active state for add-on " + aAddon.id + " to " + aActive);
+    logger.debug("Updating active state for add-on " + aAddon.id + " to " + aActive);
 
     aAddon.active = aActive;
     this.saveChanges();
   },
 
   /**
    * Synchronously calculates and updates all the active flags in the database.
    */
   updateActiveAddons: function XPIDB_updateActiveAddons() {
     if (!this.addonDB) {
-      WARN("updateActiveAddons called when DB isn't loaded");
+      logger.warn("updateActiveAddons called when DB isn't loaded");
       // force the DB to load
       AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_updateActive",
           XPIProvider.runPhase);
       this.syncLoadDB(true);
     }
-    LOG("Updating add-on states");
+    logger.debug("Updating add-on states");
     for (let [, addon] of this.addonDB) {
       let newActive = (addon.visible && !addon.userDisabled &&
                       !addon.softDisabled && !addon.appDisabled &&
                       !addon.pendingUninstall);
       if (newActive != addon.active) {
         addon.active = newActive;
         this.saveChanges();
       }
@@ -1467,42 +1461,42 @@ this.XPIDatabase = {
         text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
         enabledAddons.push(encodeURIComponent(row.id) + ":" +
                            encodeURIComponent(row.version));
       }
       fullCount += count;
     }
 
     if (fullCount > 0) {
-      LOG("Writing add-ons list");
+      logger.debug("Writing add-ons list");
 
       try {
         let addonsListTmp = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST + ".tmp"],
                                               true);
         var fos = FileUtils.openFileOutputStream(addonsListTmp);
         fos.write(text, text.length);
         fos.close();
         addonsListTmp.moveTo(addonsListTmp.parent, FILE_XPI_ADDONS_LIST);
 
         Services.prefs.setCharPref(PREF_EM_ENABLED_ADDONS, enabledAddons.join(","));
       }
       catch (e) {
-        ERROR("Failed to write add-ons list to " + addonsListTmp.parent + "/" +
+        logger.error("Failed to write add-ons list to " + addonsListTmp.parent + "/" +
               FILE_XPI_ADDONS_LIST, e);
         return false;
       }
     }
     else {
       if (addonsList.exists()) {
-        LOG("Deleting add-ons list");
+        logger.debug("Deleting add-ons list");
         try {
           addonsList.remove(false);
         }
         catch (e) {
-          ERROR("Failed to remove " + addonsList.path, e);
+          logger.error("Failed to remove " + addonsList.path, e);
           return false;
         }
       }
 
       Services.prefs.clearUserPref(PREF_EM_ENABLED_ADDONS);
     }
     return true;
   }