Skip to content
This repository
Browse code

overhaul architecture for toolbar. more full-fledged xpcom service ru…

…ns in

the background for the duration of the browser session, polling facebook every
5 minutes.  next up: notification service to establish communication between
back-end and front-end.
  • Loading branch information...
commit 098a0859aad1cb37f76bbe5eceeca2f504e439c7 1 parent 40ddc33
authored October 06, 2006
211  chrome/content/facebook.js
... ...
@@ -1,30 +1,26 @@
1  
-function FacebookRestClient() {
2  
-    this.settings = Components.classes['@facebook.com/simple-service;1']
3  
-                              .getService()
4  
-                              .QueryInterface(Components.interfaces.fbISimpleFacebookService);
  1
+// Note that this file is intended for login-related API function calls only
  2
+// (ie facebook.auth.*).  Other API calls go through the Facebook xpcom service.
  3
+var Cc = Components.classes;
  4
+var Ci = Components.interfaces;
  5
+function FacebookLoginClient() {
  6
+    this.fbSvc = Cc['@facebook.com/facebook-service;1'].getService().QueryInterface(Ci.fbIFacebookService);
5 7
 }
6 8
 
7  
-FacebookRestClient.prototype = {
8  
-    generateSig: function (params, useSessionSecret) {
  9
+FacebookLoginClient.prototype = {
  10
+    generateSig: function (params) {
9 11
         var str = '';
10 12
         params.sort();
11 13
         for (var i = 0; i < params.length; i++) {
12 14
             str += params[i];
13 15
         }
14  
-        if (useSessionSecret) {
15  
-            str += this.settings.sessionSecret;
16  
-        } else {
17  
-            str += this.settings.secret;
18  
-        }
  16
+        str += this.fbSvc.secret;
19 17
         return MD5(str);
20 18
     },
21 19
     callMethod: function (method, params, callback) {
22  
-        var sessionMethod = (method.indexOf('facebook.auth') == -1);
23 20
         params.push('method=' + method);
24  
-        params.push('session_key=' + this.settings.sessionKey);
25  
-        params.push('api_key=' + this.settings.apiKey);
  21
+        params.push('api_key=' + this.fbSvc.apiKey);
26 22
         params.push('call_id=' + (new Date()).getTime());
27  
-        params.push('sig=' + this.generateSig(params, sessionMethod));
  23
+        params.push('sig=' + this.generateSig(params));
28 24
 
29 25
         var req = new XMLHttpRequest();
30 26
         req.onreadystatechange = function (event) {
@@ -44,17 +40,188 @@ FacebookRestClient.prototype = {
44 40
             }
45 41
         };
46 42
         try {
47  
-            if (sessionMethod) {
48  
-                var protocol = 'http';
49  
-            } else {
50  
-                var protocol = 'https';
51  
-            }
52  
-            req.open('POST', protocol + '://api.facebook.com/restserver.php', true);
  43
+            req.open('POST', 'https://api.facebook.com/restserver.php', true);
53 44
             req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
54 45
             req.send(params.join('&'));
55 46
         } catch (e) {
56  
-            dump(e);
  47
+            dump('Exception sending REST request: ' + e + '\n');
57 48
         }
58 49
     },
  50
+};
  51
+
  52
+/* =====MD5 CODE FROM HERE DOWN=====
  53
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  54
+ * Digest Algorithm, as defined in RFC 1321.
  55
+ * Copyright (C) Paul Johnston 1999 - 2000.
  56
+ * Updated by Greg Holt 2000 - 2001.
  57
+ * See http://pajhome.org.uk/site/legal.html for details.
  58
+ */
  59
+
  60
+/*
  61
+ * Convert a 32-bit number to a hex string with ls-byte first
  62
+ */
  63
+var hex_chr = "0123456789abcdef";
  64
+function rhex(num)
  65
+{
  66
+  str = "";
  67
+  for(j = 0; j <= 3; j++)
  68
+    str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
  69
+           hex_chr.charAt((num >> (j * 8)) & 0x0F);
  70
+  return str;
  71
+}
  72
+
  73
+/*
  74
+ * Convert a string to a sequence of 16-word blocks, stored as an array.
  75
+ * Append padding bits and the length, as described in the MD5 standard.
  76
+ */
  77
+function str2blks_MD5(str)
  78
+{
  79
+  nblk = ((str.length + 8) >> 6) + 1;
  80
+  blks = new Array(nblk * 16);
  81
+  for(i = 0; i < nblk * 16; i++) blks[i] = 0;
  82
+  for(i = 0; i < str.length; i++)
  83
+    blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
  84
+  blks[i >> 2] |= 0x80 << ((i % 4) * 8);
  85
+  blks[nblk * 16 - 2] = str.length * 8;
  86
+  return blks;
  87
+}
  88
+
  89
+/*
  90
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally 
  91
+ * to work around bugs in some JS interpreters.
  92
+ */
  93
+function add(x, y)
  94
+{
  95
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  96
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  97
+  return (msw << 16) | (lsw & 0xFFFF);
  98
+}
  99
+
  100
+/*
  101
+ * Bitwise rotate a 32-bit number to the left
  102
+ */
  103
+function rol(num, cnt)
  104
+{
  105
+  return (num << cnt) | (num >>> (32 - cnt));
  106
+}
  107
+
  108
+/*
  109
+ * These functions implement the basic operation for each round of the
  110
+ * algorithm.
  111
+ */
  112
+function cmn(q, a, b, x, s, t)
  113
+{
  114
+  return add(rol(add(add(a, q), add(x, t)), s), b);
59 115
 }
  116
+function ff(a, b, c, d, x, s, t)
  117
+{
  118
+  return cmn((b & c) | ((~b) & d), a, b, x, s, t);
  119
+}
  120
+function gg(a, b, c, d, x, s, t)
  121
+{
  122
+  return cmn((b & d) | (c & (~d)), a, b, x, s, t);
  123
+}
  124
+function hh(a, b, c, d, x, s, t)
  125
+{
  126
+  return cmn(b ^ c ^ d, a, b, x, s, t);
  127
+}
  128
+function ii(a, b, c, d, x, s, t)
  129
+{
  130
+  return cmn(c ^ (b | (~d)), a, b, x, s, t);
  131
+}
  132
+
  133
+/*
  134
+ * Take a string and return the hex representation of its MD5.
  135
+ */
  136
+function MD5(str)
  137
+{
  138
+  x = str2blks_MD5(str);
  139
+  var a =  1732584193;
  140
+  var b = -271733879;
  141
+  var c = -1732584194;
  142
+  var d =  271733878;
  143
+ 
  144
+  for(i = 0; i < x.length; i += 16)
  145
+  {
  146
+    var olda = a;
  147
+    var oldb = b;
  148
+    var oldc = c;
  149
+    var oldd = d;
  150
+
  151
+    a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
  152
+    d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
  153
+    c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
  154
+    b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
  155
+    a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
  156
+    d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
  157
+    c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
  158
+    b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
  159
+    a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
  160
+    d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
  161
+    c = ff(c, d, a, b, x[i+10], 17, -42063);
  162
+    b = ff(b, c, d, a, x[i+11], 22, -1990404162);
  163
+    a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
  164
+    d = ff(d, a, b, c, x[i+13], 12, -40341101);
  165
+    c = ff(c, d, a, b, x[i+14], 17, -1502002290);
  166
+    b = ff(b, c, d, a, x[i+15], 22,  1236535329);    
  167
+
  168
+    a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
  169
+    d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
  170
+    c = gg(c, d, a, b, x[i+11], 14,  643717713);
  171
+    b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
  172
+    a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
  173
+    d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
  174
+    c = gg(c, d, a, b, x[i+15], 14, -660478335);
  175
+    b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
  176
+    a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
  177
+    d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
  178
+    c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
  179
+    b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
  180
+    a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
  181
+    d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
  182
+    c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
  183
+    b = gg(b, c, d, a, x[i+12], 20, -1926607734);
  184
+    
  185
+    a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
  186
+    d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
  187
+    c = hh(c, d, a, b, x[i+11], 16,  1839030562);
  188
+    b = hh(b, c, d, a, x[i+14], 23, -35309556);
  189
+    a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
  190
+    d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
  191
+    c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
  192
+    b = hh(b, c, d, a, x[i+10], 23, -1094730640);
  193
+    a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
  194
+    d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
  195
+    c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
  196
+    b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
  197
+    a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
  198
+    d = hh(d, a, b, c, x[i+12], 11, -421815835);
  199
+    c = hh(c, d, a, b, x[i+15], 16,  530742520);
  200
+    b = hh(b, c, d, a, x[i+ 2], 23, -995338651);
  201
+
  202
+    a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
  203
+    d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
  204
+    c = ii(c, d, a, b, x[i+14], 15, -1416354905);
  205
+    b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
  206
+    a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
  207
+    d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
  208
+    c = ii(c, d, a, b, x[i+10], 15, -1051523);
  209
+    b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
  210
+    a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
  211
+    d = ii(d, a, b, c, x[i+15], 10, -30611744);
  212
+    c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
  213
+    b = ii(b, c, d, a, x[i+13], 21,  1309151649);
  214
+    a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
  215
+    d = ii(d, a, b, c, x[i+11], 10, -1120210379);
  216
+    c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
  217
+    b = ii(b, c, d, a, x[i+ 9], 21, -343485551);
  218
+
  219
+    a = add(a, olda);
  220
+    b = add(b, oldb);
  221
+    c = add(c, oldc);
  222
+    d = add(d, oldd);
  223
+  }
  224
+  return rhex(a) + rhex(b) + rhex(c) + rhex(d);
  225
+}
  226
+
60 227
 dump('loaded facebook.js\n');
50  chrome/content/login.js
... ...
@@ -1,49 +1,45 @@
1  
-var client = new FacebookRestClient();
  1
+function debug(s) { dump('** login.js: ' + s + '\n'); }
  2
+var client = new FacebookLoginClient();
2 3
 function startup() {
3  
-    if (client.settings.sessionKey) {
4  
-        dump('already logged in!\n');
  4
+    if (client.fbSvc.loggedIn) {
  5
+        debug('already logged in!');
5 6
         window.close();
6  
-    } else if (!client.settings.authToken) {
7  
-        client.settings.apiKey = '64f19267b0e6177ea503046d801c00df';
8  
-        client.settings.secret = 'a8a5a57a9f9cd57473797c4612418908';
9  
-        dump('requesting token\n');
  7
+    } else if (!client.authToken) {
  8
+        debug('requesting token');
10 9
         try {
11 10
         client.callMethod('facebook.auth.createToken', [], function(req) {
12  
-            dump('received token response:\n');
  11
+            debug('received token response:');
13 12
             dump(req.responseText);
14  
-            client.settings.authToken = req.xmldata.token;
15  
-            dump('token is: '+client.settings.authToken+'\n');
  13
+            client.authToken = req.xmldata.token;
  14
+            debug('token is: '+client.authToken);
16 15
             startup();
17 16
         });
18 17
         } catch (e) {
19  
-            dump('exception: ' + e + '\n');
  18
+            debug('exception: ' + e);
20 19
         }
21 20
     } else {
22 21
         document.getElementById('facebook-login-body').
23 22
             setAttribute('src', 'http://api.facebook.com/login.php?api_key=' +
24  
-                                client.settings.apiKey + '&auth_token=' +
25  
-                                client.settings.authToken);
26  
-        dump('loading login page\n');
  23
+                                client.fbSvc.apiKey + '&auth_token=' + client.authToken);
  24
+        debug('loading login page');
27 25
     }
28 26
 }
29 27
 window.addEventListener('load', startup, false);
30 28
 
31 29
 function done() {
32  
-    dump('done called\n');
33  
-    client.callMethod('facebook.auth.getSession', ['auth_token='+client.settings.authToken], function(req) {
34  
-        dump('received session response:\n');
  30
+    debug('done()');
  31
+    client.callMethod('facebook.auth.getSession', ['auth_token='+client.authToken], function(req) {
  32
+        debug('received session response:');
35 33
         dump(req.responseText);
36  
-        client.settings.sessionKey = req.xmldata.session_key;
37  
-        client.settings.uid        = req.xmldata.uid;
38  
-        client.settings.sessionSecret = req.xmldata.secret;
39  
-        client.settings.authToken  = null;
40  
-        dump('session: ' + client.settings.sessionKey + ', uid: ' + client.settings.uid + ', secret: ' + client.settings.sessionSecret + '\n');
  34
+        var sessionKey    = req.xmldata.session_key;
  35
+        var sessionSecret = req.xmldata.secret;
  36
+        var uid           = req.xmldata.uid;
  37
+        client.fbSvc.sessionStart(sessionKey, sessionSecret, uid);
  38
+        client.authToken  = null;
  39
+        debug('session: ' + sessionKey + ', uid: ' + uid + ', secret: ' + sessionSecret);
41 40
         window.setTimeout("window.close();",1); // for some reason calling window.close directly does not work
42  
-        parent.getElementById('facebook-panel').label = 'logged in!';
  41
+        parent.getElementById('facebook-panel').label = 'logged in!'; // XXX will be handled by observer
43 42
     });
44 43
 }
45 44
 
46  
-function ready() {
47  
-    dump('readystatechanged\n');
48  
-}
49  
-dump('loaded login.js\n');
  45
+debug('loaded login.js');
17  chrome/content/sample.js
... ...
@@ -1,6 +1,9 @@
1  
-var client = new FacebookRestClient();
  1
+var Cc = Components.classes;
  2
+var Ci = Components.interfaces;
  3
+
  4
+var fbSvc = Cc['@facebook.com/facebook-service;1'].getService().QueryInterface(Ci.fbIFacebookService);
2 5
 function startup() {
3  
-    if (client.settings.sessionKey) {
  6
+    if (fbSvc.loggedIn) {
4 7
         document.getElementById('facebook-panel').label = 'logged in!';
5 8
     } else {
6 9
         document.getElementById('facebook-panel').label = 'logged out';
@@ -9,16 +12,16 @@ function startup() {
9 12
 window.addEventListener('load', startup, false);
10 13
 
11 14
 function FacebookLogin(statusElem) {
12  
-    if (client.settings.sessionKey) {
  15
+    if (fbSvc.loggedIn) {
13 16
         dump('logging out\n');
14  
-        client.settings.sessionKey = null;
15  
-        document.getElementById('facebook-panel').label = 'logged out';
  17
+        fbSvc.sessionEnd();
  18
+        document.getElementById('facebook-panel').label = 'logged out'; // XXX: move to signal facebook-session-end
16 19
     } else {
17 20
         window.open('chrome://facebook/content/login.xul', '',
18 21
                     'chrome,centerscreen,width=780,height=500,modal=yes,dialog=yes,close=no');
19 22
         // since the above is modal the following won't get run until the dialog is closed
20  
-        if (client.settings.sessionKey) {
21  
-            document.getElementById('facebook-panel').label = 'logged in!';
  23
+        if (fbSvc.loggedIn) {
  24
+            document.getElementById('facebook-panel').label = 'logged in!'; // XXX: move to signal facebook-session-start
22 25
         }
23 26
     }
24 27
 }
2  chrome/content/sample.xul
... ...
@@ -1,7 +1,5 @@
1 1
 <?xml version="1.0"?>
2 2
 <overlay id="facebook" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
3  
- <script src="chrome://facebook/content/md5.js"/>
4  
- <script src="chrome://facebook/content/facebook.js"/>
5 3
  <script src="chrome://facebook/content/sample.js"/>
6 4
  <statusbar id="status-bar">
7 5
   <statusbarpanel id="facebook-panel" onmousedown="if (event.button == 0) FacebookLogin(this)" />
37  components/facebook.idl
... ...
@@ -0,0 +1,37 @@
  1
+#include "nsISupports.idl"
  2
+
  3
+/*
  4
+[scriptable, uuid(c4051391-2b3a-427d-8cd0-8398d168858d)]
  5
+interface fbIFacebookCallListener : nsISupports
  6
+{
  7
+    void onFail(in string result);
  8
+    void onSuccess(in string result);
  9
+};
  10
+*/
  11
+
  12
+[scriptable, uuid(19baf81b-d1bd-4a99-9010-dd6f478bfd22)]
  13
+interface fbIFacebookService : nsISupports
  14
+{
  15
+    readonly attribute AString apiKey;
  16
+    readonly attribute AString secret;
  17
+
  18
+    // login.xul is responsible for performing the login sequence.  Once that
  19
+    // is finished, it should call sessionStart, which initiates the polling
  20
+    // process.  If the session ever ends, the polling will stop until
  21
+    // sessionStart is called again.  Aside from the facebook.auth methods,
  22
+    // all other Facebook API calls should be done inside this service.
  23
+    void sessionStart(in AString sessionKey, in AString sessionSecret, in AString uid);
  24
+    void sessionEnd();
  25
+
  26
+    // All other interaction comes by windows listing themselves as observers
  27
+    // and notifications that get triggered by the polling process.
  28
+    // Notifications that we fire:
  29
+    //   facebook-session-start
  30
+    //   facebook-session-end
  31
+    //   facebook-new-message
  32
+    //   facebook-new-poke
  33
+    // Windows can also query the most recent status of these things via the
  34
+    // readonly attributes below:
  35
+    readonly attribute boolean loggedIn;
  36
+    readonly attribute long    numMsgs;
  37
+};
404  components/facebook.js
... ...
@@ -0,0 +1,404 @@
  1
+function debug(s) { dump('** facebookService: ' + s + '\n'); }
  2
+// status:
  3
+// i can get this to load the constructor at startup
  4
+// but how do we want to deal with instantiating a session?
  5
+//  - i could delay loading this until login.xul tells us that it successfully
  6
+//    created a session and gives us a session key
  7
+//  - or i could load it but delay the polling until the session is created
  8
+// The service probably gets instantiated at startup but does not start polling.
  9
+// This way windows can still register themselves as observers of this so that
  10
+// as soon as we are logged in they get updated whenever possible.
  11
+
  12
+const FB_SVC_CONTRACTID  = '@facebook.com/facebook-service;1';
  13
+const FB_SVC_CID = '{e983db0e-05fc-46e7-9fba-a22041c894ac}';
  14
+const FB_SVC_DESC = 'fb api connector';
  15
+
  16
+var Cc = Components.classes;
  17
+var Ci = Components.interfaces;
  18
+
  19
+function facebookService()
  20
+{
  21
+    debug('constructor');
  22
+    this.apiKey = '64f19267b0e6177ea503046d801c00df';
  23
+    this.secret = 'a8a5a57a9f9cd57473797c4612418908';
  24
+
  25
+    this.sessionKey    = null;
  26
+    this.sessionSecret = null;
  27
+    this.uid           = null;
  28
+    this.numMsgs       = 0;
  29
+    this.loggedIn      = false;
  30
+
  31
+    var fbSvc = this; // so that poll can access us
  32
+    this.poll = {
  33
+        notify: function(timer) {
  34
+            debug('poll.notify');
  35
+            fbSvc.checkMessages();
  36
+            // XXX poll for other stuff
  37
+        }
  38
+    };
  39
+}
  40
+
  41
+facebookService.prototype = {
  42
+    // nsISupports implementation
  43
+    QueryInterface: function (iid) {
  44
+        if (!iid.equals(Ci.fbIFacebookService) && 
  45
+            !iid.equals(Ci.nsIClassInfo) &&
  46
+            !iid.equals(Ci.nsISupports))
  47
+            throw Components.results.NS_ERROR_NO_INTERFACE;
  48
+        return this;
  49
+    },
  50
+
  51
+    // nsIClassInfo implementation
  52
+    flags: Ci.nsIClassInfo.SINGLETON,
  53
+    classDescription: FB_SVC_DESC,
  54
+    getInterfaces: function(count) {
  55
+        debug('getInterfaces');
  56
+        var interfaceList = [Ci.fbIFacebookService, Ci.nsIClassInfo];
  57
+        count.value = interfaceList.length;
  58
+        return interfaceList;
  59
+    },
  60
+    getHelperForLanguage: function (count) {return null;},
  61
+
  62
+    getNumMsgs: function() {
  63
+        return this.numMsgs;
  64
+    },
  65
+    getApiKey: function() {
  66
+        return this.apiKey;
  67
+    },
  68
+    getSecret: function() {
  69
+        return this.secret;
  70
+    },
  71
+    getLoggedIn: function() {
  72
+        return this.loggedIn;
  73
+    },
  74
+    sessionStart: function(sessionKey, sessionSecret, uid) {
  75
+        debug('sessionStart');
  76
+        this.sessionKey    = sessionKey;
  77
+        this.sessionSecret = sessionSecret;
  78
+        this.loggedIn      = true;
  79
+        this.uid           = uid;
  80
+
  81
+        this.timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
  82
+        this.timer.initWithCallback(this.poll, 300 * 1000, Ci.nsITimer.TYPE_REPEATING_SLACK); // 5 mins
  83
+
  84
+        // fire off another thread to get things started
  85
+        this.timer2 = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
  86
+        this.timer2.initWithCallback(this.poll, 1, Ci.nsITimer.TYPE_ONE_SHOT);
  87
+    },
  88
+    sessionEnd: function() {
  89
+        debug('sessionEnd');
  90
+        this.sessionKey    = null;
  91
+        this.sessionSecret = null;
  92
+        this.uid           = null;
  93
+        this.loggedIn      = false;
  94
+        this.timer.cancel();
  95
+        this.timer2.cancel();
  96
+    },
  97
+
  98
+    checkMessages: function() {
  99
+        debug('checkMessages');
  100
+        var data = this.callMethod('facebook.messages.getCount', []);
  101
+        // XXX error-check
  102
+        var newMsgCount = data.unread;
  103
+        if (!this.numMsgs || newMsgCount > this.numMsgs) {
  104
+            this.numMsgs = newMsgCount;
  105
+            // XXX fire signal facebook-new-message
  106
+        }
  107
+        debug('you have ' + this.numMsgs + ' unread messages');
  108
+    },
  109
+
  110
+    generateSig: function (params) {
  111
+        var str = '';
  112
+        params.sort();
  113
+        for (var i = 0; i < params.length; i++) {
  114
+            str += params[i];
  115
+        }
  116
+        str += this.sessionSecret;
  117
+        return MD5(str);
  118
+    },
  119
+    // Note that this is intended to call non-login related Facebook API
  120
+    // functions - ie things other than facebook.auth.*.  The login-related
  121
+    // calls are done in the chrome layer.
  122
+    // Also note that this is synchronous so you should not call it from the UI.
  123
+    callMethod: function (method, params) {
  124
+        if (!this.loggedIn) return null;
  125
+
  126
+        params.push('method=' + method);
  127
+        params.push('session_key=' + this.sessionKey);
  128
+        params.push('api_key=' + this.apiKey);
  129
+        params.push('call_id=' + (new Date()).getTime());
  130
+        params.push('sig=' + this.generateSig(params));
  131
+        var message = params.join('&');
  132
+
  133
+        try {
  134
+            // Yuck...xmlhttprequest doesn't always work so we have to do this
  135
+            // the hard way.  Thanks to Manish from Flock for the tip!
  136
+            var channel = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService)
  137
+                               .newChannel('http://api.facebook.com/restserver.php', null, null)
  138
+                               .QueryInterface(Ci.nsIHttpChannel);
  139
+            var upStream = Cc['@mozilla.org/io/string-input-stream;1'].createInstance(Ci.nsIStringInputStream);
  140
+            upStream.setData(message, message.length);
  141
+            channel.QueryInterface(Ci.nsIUploadChannel)
  142
+                   .setUploadStream(upStream, "application/x-www-form-urlencoded", -1);
  143
+            channel.requestMethod = "POST";
  144
+            var downStream = channel.open();
  145
+            var sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
  146
+            sis.init(downStream);
  147
+            var resultText = sis.read(-1);
  148
+            resultText = resultText.substr(resultText.indexOf("\n") + 1);
  149
+            debug('received text:');
  150
+            dump(resultText);
  151
+            var xmldata = new XML(resultText);
  152
+            return xmldata;
  153
+        } catch (e) {
  154
+            debug('Exception sending REST request: ' + e);
  155
+            return null;
  156
+        }
  157
+    },
  158
+};
  159
+
  160
+// JavaScript XPCOM stuff
  161
+function facebookModule (aCID, aContractId, aComponentName, aConstructor) {
  162
+    debug('module constructor');
  163
+    this.mCID = Components.ID (aCID);
  164
+    this.mContractId = aContractId;
  165
+    this.mComponentName = aComponentName;
  166
+    this.mConstructor = aConstructor;
  167
+
  168
+    // factory object
  169
+    this.mFactory = {
  170
+        constructor: this.mConstructor,
  171
+        createInstance: function (aOuter, aIID) {
  172
+            debug('createInstance');
  173
+            if (aOuter != null) {
  174
+                throw Components.results.NS_ERROR_NO_AGGREGATION;
  175
+            }
  176
+
  177
+            return (new (this.constructor) ()).QueryInterface (aIID);
  178
+        }
  179
+    };
  180
+}
  181
+facebookModule.prototype = {
  182
+    // the module should register itself
  183
+    registerSelf: function (aCompMgr, aLocation, aLoaderStr, aType) {
  184
+        debug('registerSelf');
  185
+        aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  186
+        aCompMgr.registerFactoryLocation(this.mCID, this.mComponentName, this.mContractId,
  187
+                                         aLocation, aLoaderStr, aType);
  188
+
  189
+        // make the service get constructed at app-startup
  190
+        var catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  191
+        catMan.addCategoryEntry("app-startup", this.mComponentName, "service," + this.mContractId, true, true, null);
  192
+    },
  193
+
  194
+    unregisterSelf: function(aCompMgr, aLocation, aType) {
  195
+        debug('unregisterSelf');
  196
+        var catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  197
+        catMan.deleteCategoryEntry("app-startup", "service," + this.mContractId, true);
  198
+
  199
+        aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  200
+        aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  201
+    },
  202
+
  203
+    // get the factory                  
  204
+    getClassObject: function (aCompMgr, aCID, aIID) {
  205
+        debug('getClassObject');
  206
+        if (!aCID.equals (this.mCID)) {
  207
+            throw Components.results.NS_ERROR_NO_INTERFACE;
  208
+        }
  209
+
  210
+        if (!aIID.equals (Ci.nsIFactory)) {
  211
+            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  212
+        }
  213
+
  214
+        return this.mFactory;
  215
+    },
  216
+
  217
+    canUnload: function(compMgr) {
  218
+        debug('canUnload');
  219
+        return true;
  220
+    }
  221
+};
  222
+// entrypoint
  223
+function NSGetModule(compMgr, fileSpec) {
  224
+    debug('NSGetModule');
  225
+    return new facebookModule(FB_SVC_CID, FB_SVC_CONTRACTID, FB_SVC_DESC, facebookService);
  226
+}
  227
+
  228
+/* =====MD5 CODE FROM HERE DOWN=====
  229
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  230
+ * Digest Algorithm, as defined in RFC 1321.
  231
+ * Copyright (C) Paul Johnston 1999 - 2000.
  232
+ * Updated by Greg Holt 2000 - 2001.
  233
+ * See http://pajhome.org.uk/site/legal.html for details.
  234
+ */
  235
+
  236
+/*
  237
+ * Convert a 32-bit number to a hex string with ls-byte first
  238
+ */
  239
+var hex_chr = "0123456789abcdef";
  240
+function rhex(num)
  241
+{
  242
+  str = "";
  243
+  for(j = 0; j <= 3; j++)
  244
+    str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
  245
+           hex_chr.charAt((num >> (j * 8)) & 0x0F);
  246
+  return str;
  247
+}
  248
+
  249
+/*
  250
+ * Convert a string to a sequence of 16-word blocks, stored as an array.
  251
+ * Append padding bits and the length, as described in the MD5 standard.
  252
+ */
  253
+function str2blks_MD5(str)
  254
+{
  255
+  nblk = ((str.length + 8) >> 6) + 1;
  256
+  blks = new Array(nblk * 16);
  257
+  for(i = 0; i < nblk * 16; i++) blks[i] = 0;
  258
+  for(i = 0; i < str.length; i++)
  259
+    blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
  260
+  blks[i >> 2] |= 0x80 << ((i % 4) * 8);
  261
+  blks[nblk * 16 - 2] = str.length * 8;
  262
+  return blks;
  263
+}
  264
+
  265
+/*
  266
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally 
  267
+ * to work around bugs in some JS interpreters.
  268
+ */
  269
+function add(x, y)
  270
+{
  271
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  272
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  273
+  return (msw << 16) | (lsw & 0xFFFF);
  274
+}
  275
+
  276
+/*
  277
+ * Bitwise rotate a 32-bit number to the left
  278
+ */
  279
+function rol(num, cnt)
  280
+{
  281
+  return (num << cnt) | (num >>> (32 - cnt));
  282
+}
  283
+
  284
+/*
  285
+ * These functions implement the basic operation for each round of the
  286
+ * algorithm.
  287
+ */
  288
+function cmn(q, a, b, x, s, t)
  289
+{
  290
+  return add(rol(add(add(a, q), add(x, t)), s), b);
  291
+}
  292
+function ff(a, b, c, d, x, s, t)
  293
+{
  294
+  return cmn((b & c) | ((~b) & d), a, b, x, s, t);
  295
+}
  296
+function gg(a, b, c, d, x, s, t)
  297
+{
  298
+  return cmn((b & d) | (c & (~d)), a, b, x, s, t);
  299
+}
  300
+function hh(a, b, c, d, x, s, t)
  301
+{
  302
+  return cmn(b ^ c ^ d, a, b, x, s, t);
  303
+}
  304
+function ii(a, b, c, d, x, s, t)
  305
+{
  306
+  return cmn(c ^ (b | (~d)), a, b, x, s, t);
  307
+}
  308
+
  309
+/*
  310
+ * Take a string and return the hex representation of its MD5.
  311
+ */
  312
+function MD5(str)
  313
+{
  314
+  x = str2blks_MD5(str);
  315
+  var a =  1732584193;
  316
+  var b = -271733879;
  317
+  var c = -1732584194;
  318
+  var d =  271733878;
  319
+ 
  320
+  for(i = 0; i < x.length; i += 16)
  321
+  {
  322
+    var olda = a;
  323
+    var oldb = b;
  324
+    var oldc = c;
  325
+    var oldd = d;
  326
+
  327
+    a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
  328
+    d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
  329
+    c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
  330
+    b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
  331
+    a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
  332
+    d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
  333
+    c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
  334
+    b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
  335
+    a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
  336
+    d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
  337
+    c = ff(c, d, a, b, x[i+10], 17, -42063);
  338
+    b = ff(b, c, d, a, x[i+11], 22, -1990404162);
  339
+    a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
  340
+    d = ff(d, a, b, c, x[i+13], 12, -40341101);
  341
+    c = ff(c, d, a, b, x[i+14], 17, -1502002290);
  342
+    b = ff(b, c, d, a, x[i+15], 22,  1236535329);    
  343
+
  344
+    a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
  345
+    d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
  346
+    c = gg(c, d, a, b, x[i+11], 14,  643717713);
  347
+    b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
  348
+    a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
  349
+    d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
  350
+    c = gg(c, d, a, b, x[i+15], 14, -660478335);
  351
+    b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
  352
+    a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
  353
+    d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
  354
+    c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
  355
+    b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
  356
+    a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
  357
+    d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
  358
+    c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
  359
+    b = gg(b, c, d, a, x[i+12], 20, -1926607734);
  360
+    
  361
+    a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
  362
+    d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
  363
+    c = hh(c, d, a, b, x[i+11], 16,  1839030562);
  364
+    b = hh(b, c, d, a, x[i+14], 23, -35309556);
  365
+    a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
  366
+    d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
  367
+    c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
  368
+    b = hh(b, c, d, a, x[i+10], 23, -1094730640);
  369
+    a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
  370
+    d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
  371
+    c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
  372
+    b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
  373
+    a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
  374
+    d = hh(d, a, b, c, x[i+12], 11, -421815835);
  375
+    c = hh(c, d, a, b, x[i+15], 16,  530742520);
  376
+    b = hh(b, c, d, a, x[i+ 2], 23, -995338651);
  377
+
  378
+    a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
  379
+    d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
  380
+    c = ii(c, d, a, b, x[i+14], 15, -1416354905);
  381
+    b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
  382
+    a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
  383
+    d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
  384
+    c = ii(c, d, a, b, x[i+10], 15, -1051523);
  385
+    b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
  386
+    a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
  387
+    d = ii(d, a, b, c, x[i+15], 10, -30611744);
  388
+    c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
  389
+    b = ii(b, c, d, a, x[i+13], 21,  1309151649);
  390
+    a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
  391
+    d = ii(d, a, b, c, x[i+11], 10, -1120210379);
  392
+    c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
  393
+    b = ii(b, c, d, a, x[i+ 9], 21, -343485551);
  394
+
  395
+    a = add(a, olda);
  396
+    b = add(b, oldb);
  397
+    c = add(c, oldc);
  398
+    d = add(d, oldd);
  399
+  }
  400
+  return rhex(a) + rhex(b) + rhex(c) + rhex(d);
  401
+}
  402
+
  403
+debug('loaded facebook.js');
  404
+
BIN  components/facebook.xpt
Binary file not shown
14  components/simple-facebook.idl
... ...
@@ -1,14 +0,0 @@
1  
-#include "nsISupports.idl"
2  
-
3  
-[scriptable, uuid(19baf81b-d1bd-4a99-9010-dd6f478bfd22)]
4  
-interface fbISimpleFacebookService : nsISupports
5  
-{
6  
-    attribute AString apiKey;
7  
-    attribute AString secret;
8  
-
9  
-    attribute AString authToken;
10  
-
11  
-    attribute AString sessionKey;
12  
-    attribute AString sessionSecret;
13  
-    attribute AString uid;
14  
-};
132  components/simple-facebook.js
... ...
@@ -1,132 +0,0 @@
1  
-const FB_SVC_CONTRACTID  = '@facebook.com/simple-service;1';
2  
-const FB_SVC_CID = '{5b88bfed-0e9e-475d-a3e9-f2c4a5814e8d}';
3  
-const FB_SVC_DESC = 'store some basic settings about the fb api connector';
4  
-
5  
-const fbISimpleFacebookService      = Components.interfaces.fbISimpleFacebookService;
6  
-const nsISupports                   = Components.interfaces.nsISupports;
7  
-const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
8  
-const nsITimer                      = Components.interfaces.nsITimer;
9  
-
10  
-function facebookSimpleService()
11  
-{
12  
-    this.apiKey = null;
13  
-    this.secret = null;
14  
-    this.authToken = null;
15  
-    this.sessionKey = null;
16  
-    this.sessionSecret = null;
17  
-    this.uid = null;
18  
-}
19  
-
20  
-facebookSimpleService.prototype = {
21  
-    // nsISupports implementation
22  
-    QueryInterface: function (iid) {
23  
-        if (!iid.equals(fbISimpleFacebookService) && 
24  
-            !iid.equals(nsITimer) &&
25  
-            !iid.equals(nsIClassInfo) &&
26  
-            !iid.equals(nsISupports))
27  
-            throw Components.results.NS_ERROR_NO_INTERFACE;
28  
-        return this;
29  
-    },
30  
-
31  
-    // nsIClassInfo implementation
32  
-    flags: nsIClassInfo.SINGLETON,
33  
-    classDescription: FB_SVC_DESC,
34  
-    getInterfaces: function(count) {
35  
-        var interfaceList = [fbISimpleFacebookService, nsIClassInfo];
36  
-        count.value = interfaceList.length;
37  
-        return interfaceList;
38  
-    },
39  
-    getHelperForLanguage: function (count) {return null;},
40  
-
41  
-    // fbISimpleFacebookService implementation
42  
-    getApiKey: function() {
43  
-        dump('getApiKey' + this.apiKey + '\n');
44  
-        return this.apiKey;
45  
-    },
46  
-    setApiKey: function(apiKey) {
47  
-        dump('setApiKey' + apiKey + '\n');
48  
-        this.apiKey = apiKey;
49  
-    },
50  
-    getSecret: function() {
51  
-        return this.secret;
52  
-    },
53  
-    setSecret: function(secret) {
54  
-        this.secret = secret;
55  
-    },
56  
-    getAuthToken: function() {
57  
-        return this.authToken;
58  
-    },
59  
-    setAuthToken: function(authToken) {
60  
-        this.authToken = authToken;
61  
-    },
62  
-    getSessionKey: function() {
63  
-        return this.sessionKey;
64  
-    },
65  
-    setSessionKey: function(sessionKey) {
66  
-        this.sessionKey = sessionKey;
67  
-    },
68  
-    getSessionSecret: function() {
69  
-        return this.sessionSecret;
70  
-    },
71  
-    setSessionSecret: function(sessionSecret) {
72  
-        this.sessionSecret = sessionSecret;
73  
-    },
74  
-    getUid: function() {
75  
-        return this.uid;
76  
-    },
77  
-    setUid: function(uid) {
78  
-        this.uid = uid;
79  
-    },
80  
-};
81  
-
82  
-// JavaScript XPCOM stuff
83  
-function facebookModule (aCID, aContractId, aComponentName, aConstructor) {
84  
-    this.mCID = Components.ID (aCID);
85  
-    this.mContractId = aContractId;
86  
-    this.mComponentName = aComponentName;
87  
-    this.mConstructor = aConstructor;
88  
-
89  
-    // factory object
90  
-    this.mFactory = {
91  
-        constructor: this.mConstructor,
92  
-        createInstance: function (aOuter, aIID) {
93  
-            if (aOuter != null) {
94  
-                throw Components.results.NS_ERROR_NO_AGGREGATION;
95  
-            }
96  
-
97  
-            return (new (this.constructor) ()).QueryInterface (aIID);
98  
-        }
99  
-    };
100  
-}
101  
-facebookModule.prototype = {
102  
-    // the module should register itself
103  
-    registerSelf: function (aCompMgr, aLocation, aLoaderStr, aType) {
104  
-        aCompMgr = aCompMgr.QueryInterface (
105  
-                Components.interfaces.nsIComponentRegistrar);
106  
-        aCompMgr.registerFactoryLocation (this.mCID, this.mComponentName, 
107  
-                this.mContractId, aLocation, aLoaderStr, aType);
108  
-    },
109  
-
110  
-    // get the factory                  
111  
-    getClassObject: function (aCompMgr, aCID, aIID) {
112  
-        if (!aCID.equals (this.mCID)) {
113  
-            throw Components.results.NS_ERROR_NO_INTERFACE;
114  
-        }
115  
-
116  
-        if (!aIID.equals (Components.interfaces.nsIFactory)) {
117  
-            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
118  
-        }
119  
-
120  
-        return this.mFactory;
121  
-    },
122  
-
123  
-    canUnload: function(compMgr) {
124  
-        return true;
125  
-    }
126  
-};
127  
-// entrypoint
128  
-function NSGetModule(compMgr, fileSpec) {
129  
-    dump('constructing facebook module\n');
130  
-    return new facebookModule(FB_SVC_CID, FB_SVC_CONTRACTID, FB_SVC_DESC, facebookSimpleService);
131  
-}
132  
-dump('loaded simple-facebook.js\n');
BIN  components/simple-facebook.xpt
Binary file not shown
1  notes
@@ -18,3 +18,4 @@ ari@Ari:platform/client/firefox$ cat `which FirefoxDev.sh`
18 18
     rm "/Users/ari/Library/Application Support/Firefox/Profiles/949k0h9m.dev/compreg.dat" "/Users/ari/Library/Application Support/Firefox/Profiles/949k0h9m.dev/xpti.dat"
19 19
     /Applications/Firefox2.app/Contents/MacOS/firefox -P dev -console
20 20
 
  21
+to persist data across multiple windows, try http://www.xulplanet.com/references/xpcomref/comps/c_hashpropertybag1.html

0 notes on commit 098a085

Please sign in to comment.
Something went wrong with that request. Please try again.