Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #6993 from dscravag/nightly2

Nightly 2012-12-13
  • Loading branch information...
commit 47990e89ad5710224a771cf64751ed84935433cc 2 parents db43572 + ba1f140
David Scravaglieri authored December 12, 2012

Showing 66 changed files with 1,536 additions and 432 deletions. Show diff stats Hide diff stats

  1. 6  Makefile
  2. 9  apps/browser/js/browser.js
  3. 80  apps/communications/contacts/js/fb/fb_contact_utils.js
  4. 356  apps/communications/contacts/js/fb/fb_data.js
  5. 9  apps/communications/contacts/js/fb/fb_init.js
  6. 43  apps/communications/contacts/js/fb/fb_utils.js
  7. 2  apps/communications/contacts/js/fb/friends_list.js
  8. 3  apps/communications/dialer/index.html
  9. 98  apps/communications/dialer/js/contacts.js
  10. 4  apps/communications/dialer/oncall.html
  11. 2  apps/communications/ftu/js/wifi.js
  12. 2  apps/email/js/ext/gaia-email-opt.js
  13. 13  apps/gallery/js/MetadataParser.js
  14. 151  apps/gallery/js/gallery.js
  15. 1  apps/gallery/style/gallery.css
  16. 2  apps/settings/index.html
  17. 82  apps/settings/js/bluetooth.js
  18. 2  apps/settings/js/connectivity.js
  19. 32  apps/settings/js/settings.js
  20. 14  apps/settings/style/settings.css
  21. 22  apps/system/js/quick_settings.js
  22. 4  apps/system/style/quick_settings/quick_settings.css
  23. 4  build/settings.py
  24. 26  shared/js/l10n.js
  25. 4  shared/js/media/jpeg_metadata_parser.js
  26. 57  tests/atoms/gaia_apps.js
  27. 161  tests/atoms/gaia_data_layer.js
  28. 57  tests/python/README.md
  29. 59  tests/python/gaiatest/gaia_test.py
  30. 6  tests/python/gaiatest/tests/contacts/manifest.ini
  31. 2  tests/python/gaiatest/tests/contacts/test_add_new_contact.py
  32. 10  tests/python/gaiatest/tests/contacts/test_call_contact.py
  33. 2  tests/python/gaiatest/tests/contacts/test_edit_contact.py
  34. 2  tests/python/gaiatest/tests/contacts/test_sms_contact.py
  35. 38  tests/python/gaiatest/tests/manifest.ini
  36. 2  tests/python/gaiatest/tests/marketplace/manifest.ini
  37. 12  tests/python/gaiatest/tests/marketplace/test_marketplace_login.py
  38. 28  tests/python/gaiatest/tests/marketplace/test_search_marketplace_and_install_app.py
  39. 8  tests/python/gaiatest/tests/test_browser_cell_data.py
  40. 57  tests/python/gaiatest/tests/test_browser_lan.py
  41. 3  tests/python/gaiatest/tests/test_calculator.py
  42. 86  tests/python/gaiatest/tests/test_calendar.py
  43. 3  tests/python/gaiatest/tests/test_call_log.py
  44. 11  tests/python/gaiatest/tests/test_camera.py
  45. 28  tests/python/gaiatest/tests/test_cards_view.py
  46. 3  tests/python/gaiatest/tests/test_clock.py
  47. 20  tests/python/gaiatest/tests/test_dialer.py
  48. 3  tests/python/gaiatest/tests/test_gallery.py
  49. 51  tests/python/gaiatest/tests/test_launch_app.py
  50. 10  tests/python/gaiatest/tests/test_lockscreen.py
  51. 15  tests/python/gaiatest/tests/test_music.py
  52. 55  tests/python/gaiatest/tests/test_radio.py
  53. 3  tests/python/gaiatest/tests/test_sms.py
  54. 3  tests/python/gaiatest/tests/test_updater.py
  55. 13  tests/python/gaiatest/tests/test_video_player.py
  56. 7  tests/python/gaiatest/tests/unit/manifest.ini
  57. 5  tests/python/gaiatest/tests/unit/settings/manifest.ini
  58. 26  tests/python/gaiatest/tests/unit/settings/test_carrier_settings.py
  59. 27  tests/python/gaiatest/tests/unit/settings/test_settings.py
  60. 17  tests/python/gaiatest/tests/unit/settings/test_wifi_settings.py
  61. 52  tests/python/gaiatest/tests/unit/test_initial_state.py
  62. 30  tests/python/gaiatest/tests/unit/test_kill.py
  63. 5  tests/python/gaiatest/tests/unit/test_killall.py
  64. 6  tests/python/gaiatest/tests/unit/test_launch_entrypoint.py
  65. 1  tests/python/gaiatest/tests/unit/test_lock_screen.py
  66. 13  tests/python/gaiatest/tests/unit/test_wifi.py
6  Makefile
@@ -611,8 +611,12 @@ purge:
611 611
 	$(ADB) shell rm -r $(MSYS_FIX)$(GAIA_INSTALL_PARENT)/webapps
612 612
 
613 613
 # Build the settings.json file from settings.py
  614
+ifeq ($(NOFTU), 1)
  615
+SETTINGS_ARG=--noftu
  616
+endif
  617
+
614 618
 profile/settings.json: build/settings.py build/wallpaper.jpg
615  
-	python build/settings.py --console --homescreen $(SCHEME)homescreen.$(GAIA_DOMAIN)$(GAIA_PORT)/manifest.webapp --ftu $(SCHEME)communications.$(GAIA_DOMAIN)$(GAIA_PORT)/manifest.webapp --wallpaper build/wallpaper.jpg --output $@
  619
+	python build/settings.py $(SETTINGS_ARG) --console --homescreen $(SCHEME)homescreen.$(GAIA_DOMAIN)$(GAIA_PORT)/manifest.webapp --ftu $(SCHEME)communications.$(GAIA_DOMAIN)$(GAIA_PORT)/manifest.webapp --wallpaper build/wallpaper.jpg --output $@
616 620
 
617 621
 # push profile/settings.json to the phone
618 622
 install-settings-defaults: profile/settings.json
9  apps/browser/js/browser.js
@@ -1313,7 +1313,6 @@ var Browser = {
1313 1313
     if (length == 1)
1314 1314
       places.push({uri: '', title: ''});
1315 1315
 
1316  
-    var self = this;
1317 1316
     places.forEach(function processPlace(place) {
1318 1317
       var thumbnail = document.createElement('li');
1319 1318
       var link = document.createElement('a');
@@ -1321,9 +1320,11 @@ var Browser = {
1321 1320
       link.href = place.uri;
1322 1321
       title.textContent = place.title ? place.title : place.uri;
1323 1322
 
1324  
-      var objectURL = URL.createObjectURL(place.screenshot);
1325  
-      self._topSiteThumbnailObjectURLs.push(objectURL);
1326  
-      link.style.backgroundImage = 'url(' + objectURL + ')';
  1323
+      if (place.screenshot) {
  1324
+        var objectURL = URL.createObjectURL(place.screenshot);
  1325
+        this._topSiteThumbnailObjectURLs.push(objectURL);
  1326
+        link.style.backgroundImage = 'url(' + objectURL + ')';
  1327
+      }
1327 1328
 
1328 1329
       thumbnail.appendChild(link);
1329 1330
       thumbnail.appendChild(title);
80  apps/communications/contacts/js/fb/fb_contact_utils.js
@@ -209,3 +209,83 @@ fb.getAddress = function(fbdata) {
209 209
 
210 210
   return out;
211 211
 };
  212
+
  213
+// Merge done specifically for dialer and Call Log apps
  214
+fb.mergeContact = function(devContact, fbContact) {
  215
+  var fbPhotos = fbContact.photo;
  216
+  if (!devContact.photo && Array.isArray(fbPhotos)) {
  217
+    devContact.photo = [];
  218
+  }
  219
+
  220
+  if (Array.isArray(fbPhotos) && fbPhotos.length > 0 && fbPhotos[0]) {
  221
+    devContact.photo.push(fbPhotos[0]);
  222
+  }
  223
+
  224
+  if (!devContact.tel && Array.isArray(fbContact.tel)) {
  225
+    devContact.tel = [];
  226
+  }
  227
+
  228
+  if (Array.isArray(fbContact.tel)) {
  229
+    fbContact.tel.forEach(function(atel) {
  230
+      devContact.tel.push(atel);
  231
+    });
  232
+  }
  233
+
  234
+  return devContact;
  235
+};
  236
+
  237
+fb.getContactByNumber = function(number, onsuccess, onerror) {
  238
+  var req = fb.contacts.getByPhone(number);
  239
+
  240
+  req.onsuccess = function(e) {
  241
+    onsuccess(e.target.result);
  242
+  };
  243
+
  244
+  req.onerror = onerror;
  245
+};
  246
+
  247
+// Only will be executed in the case of not loading fb.utils previously
  248
+// i.e. dialer and call log FB integration
  249
+var fb = window.fb || {};
  250
+fb.utils = window.fb.utils || {};
  251
+
  252
+// Returns the mozContact associated to a UID in FB
  253
+fb.utils.getMozContactByUid = function(uid, onsuccess, onerror) {
  254
+  var filter = {
  255
+    filterBy: ['category'],
  256
+    filterValue: uid,
  257
+    filterOp: 'contains'
  258
+  };
  259
+
  260
+  var req = navigator.mozContacts.find(filter);
  261
+  req.onsuccess = onsuccess;
  262
+  req.onerror = onerror;
  263
+};
  264
+
  265
+ /**
  266
+  *   Request auxiliary object to support asynchronous calls
  267
+  *
  268
+  */
  269
+fb.utils.Request = function() {
  270
+  this.done = function(result) {
  271
+    this.result = result;
  272
+    if (typeof this.onsuccess === 'function') {
  273
+      var ev = {};
  274
+      ev.target = this;
  275
+      window.setTimeout(function() {
  276
+        this.onsuccess(ev);
  277
+      }.bind(this), 0);
  278
+    }
  279
+  }
  280
+
  281
+  this.failed = function(error) {
  282
+    this.error = error;
  283
+    if (typeof this.onerror === 'function') {
  284
+      var ev = {};
  285
+      ev.target = this;
  286
+      window.setTimeout(function() {
  287
+        this.onerror(ev);
  288
+      }.bind(this), 0);
  289
+    }
  290
+  }
  291
+};
356  apps/communications/contacts/js/fb/fb_data.js
@@ -10,17 +10,163 @@ if (!window.fb.contacts) {
10 10
       window.indexedDB;
11 11
 
12 12
     var database;
13  
-    var STORE_NAME = 'FBFriends';
  13
+    var DB_NAME = 'Gaia_Facebook_Friends';
  14
+    var OLD_DB_VERSION = 1.0;
  15
+    var DB_VERSION = 2.0;
  16
+    var OLD_STORE_NAME = 'FBFriends';
  17
+    var STORE_NAME = 'FBFriendsV2';
  18
+    var INDEX_NAME = 'byTelephone';
  19
+    var isInitialized = false;
  20
+    var migrationNeeded = false;
  21
+
  22
+    function DatabaseMigrator(items) {
  23
+      var pointer = 0;
  24
+      var CHUNK_SIZE = 5;
  25
+      var numResponses = 0;
  26
+      var self = this;
  27
+
  28
+      this.items = items;
  29
+
  30
+      function continueCb() {
  31
+        numResponses++;
  32
+        pointer++;
  33
+        continuee();
  34
+      }
14 35
 
  36
+      function continuee() {
  37
+        if (pointer < self.items.length && numResponses === CHUNK_SIZE) {
  38
+          numResponses = 0;
  39
+          migrateSlice(pointer);
  40
+        }
  41
+        else if (pointer >= self.items.length) {
  42
+          if (typeof self.onfinish === 'function') {
  43
+            self.onfinish();
  44
+          }
  45
+        }
  46
+      }
  47
+
  48
+      this.start = function() {
  49
+        if (Array.isArray(self.items) && self.items.length > 0) {
  50
+          migrateSlice(0);
  51
+        }
  52
+        else {
  53
+          if (typeof self.onfinish === 'function') {
  54
+            self.onfinish();
  55
+          }
  56
+        }
  57
+      }
  58
+
  59
+      function migrateSlice(from) {
  60
+        for (var i = from; i < from + CHUNK_SIZE
  61
+             && i < self.items.length; i++) {
  62
+          var req = new fb.utils.Request();
  63
+          var item = self.items[i];
  64
+          doSave(item, req);
  65
+          req.onsuccess = function saveSuccess() {
  66
+            console.log('FB Cache: Success migrating ', item.uid);
  67
+            continueCb();
  68
+          }
  69
+          req.onerror = function saveError() {
  70
+            console.error('FB Cache: Error migrating ', item.uid);
  71
+            continueCb();
  72
+          }
  73
+        }
  74
+      }
  75
+    }
15 76
 
16 77
     /**
17 78
      *  Creates the store
18 79
      *
19 80
      */
20 81
     function createStore(e) {
21  
-      e.target.result.createObjectStore(STORE_NAME, { keyPath: 'uid' });
  82
+      var db = e.target.result;
  83
+      if (db.objectStoreNames.contains(STORE_NAME)) {
  84
+        db.deleteObjectStore(STORE_NAME);
  85
+      }
  86
+      return db.createObjectStore(STORE_NAME, { keyPath: 'uid' });
  87
+    }
  88
+
  89
+    function createStoreAndIndex(e) {
  90
+      var store = createStore(e);
  91
+      store.createIndex(INDEX_NAME, 'telephone', {
  92
+        unique: true,
  93
+        multiEntry: true
  94
+      });
  95
+    }
  96
+
  97
+    function upgradeDB(e) {
  98
+      if (e.oldVersion === OLD_DB_VERSION && e.newVersion === DB_VERSION) {
  99
+        window.console.warn('Upgrading Facebook Cache!!!!!');
  100
+        migrationNeeded = true;
  101
+        createStoreAndIndex(e);
  102
+      }
  103
+      else if (e.newVersion === 1.0) {
  104
+        createStore(e);
  105
+      }
  106
+      else if (e.newVersion === DB_VERSION) {
  107
+        createStoreAndIndex(e);
  108
+      }
  109
+    }
  110
+
  111
+    function clearOldObjStore(cb) {
  112
+      var transaction = database.transaction([OLD_STORE_NAME], 'readwrite');
  113
+      var objectStore = transaction.objectStore(OLD_STORE_NAME);
  114
+
  115
+      var req = objectStore.clear();
  116
+      req.onsuccess = cb;
  117
+      req.onerror = function error_del_objstore(e) {
  118
+        window.console.error('FB Cache. Error while clearing old Obj Store',
  119
+                             e.target.error.name);
  120
+        cb();
  121
+      }
22 122
     }
23 123
 
  124
+    function migrateData(onfinishCb) {
  125
+      if (!database.objectStoreNames.contains(OLD_STORE_NAME)) {
  126
+        onfinishCb();
  127
+        return;
  128
+      }
  129
+
  130
+      var transaction = database.transaction([OLD_STORE_NAME], 'readonly');
  131
+      var objectStore = transaction.objectStore(OLD_STORE_NAME);
  132
+
  133
+      var req = objectStore.mozGetAll();
  134
+
  135
+      req.onsuccess = function(e) {
  136
+        var data = e.target.result;
  137
+
  138
+        var migrator = new DatabaseMigrator(data);
  139
+         migrator.onfinish = function migration_finished() {
  140
+          window.console.log('FB Cache: Migration process finished!!');
  141
+          migrationNeeded = false;
  142
+          clearOldObjStore(onfinishCb);
  143
+        };
  144
+        migrator.start();
  145
+      };
  146
+
  147
+      req.onerror = function(e) {
  148
+        window.console.error('FB Cache: Data migration failed !!!! ');
  149
+        onfinishCb();
  150
+      }
  151
+    }
  152
+
  153
+    function initError(outRequest, error) {
  154
+      outRequest.failed(error);
  155
+    }
  156
+
  157
+    function doGet(uid, outRequest) {
  158
+      var transaction = database.transaction([STORE_NAME], 'readonly');
  159
+      var objectStore = transaction.objectStore(STORE_NAME);
  160
+      var areq = objectStore.get(uid);
  161
+
  162
+      areq.onsuccess = function(e) {
  163
+        outRequest.done(e.target.result);
  164
+      };
  165
+
  166
+      areq.onerror = function(e) {
  167
+        outRequest.failed(e.target.error);
  168
+      }
  169
+    }
24 170
 
25 171
     /**
26 172
      *  Allows to obtain the FB contact information by UID
@@ -30,22 +176,73 @@ if (!window.fb.contacts) {
30 176
     contacts.get = function(uid) {
31 177
       var retRequest = new fb.utils.Request();
32 178
 
33  
-      window.setTimeout(function() {
34  
-        var transaction = database.transaction([STORE_NAME], 'readonly');
35  
-        var objectStore = transaction.objectStore(STORE_NAME);
36  
-        var areq = objectStore.get(uid);
  179
+      window.setTimeout(function get() {
  180
+        contacts.init(function() {
  181
+          doGet(uid, retRequest);
  182
+        }, function() {
  183
+          initError(retRequest);
  184
+        });
  185
+      },0);
37 186
 
38  
-        areq.onsuccess = function(e) {
39  
-          retRequest.done(e.target.result);
40  
-        };
  187
+      return retRequest;
  188
+    }
41 189
 
42  
-        areq.onerror = function(e) {
43  
-          reqRequest.failed(e.target.error);
44  
-        }
  190
+    function doGetByPhone(tel, outRequest) {
  191
+      var transaction = database.transaction([STORE_NAME], 'readonly');
  192
+      var objectStore = transaction.objectStore(STORE_NAME);
45 193
 
46  
-      },0);
  194
+      var index = objectStore.index(INDEX_NAME);
47 195
 
48  
-      return retRequest;
  196
+      var areq = index.get(tel);
  197
+
  198
+      areq.onsuccess = function(e) {
  199
+        outRequest.done(e.target.result);
  200
+      }
  201
+
  202
+      areq.onerror = function(e) {
  203
+        outRequest.failed(e.target.error);
  204
+      }
  205
+    }
  206
+
  207
+    contacts.getByPhone = function(tel) {
  208
+      var outRequest = new fb.utils.Request();
  209
+
  210
+      window.setTimeout(function get_by_phone() {
  211
+        contacts.init(function get_by_phone() {
  212
+          doGetByPhone(tel, outRequest);
  213
+        },
  214
+        function() {
  215
+          initError(outRequest);
  216
+        });
  217
+      }, 0);
  218
+
  219
+      return outRequest;
  220
+    }
  221
+
  222
+    function doSave(obj,outRequest) {
  223
+      var transaction = database.transaction([STORE_NAME], 'readwrite');
  224
+
  225
+      transaction.onerror = function(e) {
  226
+        outRequest.failed(e.target.error);
  227
+      }
  228
+
  229
+      var objectStore = transaction.objectStore(STORE_NAME);
  230
+
  231
+      if (Array.isArray(obj.tel) && obj.tel.length > 0) {
  232
+        obj.telephone = [];
  233
+        obj.tel.forEach(function(atel) {
  234
+          obj.telephone.push(atel.value);
  235
+        });
  236
+      }
  237
+
  238
+      var req = objectStore.put(obj);
  239
+      req.onsuccess = function(e) {
  240
+        outRequest.done(e.target.result);
  241
+      }
  242
+
  243
+      req.onerror = function(e) {
  244
+        outRequest.failed(e.target.error);
  245
+      }
49 246
     }
50 247
 
51 248
     /**
@@ -56,52 +253,66 @@ if (!window.fb.contacts) {
56 253
     contacts.save = function(obj) {
57 254
       var retRequest = new fb.utils.Request();
58 255
 
59  
-      window.setTimeout(function() {
60  
-        var transaction = database.transaction([STORE_NAME], 'readwrite');
  256
+      window.setTimeout(function save() {
  257
+        contacts.init(function() {
  258
+          doSave(obj, retRequest);
  259
+        },
  260
+        function() {
  261
+          initError(retRequest);
  262
+        });
  263
+      },0);
61 264
 
62  
-        transaction.onerror = function(e) {
63  
-          retRequest.failed(e.target.error);
64  
-        }
  265
+      return retRequest;
  266
+    }
65 267
 
66  
-        var objectStore = transaction.objectStore(STORE_NAME);
  268
+    function doGetAll(outRequest) {
  269
+      var transaction = database.transaction([STORE_NAME], 'readonly');
  270
+      var objectStore = transaction.objectStore(STORE_NAME);
67 271
 
68  
-        var req = objectStore.put(obj);
69  
-        req.onsuccess = function(e) {
70  
-          retRequest.done(e.target.result);
71  
-        }
  272
+      var req = objectStore.mozGetAll();
72 273
 
73  
-        req.onerror = function(e) {
74  
-          retRequest.failed(e.target.error);
75  
-        }
76  
-      },0);
  274
+      req.onsuccess = function(e) {
  275
+        var data = e.target.result;
  276
+        var out = {};
  277
+        data.forEach(function(contact) {
  278
+          out[contact.uid] = contact;
  279
+        });
  280
+        outRequest.done(out);
  281
+      };
77 282
 
78  
-        return retRequest;
  283
+      req.onerror = function(e) {
  284
+        outRequest.failed(e.target.error);
79 285
       }
  286
+    }
80 287
 
81  
-      contacts.getAll = function() {
82  
-        var retRequest = new fb.utils.Request();
83  
-        window.setTimeout(function() {
84  
-          var transaction = database.transaction([STORE_NAME], 'readonly');
85  
-          var objectStore = transaction.objectStore(STORE_NAME);
  288
+    contacts.getAll = function() {
  289
+      var retRequest = new fb.utils.Request();
86 290
 
87  
-          var req = objectStore.mozGetAll();
  291
+      window.setTimeout(function() {
  292
+        contacts.init(function get_all() {
  293
+          doGetAll(retRequest);
  294
+        },
  295
+        function() {
  296
+          initError(retRequest);
  297
+        });
  298
+      },0);
88 299
 
89  
-          req.onsuccess = function(e) {
90  
-            var data = e.target.result;
91  
-            var out = {};
92  
-            data.forEach(function(contact) {
93  
-              out[contact.uid] = contact;
94  
-            });
95  
-            retRequest.done(out);
96  
-          };
  300
+      return retRequest;
  301
+    }
97 302
 
98  
-          req.onerror = function(e) {
99  
-            retRequest.failed(e.target.error);
100  
-          }
101  
-        }, 0);
  303
+    function doRemove(uid,outRequest) {
  304
+      var transaction = database.transaction([STORE_NAME], 'readwrite');
  305
+      transaction.oncomplete = function(e) {
  306
+        outRequest.done(e.target.result);
  307
+      }
102 308
 
103  
-        return retRequest;
  309
+      transaction.onerror = function(e) {
  310
+        outRequest.failed(e.target.error);
104 311
       }
  312
+      var objectStore = transaction.objectStore(STORE_NAME);
  313
+
  314
+      objectStore.delete(uid);
  315
+    }
105 316
 
106 317
     /**
107 318
      *  Allows to remove FB contact from the DB
@@ -111,41 +322,54 @@ if (!window.fb.contacts) {
111 322
     contacts.remove = function(uid) {
112 323
       var retRequest = new fb.utils.Request();
113 324
 
114  
-      window.setTimeout(function() {
115  
-        var transaction = database.transaction([STORE_NAME], 'readwrite');
116  
-        transaction.oncomplete = function(e) {
117  
-          retRequest.done(e.target.result);
118  
-        }
119  
-
120  
-        transaction.onerror = function(e) {
121  
-          retRequest.failed(e.target.error);
122  
-        }
123  
-        var objectStore = transaction.objectStore(STORE_NAME);
124  
-        objectStore.delete(uid);
  325
+      window.setTimeout(function remove() {
  326
+        contacts.init(function() {
  327
+          doRemove(uid, retRequest);
  328
+        },
  329
+        function() {
  330
+           initError(retRequest);
  331
+        });
125 332
       },0);
126 333
 
127 334
       return retRequest;
128 335
     }
129 336
 
130  
-    contacts.init = function(cb) {
131  
-      var req = indexedDB.open('Gaia_Facebook_Friends', 1.0);
  337
+    function notifyOpenSuccess(cb) {
  338
+      isInitialized = true;
  339
+      if (typeof cb === 'function') {
  340
+        cb();
  341
+      }
  342
+    }
  343
+
  344
+    contacts.init = function(cb, errorCb) {
  345
+      if (isInitialized === true) {
  346
+        cb();
  347
+        return;
  348
+      }
  349
+
  350
+      var req = indexedDB.open(DB_NAME, DB_VERSION);
132 351
 
133 352
       req.onsuccess = function(e) {
134 353
         database = e.target.result;
135  
-        if (typeof cb === 'function') {
136  
-          cb();
  354
+        if (migrationNeeded === true) {
  355
+          migrateData(function migrated() {
  356
+            notifyOpenSuccess(cb);
  357
+          });
  358
+        }
  359
+        else {
  360
+          notifyOpenSuccess(cb);
137 361
         }
138 362
       };
139 363
 
140 364
       req.onerror = function(e) {
141 365
         window.console.error('FB: Error while opening the DB: ',
142 366
                                                         e.target.error.name);
143  
-        if (typeof cb === 'function') {
144  
-          cb();
  367
+        if (typeof errorCb === 'function') {
  368
+          errorCb();
145 369
         }
146 370
       };
147 371
 
148  
-      req.onupgradeneeded = createStore;
  372
+      req.onupgradeneeded = upgradeDB;
149 373
     }
150 374
 
151 375
   }) (document);
9  apps/communications/contacts/js/fb/fb_init.js
@@ -18,10 +18,7 @@ if (typeof fb.init === 'undefined') {
18 18
         fb.syncPeriod = configData.facebookSyncPeriod || 24;
19 19
         fb.testToken = configData.testToken;
20 20
 
21  
-        // The FB Contacts DB Cache is initialized regardless FB is enabled
22  
-        // or not. That's because we would like to avoid to add extra conditions
23  
-        // throughout the code, thus keeping it as simple as possible
24  
-        initalizeDB(callback);
  21
+        callback();
25 22
       }
26 23
 
27 24
       req.onerror = function(code) {
@@ -35,9 +32,5 @@ if (typeof fb.init === 'undefined') {
35 32
       }
36 33
     }
37 34
 
38  
-    function initalizeDB(cb) {
39  
-      fb.contacts.init(cb);
40  
-    }
41  
-
42 35
   })();
43 36
 }
43  apps/communications/contacts/js/fb/fb_utils.js
@@ -17,18 +17,6 @@ if (!fb.utils) {
17 17
       fb.oauthflow.params['redirectLogout'] : '';
18 18
     var STORAGE_KEY = Utils.TOKEN_DATA_KEY = 'tokenData';
19 19
 
20  
-    function getMozContactByUid(uid, success, error) {
21  
-      var filter = {
22  
-        filterBy: ['category'],
23  
-        filterValue: uid,
24  
-        filterOp: 'contains'
25  
-      };
26  
-
27  
-      var req = navigator.mozContacts.find(filter);
28  
-      req.onsuccess = success;
29  
-      req.onerror = error;
30  
-    }
31  
-
32 20
       // For controlling data synchronization
33 21
     Utils.setLastUpdate = function(value, cb) {
34 22
       window.asyncStorage.setItem(LAST_UPDATED_KEY, {
@@ -79,7 +67,7 @@ if (!fb.utils) {
79 67
       var outReq = new Utils.Request();
80 68
 
81 69
       window.setTimeout(function get_mozContact_ByUid() {
82  
-        getMozContactByUid(uid,
  70
+        Utils.getMozContactByUid(uid,
83 71
           function onsuccess(e) {
84 72
             if (e.target.result && e.target.result.length > 0) {
85 73
               outReq.done(e.target.result[0]);
@@ -101,7 +89,7 @@ if (!fb.utils) {
101 89
       var outReq = new Utils.Request();
102 90
 
103 91
       window.setTimeout(function get_mozContact_ByUid() {
104  
-        getMozContactByUid(uid,
  92
+        Utils.getMozContactByUid(uid,
105 93
           function onsuccess(e) {
106 94
             if (e.target.result && e.target.result.length > 0) {
107 95
               outReq.done(e.target.result.length);
@@ -358,33 +346,6 @@ if (!fb.utils) {
358 346
 
359 347
     } // logout
360 348
 
361  
-    /**
362  
-     *   Request auxiliary object to support asynchronous calls
363  
-     *
364  
-     */
365  
-    Utils.Request = function() {
366  
-      this.done = function(result) {
367  
-        this.result = result;
368  
-        if (typeof this.onsuccess === 'function') {
369  
-          var ev = {};
370  
-          ev.target = this;
371  
-          window.setTimeout(function() {
372  
-            this.onsuccess(ev);
373  
-          }.bind(this), 0);
374  
-        }
375  
-      }
376  
-
377  
-      this.failed = function(error) {
378  
-        this.error = error;
379  
-        if (typeof this.onerror === 'function') {
380  
-          var ev = {};
381  
-          ev.target = this;
382  
-          window.setTimeout(function() {
383  
-            this.onerror(ev);
384  
-          }.bind(this), 0);
385  
-        }
386  
-      }
387  
-    }
388 349
 
389 350
     // FbContactsCleaner Object
390 351
     Utils.FbContactsCleaner = function(contacts) {
2  apps/communications/contacts/js/fb/friends_list.js
@@ -39,7 +39,7 @@ fbFriends.List = (function() {
39 39
         });
40 40
 
41 41
         // Enabling searching by email
42  
-        if(friend['email1']) {
  42
+        if (friend['email1']) {
43 43
           searchInfo.push(friend['email1']);
44 44
         }
45 45
 
3  apps/communications/dialer/index.html
@@ -36,6 +36,8 @@
36 36
     <script defer type="application/javascript" src="/dialer/js/recents_db.js"></script>
37 37
     <script defer type="application/javascript" src="/dialer/js/recents.js"></script>
38 38
     <script defer type="application/javascript" src="/dialer/js/ussd.js"></script>
  39
+    <script defer type="application/javascript" src="/contacts/js/fb/fb_data.js"></script>
  40
+    <script defer type="application/javascript" src="/contacts/js/fb/fb_contact_utils.js"></script>
39 41
     <!-- Shared code. We need to have 'recents_db.js' loaded before loading 'desktop'-->
40 42
     <script defer type="application/javascript" src="/shared/js/desktop.js"></script>
41 43
   <body role="application" class="hidden">
@@ -248,4 +250,3 @@ <h1 id="header-edit-mode-text" data-l10n-id="edit">Edit</h1>
248 250
 
249 251
   </body>
250 252
 </html>
251  
-
98  apps/communications/dialer/js/contacts.js
@@ -3,7 +3,71 @@
3 3
 
4 4
 'use strict';
5 5
 
  6
+var fbContacts = {};
  7
+
  8
+// Searcher object for FB Data
  9
+var _FbDataSearcher = function(variants) {
  10
+  var pointer = 0;
  11
+  var self = this;
  12
+  this.variants = variants;
  13
+
  14
+  window.console.log('Num Variants ', variants.length);
  15
+
  16
+  function checkVariant(variant, successCb, notFoundCb) {
  17
+    fb.getContactByNumber(variant, function success(result) {
  18
+      var contact = result;
  19
+
  20
+      if(contact) {
  21
+        fb.utils.getMozContactByUid(contact.uid, function merge(e) {
  22
+          var devContact = e.target.result[0];
  23
+          var finalContact = fb.mergeContact(devContact, contact);
  24
+          successCb(finalContact, {
  25
+            value: variant,
  26
+            // Facebook telephone are always of type personal
  27
+            type: 'personal',
  28
+            // We don't know the carrier from FB phones
  29
+            carrier: null
  30
+          });
  31
+        }, function error_get_mozContact() {
  32
+            console.error('Error getting mozContact');
  33
+            notFoundCb();
  34
+        });
  35
+      }
  36
+      else {
  37
+        notFoundCb();
  38
+      }
  39
+    },function error_getContactByNumber () {
  40
+        console.error('Error getting FB contacts');
  41
+        notFoundCb();
  42
+    });
  43
+  }
  44
+
  45
+  function successCb(fbContact, matchingTel) {
  46
+    self.onsuccess(fbContact, matchingTel);
  47
+  }
  48
+
  49
+  function notFoundCb() {
  50
+    pointer++;
  51
+    if(pointer < self.variants.length) {
  52
+      window.console.log('******* Checking variant *****', pointer);
  53
+      check(self.variants[pointer]);
  54
+    }
  55
+    else {
  56
+      self.onNotFound();
  57
+    }
  58
+  }
  59
+
  60
+  function check(variant) {
  61
+    checkVariant(variant, successCb, notFoundCb);
  62
+  }
  63
+
  64
+  this.start = function() {
  65
+    check(self.variants[0]);
  66
+  }
  67
+}
  68
+
6 69
 var Contacts = {
  70
+
7 71
   findByNumber: function findByNumber(number, callback) {
8 72
     var options;
9 73
     var variants;
@@ -29,7 +93,6 @@ var Contacts = {
29 93
       };
30 94
     }
31 95
 
32  
-
33 96
     var mozContacts = navigator.mozContacts;
34 97
     if (!mozContacts)
35 98
       callback(null);
@@ -37,7 +100,23 @@ var Contacts = {
37 100
     var request = mozContacts.find(options);
38 101
     request.onsuccess = function findCallback() {
39 102
       if (request.result.length === 0) {
40  
-        callback(null);
  103
+        // Checking if FB is enabled or not
  104
+        window.asyncStorage.getItem('tokenData', function(data) {
  105
+          if(!data || !data.access_token) {
  106
+            // Facebook is not even enabled
  107
+            callback(null);
  108
+            return;
  109
+          }
  110
+
  111
+          // Searching each variant on the fbContacts cache
  112
+          var searcher = new _FbDataSearcher(variants);
  113
+          searcher.start();
  114
+          searcher.onsuccess = callback;
  115
+          searcher.onNotFound = function not_found() {
  116
+            callback(null);
  117
+          }
  118
+        });
  119
+
41 120
         return;
42 121
       }
43 122
 
@@ -61,7 +140,20 @@ var Contacts = {
61 140
       }
62 141
 
63 142
       var matchingTel = contact.tel[matchResult.localIndex];
64  
-      callback(contact, matchingTel);
  143
+      if(fb.isFbLinked(contact)) {
  144
+        // Merge with the FB data
  145
+        var req = fb.contacts.get(fb.getFriendUid(contact));
  146
+        req.onsuccess = function() {
  147
+          callback(fb.mergeContact(contact,req.result), matchingTel);
  148
+        }
  149
+        req.onerror = function() {
  150
+          window.console.error('Error while getting FB Data');
  151
+          callback(contact, matchingTel);
  152
+        }
  153
+      }
  154
+      else {
  155
+        callback(contact, matchingTel);
  156
+      }
65 157
     };
66 158
     request.onerror = function findError() {
67 159
       callback(null);
4  apps/communications/dialer/oncall.html
@@ -10,6 +10,7 @@
10 10
     <link rel="resource" type="application/l10n" href="/dialer/locales/locales.ini">
11 11
     <script type="application/javascript" src="/shared/js/l10n.js"></script>
12 12
     <script defer type="application/javascript" src="/shared/js/settings_listener.js"></script>
  13
+    <script defer type="text/javascript" src="/shared/js/async_storage.js"></script>
13 14
 
14 15
     <script defer type="application/javascript" src="/shared/js/simple_phone_matcher.js"></script>
15 16
     <script defer type="application/javascript" src="/dialer/js/contacts.js"></script>
@@ -19,6 +20,9 @@
19 20
     <script defer type="application/javascript" src="/dialer/js/handled_call.js"></script>
20 21
     <script defer type="application/javascript" src="/dialer/js/recents_db.js"></script>
21 22
 
  23
+    <script defer type="application/javascript" src="/contacts/js/fb/fb_data.js"></script>
  24
+    <script defer type="application/javascript" src="/contacts/js/fb/fb_contact_utils.js"></script>
  25
+
22 26
     <!-- incoming call / swiper on locked screen-->
23 27
     <link rel="stylesheet" type="text/css" href="/dialer/style/swiper.css">
24 28
     <script defer src="/dialer/js/swiper.js"></script>
2  apps/communications/ftu/js/wifi.js
@@ -18,7 +18,7 @@ var WifiManager = {
18 18
         callback(self.networks);
19 19
       };
20 20
       req.onerror = function onScanError() {
21  
-        console.log('Error reading networks');
  21
+        console.log('Error reading networks: ' + req.error);
22 22
       };
23 23
     } else {
24 24
       var fakeNetworks = [
2  apps/email/js/ext/gaia-email-opt.js
@@ -34547,7 +34547,7 @@ ActiveSyncAccount.prototype = {
34547 34547
       callback();
34548 34548
   },
34549 34549
 
34550  
-  scheduleMessagePurge: function(callback) {
  34550
+  scheduleMessagePurge: function(folderId, callback) {
34551 34551
     // ActiveSync servers have no incremental folder growth, so message purging
34552 34552
     // makes no sense for them.
34553 34553
     if (callback)
13  apps/gallery/js/MetadataParser.js
@@ -153,7 +153,7 @@ var metadataParsers = (function() {
@@ -192,13 +192,8 @@ var metadataParsers = (function() {
@@ -210,7 +205,7 @@ var metadataParsers = (function() {
@@ -225,7 +220,7 @@ var metadataParsers = (function() {
151  apps/gallery/js/gallery.js
@@ -126,11 +126,6 @@ window.addEventListener('localized', function showBody() {
@@ -249,19 +244,55 @@ function initUI() {
@@ -281,23 +312,31 @@ function initDB() {
@@ -306,7 +345,7 @@ function initDB() {
@@ -316,25 +355,21 @@ function initDB() {
@@ -383,7 +418,8 @@ function fileDeleted(filename) {
@@ -632,6 +668,23 @@ function createThumbnailList() {