Initial commit
authorDave Raggett <dsr@w3.org>
Thu, 28 Apr 2011 17:55:08 +0100
changeset 0 8ed5ca0acbac
child 1 fe95b601b6c5
Initial commit
dashboard/build.sh
dashboard/chrome.manifest
dashboard/chrome/content/assess.js
dashboard/chrome/content/dashboard.js
dashboard/chrome/content/dashboard.xul
dashboard/chrome/content/database.js
dashboard/chrome/content/misc.js
dashboard/chrome/content/observer.js
dashboard/chrome/content/overlay.js
dashboard/chrome/content/overlay.xul
dashboard/chrome/content/p3p.js
dashboard/chrome/content/share.js
dashboard/chrome/locale/en-US/dashboard.dtd
dashboard/chrome/locale/en-US/dashboard.dtd~
dashboard/chrome/locale/en-US/dashboard.properties
dashboard/chrome/skin/common/cat-globe.png
dashboard/chrome/skin/common/dashboard.css
dashboard/chrome/skin/common/dashboard.ico
dashboard/chrome/skin/common/dashboard.png
dashboard/chrome/skin/common/disappointed.png
dashboard/chrome/skin/common/glasses-cool.png
dashboard/chrome/skin/common/head.png
dashboard/chrome/skin/common/help.png
dashboard/chrome/skin/common/icon.png
dashboard/chrome/skin/common/left.gif
dashboard/chrome/skin/common/logo.png
dashboard/chrome/skin/common/mad-tongue.png
dashboard/chrome/skin/common/overlay.css
dashboard/chrome/skin/common/right.gif
dashboard/chrome/skin/common/texture.jpg
dashboard/chrome/skin/common/toolbar-button.css
dashboard/chrome/skin/mac/dashboard.css
dashboard/chrome/skin/mac/disappointed.png
dashboard/chrome/skin/mac/glasses-cool.png
dashboard/chrome/skin/mac/icon.png
dashboard/chrome/skin/mac/mad-tongue.png
dashboard/chrome/skin/mac/overlay.css
dashboard/chrome/skin/mac/toolbar-button.css
dashboard/chrome/skin/win/dashboard.css
dashboard/chrome/skin/win/disappointed.png
dashboard/chrome/skin/win/glasses-cool.png
dashboard/chrome/skin/win/icon.png
dashboard/chrome/skin/win/mad-tongue.png
dashboard/chrome/skin/win/overlay.css
dashboard/chrome/skin/win/toolbar-button.css
dashboard/dashboard.xpi
dashboard/defaults/preferences/dashboard.js
dashboard/install.rdf
dashboard/readme.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/build.sh	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,14 @@
+#!/bin/bash
+# build.sh -- XPI files for mozilla extensions
+# by Dave Raggett <[email protected]>
+
+TARGET=dashboard
+
+# for minimal xpi file
+# zip -r ../$TARGET.xpi chrome install.rdf chrome.manifest
+
+# to include the readme and build script etc.
+touch $TARGET.xpi
+rm $TARGET.xpi
+zip -r ../$TARGET.xpi *
+mv ../$TARGET.xpi .
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome.manifest	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,20 @@
+# scripts and xul markup
+content    dashboard                      chrome/content/
+
+# language specific text resources for localization
+locale     dashboard      en-US           chrome/locale/en-US/
+
+# register a global skin and OS dependent skins
+skin       dashboard-common    classic/1.0     chrome/skin/common/
+skin       dashboard           classic/1.0     chrome/skin/win/      os=WINCE
+skin       dashboard           classic/1.0     chrome/skin/win/      os=WINNT
+skin       dashboard           classic/1.0     chrome/skin/mac/      os=Darwin
+skin       dashboard           classic/1.0     chrome/skin/win/      os=Linux
+skin       dashboard           classic/1.0     chrome/skin/win/      os=SunOS
+skin       dashboard           classic/1.0     chrome/skin/win/      os=FreeBSD
+
+# Firefox only
+overlay  chrome://browser/content/browser.xul  chrome://dashboard/content/overlay.xul
+
+# style button in customize toolbar dialog
+style    chrome://global/content/customizeToolbar.xul  chrome://dashboard/skin/toolbar-button.css
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/assess.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,578 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Dashboard.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+var assess = {
+  start_up: function (dashboard)
+  {
+    // dashboard overlay passes current location to windowOpen()
+    this.dashboard = dashboard;
+    let status = window.arguments ? window.arguments[0] : null;
+
+    if (status)
+    {
+      this.update_current_site(status);
+      this.refresh_prefs(status.page_host);
+    }
+
+    let sharing = document.getElementById("sharing");
+    sharing.addEventListener("command", function ()
+    {
+      assess.sharing_change();
+    }, false);
+
+    let norton_button = document.getElementById("exec_norton_button");
+    norton_button.addEventListener("command", function ()
+    {
+      assess.check_norton();
+    }, false);
+
+    let freetrust_button = document.getElementById("exec_freetrust_button");
+    freetrust_button.addEventListener("command", function ()
+    {
+      assess.check_freetrust();
+    }, false);
+
+    let truste_button = document.getElementById("exec_truste_button");
+    freetrust_button.addEventListener("command", function ()
+    {
+      assess.check_truste();
+    }, false);
+
+    let simplify = document.getElementById("simplify_choices");
+    simplify.addEventListener("command", function ()
+    {
+      assess.simplify_choices(true);
+    }, false);
+
+    let complicate = document.getElementById("let_me_choose");
+    complicate.addEventListener("command", function ()
+    {
+      assess.expand_choices(true);
+    }, false);
+
+    let current = document.getElementById("dashboard_current");
+
+    current.addEventListener("click", function (e)
+    { 
+      if (e.target.nodeName != "html:a")
+         current.className = "normal";
+    }, false);
+
+    window.addEventListener("keydown", function (e)
+    {
+      if (e.which == 27) // Escape
+        current.className = "normal";
+    }, false);
+
+    this.attach_prefs_handler("simple_choice");  // radio group
+    this.attach_prefs_handler("prefs_never_block");
+    this.attach_prefs_handler("prefs_do_not_track");
+    this.attach_prefs_handler("prefs_ext_3rd_parties");
+    this.attach_prefs_handler("prefs_ext_3rd_cookies");
+    this.attach_prefs_handler("prefs_all_lasting_cookies");
+    this.attach_prefs_handler("prefs_clear_flash_cookies");
+    this.attach_prefs_handler("prefs_block_scripting");
+    this.attach_prefs_handler("prefs_block_geolocation");
+    this.attach_prefs_handler("prefs_disable_html5_pings");
+    this.attach_prefs_handler("prefs_disable_referrer");
+    this.attach_prefs_handler("prefs_disable_dom_storage");
+    this.attach_prefs_handler("prefs_disable_flash");
+    this.attach_prefs_handler("prefs_disable_java");
+  },
+
+  shut_down: function ()
+  {
+  },
+
+  sharing_change: function ()
+  {
+    let  sharing = document.getElementById("sharing");
+
+    if (window.dashboard_overlay)
+      window.dashboard_overlay.share.set_mode(sharing.value);
+  },
+
+  refresh: function ()
+  {
+    this.update_current_site(this.status);
+  },
+
+  attach_prefs_handler: function (id)
+  {
+    let el = document.getElementById(id);
+    if (el)
+      el.addEventListener("command",  this.record_prefs, true);      
+  },
+
+  simplify_choices: function (active)
+  {
+    this.simple_choices = true;
+    let detailed = document.getElementById("detailed_prefs");
+    let simple = document.getElementById("simple_prefs");
+    simple.style.display = "-moz-box";
+    simple.style.visibilty = "visible";
+    detailed.style.display = "none";
+    detailed.style.visibility = "hidden";
+
+    if (!active)
+      return;
+
+    let uri = this.uri;
+
+    if (uri && /^http.+/.test(uri.spec) )
+    {
+      let prefs = 0;
+      let simple_group = document.getElementById("simple_choice");
+
+      if ( this.get_choice("prefs_ext_3rd_cookies", 4) ||
+           this.get_choice("prefs_disable_html5_pings", 9) )
+        prefs = 1;
+
+      if ( this.get_choice("prefs_all_lasting_cookies", 5) ||
+           this.get_choice("prefs_clear_flash_cookies", 6) ||
+           this.get_choice("prefs_block_scripting", 7) ||
+           this.get_choice("prefs_block_geolocation", 8) ||
+           this.get_choice("prefs_disable_referrer", 10) ||
+           this.get_choice("prefs_disable_dom_storage", 11) ||
+           this.get_choice("prefs_disable_flash", 12) ||
+           this.get_choice("prefs_disable_java", 13) )
+        prefs = 2;
+      
+      simple_group.selectedIndex = prefs;
+
+      let host = uri.host;
+      window.database.set_prefs(host, prefs);
+      setTimeout(function () { assess.refresh_prefs(host); }, 100);
+    }
+  },
+
+  expand_choices:  function (active)
+  {
+    this.simple_choices = false;
+    let detailed = document.getElementById("detailed_prefs");
+    let simple = document.getElementById("simple_prefs");
+    simple.style.display = "none";
+    simple.style.visibilty = "hidden";
+    detailed.style.display = "-moz-box";
+    detailed.style.visibility = "visible";
+
+    if (!active)
+      return;
+
+    let uri = this.uri;
+
+    if (uri && /^http.+/.test(uri.spec))
+    {
+      let simple_group = document.getElementById("simple_choice");
+      let prefs = simple_group.selectedIndex;
+
+      if (prefs == 0) // carefree: don't block anything
+        prefs = 3;
+      else if (prefs == 1)
+        prefs = 3 | (1 << 4)|(1<<9);  // thoughtful
+      else  // paranoid, so block pretty much everything!
+        prefs = 3 | (1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<13);
+
+      let host = uri.host;
+      window.database.set_prefs(host, prefs);
+      setTimeout(function () { assess.refresh_prefs(host); }, 100);
+    }
+  },
+
+  check_norton: function ()
+  {
+    window.open("http://safeweb.norton.com/report/show?url="+this.host);
+  },
+
+  check_freetrust: function ()
+  {
+    window.open("http://freetrustseal.com/trust.php?site="+this.host);
+  },
+
+  check_truste: function ()
+  {
+    window.open("http://clicktoverify.truste.com/pvr.php?page=validate&url="+this.host);
+  },
+
+  record_prefs: function (evt)
+  {
+    assess.save_prefs(assess.host);
+  },
+
+  refresh_prefs: function (host)
+  {
+    let database = window.database;
+    let db = database.db;
+    let simple_group = document.getElementById("simple_choice");
+
+    // bit 0-1 encode simple preferences as follows
+    // 0 = carefree 1 = thoughtful 2 = paranoid 3 = advanced
+    let prefs = database.get_prefs(host, db);
+
+    //alert("prefs = " + prefs + " " + ((prefs & 3)<3 ? "complex" : "simple") );
+
+    // if no preferences are recorded for this site
+    if (typeof(prefs) == "undefined")
+    {
+      this.simplify_choices();
+      simple_group.selectedIndex = 0;
+    }
+    else // update UI to reflect recorded preferences
+    {
+      let simple = (prefs & 3);
+
+      if (simple < 3)
+      {
+        this.simplify_choices();
+        simple_group.selectedIndex = simple;
+      }
+      else // advanced choices
+      {
+        this.expand_choices(false);
+        this.mark_choice("prefs_never_block", prefs, 2);
+        this.mark_choice("prefs_ext_3rd_parties", prefs, 3);
+        this.mark_choice("prefs_ext_3rd_cookies", prefs, 4);
+        this.mark_choice("prefs_all_lasting_cookies", prefs, 5);
+        this.mark_choice("prefs_clear_flash_cookies", prefs, 6);
+        this.mark_choice("prefs_block_scripting", prefs, 7);
+        this.mark_choice("prefs_block_geolocation", prefs, 8);
+        this.mark_choice("prefs_disable_html5_pings", prefs, 9);
+        this.mark_choice("prefs_disable_referrer", prefs, 10);
+        this.mark_choice("prefs_disable_dom_storage", prefs, 11);
+        this.mark_choice("prefs_disable_flash", prefs, 12);
+        this.mark_choice("prefs_disable_java", prefs, 13);
+        this.mark_choice("prefs_use_by_default", prefs, 14);
+        this.mark_choice("prefs_do_not_track", prefs, 15);
+      }
+    }
+  },
+
+  save_prefs: function (host)
+  {
+    let database = window.database;
+    let db = database.db;
+    let simple_group = document.getElementById("simple_choice");
+    let prefs = 0;
+
+    if (this.simple_choices)
+      prefs = simple_group.selectedIndex;
+    else
+    {
+      prefs = 3;
+      prefs |= this.get_choice("prefs_never_block", 2);
+      prefs |= this.get_choice("prefs_ext_3rd_parties", 3);
+      prefs |= this.get_choice("prefs_ext_3rd_cookies", 4);
+      prefs |= this.get_choice("prefs_all_lasting_cookies", 5);
+      prefs |= this.get_choice("prefs_clear_flash_cookies", 6);
+      prefs |= this.get_choice("prefs_block_scripting", 7);
+      prefs |= this.get_choice("prefs_block_geolocation", 8);
+      prefs |= this.get_choice("prefs_disable_html5_pings", 9);
+      prefs |= this.get_choice("prefs_disable_referrer", 10);
+      prefs |= this.get_choice("prefs_disable_dom_storage", 11);
+      prefs |= this.get_choice("prefs_disable_flash", 12);
+      prefs |= this.get_choice("prefs_disable_java", 13);
+      prefs |= this.get_choice("prefs_use_by_default", 14);
+      prefs |= this.get_choice("prefs_do_not_track", 15);
+    }
+
+    database.set_prefs(host, prefs);
+
+    if (prefs & (1 << 14))
+      database.set_prefs("default", prefs);
+
+    let global_prefs = 0;
+    // now get global choices and save to database under "global"
+  },
+
+  get_choice: function (id, code)
+  {
+    let checkbox = document.getElementById(id);
+
+    if (checkbox.getAttribute("checked") == "true")
+      return (1 << code);
+
+    return 0;
+  },
+
+  mark_choice: function (id, prefs, code)
+  {
+    let checkbox = document.getElementById(id);
+    let state = prefs & (1 << code);
+    checkbox.setAttribute("checked", state ? "true" : "false");
+  },
+
+  update_current_site: function (status)
+  {
+    let uri = status.uri;
+    let host = status.page_host;
+    let path = uri.path;
+
+    if (status.new_url)
+    {
+      host = dashboard.extract_host(status.new_url);
+    }
+
+    this.status = status;
+    this.uri = uri;
+    this.host = host;
+    this.path = path;
+
+    let textbox = document.getElementById("query_textbox");
+    textbox.value=host;
+    //dashboard.history.set();
+
+    let sd = document.getElementById("site_description");
+    let p = document.getElementById("curhost");
+
+    if (p)
+      p.innerHTML = host ? host : "Blank page, no host";
+
+    let singleton_window =
+        Components.classes["@mozilla.org/appshell/appShellService;1"].
+          getService(Components.interfaces.nsIAppShellService).hiddenDOMWindow;
+
+    p.style.backgroundImage = singleton_window.dashboard_icon;
+
+    if (host)
+    {
+      this.refresh_prefs(host);
+      this.show_status(status);
+      sd.className = "";
+    }
+    else
+      sd.className = "hidden";
+  },
+
+  show_policy: function ()
+  {
+    let host = assess.host;
+    let path = assess.path;
+    let policy_uri = "http://"+host+"/w3c/p3p.xml";
+
+    dashboard_p3p.render_policy(policy_uri, path, 0);
+  },
+
+  show_status: function (status)
+  {
+    let list = document.getElementById("curstatus");
+
+    // clear status
+    let child = list.firstChild;
+
+    while (child)
+    {
+      list.removeChild(child);
+      child = list.firstChild;
+    }
+
+    let localize = dashboard.localize;
+
+    this.status_count = 0;
+
+    this.add_item(list, status.session_cookies,
+              "access.caption.cookies.session",
+              "access.tooltip.cookies.session");
+    this.add_item(list, status.lasting_cookies,
+              "access.caption.cookies.lasting",
+              "access.tooltip.cookies.lasting");
+    this.add_item(list, status.flash_cookies,
+              "access.caption.cookies.flash",
+              "access.tooltip.cookies.flash");
+
+    this.add_item(list, status.int_3rd_parties,
+              "access.caption.internal.third.parties",
+              "access.tooltip.internal.third.parties");
+
+    this.add_item(list, status.ext_3rd_parties,
+              "access.caption.external.third.parties",
+              "access.tooltip.external.third.parties");
+
+    this.add_item(list, status.int_3rd_party_session_cookies,
+              "access.caption.internal.third.parties.cookies.session",
+              "access.tooltip.internal.third.parties.cookies.session");
+    this.add_item(list, status.int_3rd_party_lasting_cookies,
+              "access.caption.internal.third.parties.cookies.lasting",
+              "access.tooltip.internal.third.parties.cookies.lasting");
+    this.add_item(list, status.int_3rd_party_flash_cookies,
+              "access.caption.internal.third.parties.cookies.flash",
+              "access.tooltip.internal.third.parties.cookies.flash");
+
+    this.add_item(list, status.ext_3rd_party_session_cookies,
+              "access.caption.external.third.parties.cookies.session",
+              "access.tooltip.external.third.parties.cookies.session");
+    this.add_item(list, status.ext_3rd_party_lasting_cookies,
+              "access.caption.external.third.parties.cookies.lasting",
+              "access.tooltip.external.third.parties.cookies.lasting");
+    this.add_item(list, status.ext_3rd_party_flash_cookies,
+              "access.caption.external.third.parties.cookies.flash",
+              "access.tooltip.external.third.parties.cookies.flash");
+
+    this.add_item(list, status.dom_storage,
+              "access.caption.dom_storage",
+              "access.tooltip.dom_storage");
+
+    child = this.add_item(list, status.geo_permission,
+              "access.caption.geolocation",
+              "access.tooltip.geolocation");
+
+    if (child)
+      child.innerHTML = localize("access.caption.geolocation");
+
+    if (status.html5_pings)
+    {
+      let item = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+      list.appendChild(item);
+
+      if (status.html5_pings == 1)
+        item.innerHTML = dashboard.localize("access.caption.ping1");
+      else
+        item.innerHTML = status.html5_pings + " "  + dashboard.localize("access.caption.ping");
+
+      item.setAttribute("tooltiptext", dashboard.localize("access.tooltip.ping"));
+      this.status_count++;
+    }
+
+    if (status.invisible_images)
+    {
+      let item = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+      list.appendChild(item);
+
+      if (status.invisible_images == 1)
+        item.innerHTML = dashboard.localize("access.caption.images1");
+      else
+        item.innerHTML = status.invisible_images + " "  + dashboard.localize("access.caption.images");
+
+      item.setAttribute("tooltiptext", dashboard.localize("access.tooltip.images"));
+      this.status_count++;
+    }
+
+    if (status.suspicious_urls)
+    {
+      let item = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+      list.appendChild(item);
+
+      if (status.suspicious_urls == 1)
+        item.innerHTML = dashboard.localize("access.caption.bad_urls1");
+      else
+        item.innerHTML = status.suspicious_urls + " "  + dashboard.localize("access.caption.bad_urls");
+
+      item.setAttribute("tooltiptext", dashboard.localize("access.tooltip.bad_urls"));
+      this.status_count++;
+    }
+
+    if (status.p3p)
+    {
+      let item = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+      list.appendChild(item);
+      item.innerHTML = dashboard.localize("access.a") +
+           " <html:a tooltiptext='"+
+           dashboard.localize("access.tooltip.p3p") +
+           "' onclick='assess.show_policy()'>" +
+           dashboard.localize("access.caption.p3p") + "</html:a>";
+      this.status_count++;
+    }
+
+    if (status.old_host && status.old_host != status.page_host)
+    {
+      let item = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+      list.appendChild(item);
+
+      item.innerHTML = dashboard.localize("access.caption.redirect") +
+        "<html:br/>" + status.old_host;
+
+      item.setAttribute("tooltiptext", dashboard.localize("access.tooltip.redirect"));
+    }
+
+    let has = document.getElementById("website_has");
+
+    if (this.status_count == 0)
+      has.innerHTML = "";
+    else
+      has.innerHTML = dashboard.localize("access.site")+":";
+
+    let sd = document.getElementById("site_description");
+
+    if (this.status_count)
+      sd.className = "";
+    else
+      sd.className = "hidden";
+  },
+
+  show_help: function (info)
+  {
+     let help_positioner = document.getElementById("help_positioner");
+     let help_box = document.getElementById("context_help_box");
+     let help_label = document.getElementById("context_help_label");
+     let help_text = document.getElementById('dashboard-strings').getString(info);
+     help_label.innerHTML = "";
+     help_label.appendChild(document.createTextNode(help_text));
+     help_box.openPopup(help_positioner, "after_start"); 
+  },
+
+  hide_help: function ()
+  {
+     let help_box = document.getElementById("context_help_box");
+     help_box.hidePopup();
+  },
+
+  add_item: function (list, count, what, help)
+  {
+    if (count > 0)
+    {
+      let item = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+      list.appendChild(item);
+      let num;
+
+      if (count == 1)
+        item.innerHTML = dashboard.localize(what+"1");
+      else
+        item.innerHTML = count + " " + dashboard.localize(what);
+
+      item.setAttribute("tooltiptext", dashboard.localize(help));
+/*
+      var help_button = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
+      help_button.setAttribute("src", 'chrome://dashboard-common/skin/help.png');
+      help_button.addEventListener("mouseover", function () { assess.show_help(help); }, false);
+      help_button.addEventListener("mouseout", function () { assess.hide_help(); }, false);
+      item.appendChild(help_button);
+*/
+      this.status_count++;
+      return item;
+    }
+
+    return null;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/dashboard.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,1158 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Dashboard.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+var console = {
+  service: null,
+  log: function(msg)
+  {
+    if (!console.service)
+      console.service = Components.classes["@mozilla.org/consoleservice;1"]
+                     .getService(Components.interfaces.nsIConsoleService);
+    console.service.logStringMessage(msg);
+  }
+};
+
+var dashboard = {
+  on_load: function ()
+  {
+    window.dashboard = this;
+    var parent_window = window.arguments ? window.arguments[1] : null;
+
+    this.strings = document.getElementById("dashboard-strings");
+
+    // URL for server to share data with
+    let share_to = document.getElementById("share_to");
+    share_to.addEventListener("change", function (e)
+    {
+       // Get the "extensions.myext." branch
+       let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                    .getService(Components.interfaces.nsIPrefService);
+       prefs = prefs.getBranch("extensions.privacydashboard.");
+
+       let value = e.target.value;
+       if (!/^http.+/.test(value))
+       {
+         value = "http://" + value;
+         e.target.value = value;
+       }
+
+       prefs.setCharPref("share.with", value);
+       return false;
+    }, false);
+
+    // hypertext link in XUL dialog normally opens in that dialog
+    // we override that behavior here to force load into new tab
+    // on the parent browser window, and to then select that tab
+    let link = document.getElementById("dashboard_website");
+    link.addEventListener("click", function (e)
+    {
+       let browser = parent_window.gBrowser;
+       browser.selectedTab =  browser.addTab("http://www.primelife.eu");
+       e.cancel = true;
+       e.preventDefault();
+       return false;
+    }, false);
+
+    link = document.getElementById("primelife_website");
+    link.addEventListener("click", function (e)
+    {
+       let browser = parent_window.gBrowser;
+       browser.selectedTab =  browser.addTab("http://www.primelife.eu");
+       e.cancel = true;
+       e.preventDefault();
+       return false;
+    }, false);
+
+    // set event handler for location mode buttons
+    let loc_mode_el = document.getElementById("location_mode");
+    loc_mode_el.addEventListener("select", function ()
+    {
+      dashboard.update_location();
+    }, false);
+
+    // hook up handlers for back/forward buttons
+    let leftbutton = document.getElementById("backward");
+    leftbutton.addEventListener("command", function ()
+    {
+      dashboard.history.back();
+    }, false);
+
+    let rightbutton = document.getElementById("forward");
+    rightbutton.addEventListener("command", function ()
+    {
+      dashboard.history.forward();
+    }, false);
+
+    // hook up button for executing queries
+    let execbutton = document.getElementById("exec_query_button");
+    execbutton.addEventListener("command", function ()
+    {
+      dashboard.exec_query(true);
+    }, false);
+
+    let query_tree = document.getElementById("query_tree");
+
+    // set click handler for results table
+    query_tree.addEventListener("click", this.results_click, false);
+
+    // make Enter key execute queries for data track 
+    let query_menu = document.getElementById('query_menu');
+
+    query_menu.addEventListener("keydown", function (e)
+    {
+      if (e.which == 13)
+        dashboard.exec_query(true);
+    }, false);
+
+    let tabs = document.getElementById("dashboard-tabs");
+
+    // when returning to current website tab
+    // set the datatrack text box to current host
+    tabs.addEventListener("select", function (e)
+    {
+       if (e.target.selectedIndex == 2)
+       {
+         let textbox = document.getElementById("query_textbox");
+         textbox.value = assess.host;
+         //dashboard.history.set();
+       }
+    }, false);
+
+    let textbox = document.getElementById("query_textbox");
+
+    textbox.addEventListener("keydown", function (e)
+    {
+      if (e.which == 13)
+        dashboard.exec_query(true);
+    }, false);
+
+    // adapt dialog window height for smaller displays
+    // this particular technique may be too late to work?
+    if (window.screen.height < 1000)
+       query_tree.setAttribute("rows", "10");
+
+    // activate the current website tab
+    let tabbox = document.getElementsByTagName("tabbox");
+    tabbox[0].selectedIndex = 2;
+
+    // activate helper object for assessing current site
+    assess.start_up(this);
+
+    // get extension's version number
+    let gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"]
+                        .getService(Components.interfaces.nsIExtensionManager);
+    let myVersion = gExtensionManager.getItemForID("[email protected]").version;
+
+    let about_heading = document.getElementById("dashboard_heading");
+    about_heading.innerHTML = about_heading.innerHTML + " " + myVersion;
+
+    // record whether site supports P3P
+
+    // try to find directory for flash storage
+    //this.set_flash_dir();
+    //var misc = window.database.misc;
+    //misc.scan_flash_storage();  // test of reading folders
+
+    // demo Firefox's ability to find out where you are (see about tab)
+    this.update_location();
+  },
+
+  on_unload: function ()
+  {
+    // de-activate helper object for assessing current site
+    assess.shut_down();
+
+    // ensure parent browser knows
+    let context = window.dashboard_overlay.context;
+    context.dashboard_dialog = null;
+  },
+
+  localize: function (key)
+  {
+    try
+    {
+      return dashboard.strings.getString(key);
+    }
+    catch (e)
+    {
+      alert("Couldn't get localized string for " + key);
+      return key;
+    }
+  },
+
+  history: 
+  {
+    here: -1,
+    history: [],
+
+    button_state: function (state)
+    {
+      let backward = document.getElementById('backward');
+      let forward = document.getElementById('forward');
+      backward.className = this.here >= 0 ? "" : "inactive";
+      forward.className = this.here < this.history.length - 1 ? "" : "inactive";
+
+      let index = state ? parseInt(state.query) : 0;
+      let query_param = document.getElementById('query_textbox');
+      query_param.disabled = index < 8 || index == 12;
+    },
+
+    execute: function (state)
+    {
+      let query_menu = document.getElementById('query_menu');
+      let query_param = document.getElementById('query_textbox');
+
+      query_menu.value = "" + state.query
+      query_param.value = state.value;
+      dashboard.exec_query(false);
+      this.button_state(state);
+      console.log("exec: " + this.here + ", " + JSON.stringify(state));
+    },
+
+
+    back: function (e)
+    {
+      if (this.here > 0)
+      {
+        let state = this.history[--this.here];
+        this.execute(state);
+      }
+      else
+      {
+        let tree = document.getElementById('query_tree');
+        let treecols = dashboard.get_element(tree, "treecols");
+        let treechildren = dashboard.get_element(tree, "treechildren");
+
+        dashboard.strip_content(treecols);
+        dashboard.strip_content(treechildren);
+        let treecol = document.createElement("treecol");
+        treecol.setAttribute("flex", 1);
+        treecols.appendChild(treecol);
+        this.here = -1;
+        this.button_state();
+      }
+    },
+
+    forward: function (e)
+    {
+      if (this.here < this.history.length - 1)
+      {
+        let state = this.history[++this.here];
+        this.execute(state);
+      }
+    },
+
+    set: function ()
+    {
+      let query_menu = document.getElementById('query_menu');
+      let query_param = document.getElementById('query_textbox');
+
+      let state = { "value": query_param.value, "query": query_menu.value };
+      this.history[++this.here] = state;
+      this.history = this.history.slice(0, this.here + 1);
+      this.button_state(state);
+    },
+
+    clear: function ()
+    {
+      this.here = 0;
+      this.history = [];
+      this.button_state();
+    }
+  },
+
+  results_click: function (e)
+  {
+    let query_tree = document.getElementById("query_tree");
+    let textbox = document.getElementById("query_textbox");
+    try
+    {
+      let treechildren = e.target;
+      let treeitem = treechildren.firstChild;
+      let i, selected = query_tree.currentIndex;
+
+      for (i = 0; i < selected; ++i)
+        treeitem = treeitem.nextSibling;
+
+      let treerow = treeitem.firstChild
+      let treecell = treerow.firstChild;
+      textbox.value = treecell.getAttribute("label");
+      //dashboard.history.set();
+    }
+    catch (e) { }
+  },
+
+  // pass this on to helper object
+  // note that the doc uri may differ from the uri passed here
+  // which is obtained from the onLocationChange event
+  update_current_site: function (status) {
+    this.assess.update_current_site(status);
+  },
+
+  show_status: function ()
+  {
+    assess.show_status();
+  },
+
+  update_location: function ()
+  {
+    let loc_tab = document.getElementById("dashboard_location");
+    let loc_mode_el = document.getElementById("location_mode");
+    let mode = loc_mode_el.selectedIndex;
+
+    let sitelist = document.getElementById("site_list");
+
+    while (sitelist.firstChild)
+      sitelist.removeChild(sitelist.firstChild);
+
+    if (mode == 0) // show list of sites with permission to access geolocation
+    {
+      loc_tab.className = "show_sites";
+      let p = document.createElementNS("http://www.w3.org/1999/xhtml", "p");
+      sitelist.appendChild(p); 
+      p.innerHTML = dashboard_overlay.localize("dashboard.loc7");
+
+      let vbox = document.createElementNS(
+        "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "vbox");
+      sitelist.appendChild(vbox);
+      vbox.setAttribute("style", "overflow:auto;height:400px");
+
+      this.show_geo_sites(vbox);
+    }
+    else // show geographic location on map
+    {
+      loc_tab.className = "show_map";
+      let map = document.getElementById("map");
+
+      if (map.getAttribute("src") == "")
+      {     
+        let pos = document.getElementById("geoloc");
+        pos.innerHTML = "";
+
+        let geo_prefs = Components.classes["@mozilla.org/preferences-service;1"]
+           .getService(Components.interfaces.nsIPrefService).getBranch("geo.");
+
+        let geo_pref = geo_prefs.getBoolPref("enabled");
+        geo_prefs.setBoolPref("enabled", true);
+
+        try
+        {
+          let geolocation = Components.classes["@mozilla.org/geolocation;1"]  
+                      .getService(Components.interfaces.nsIDOMGeoGeolocation);
+
+          if (geolocation)
+            geolocation.getCurrentPosition(this.set_gelocation,
+                       this.geo_error, {enableHighAccuracy:true, timeout:8000});
+          else
+            alert("couldn't get geolocation interface");
+        }
+        catch (e)
+        {
+          let error = {};
+          error.code = 3;
+          this.geo_error(error);
+          alert(e);
+        }
+
+        geo_prefs.setBoolPref("enabled", geo_pref);
+      }
+    }
+  },
+
+  show_geo_sites: function (vbox)
+  {
+    let handler =
+    {
+      hosts: [],
+      permissions: [],
+      count: 0,
+      vbox: vbox,
+
+      process_row: function (row)
+      {
+        let host = row.getResultByName("host");
+        this.hosts[this.count] = host;
+        let permission = row.getResultByName("permission");
+        this.permissions[this.count] = permission;
+        this.count++;
+      },
+
+      // ready to revoke selected geolocation permissions
+      finalize: function ()
+      {
+        if (this.count == 0)
+        {
+          let sitelist = this.vbox.parentNode;
+          for (let p = sitelist.firstChild; p; p = p.nextSibling)
+          {
+            if (p.nodeType == "1" && p.nodeName == "p")
+            {
+              p.innerHTML = dashboard_overlay.localize("dashboard.loc8");
+              break;
+            }
+          }
+        }
+
+        for (let i = 0; i < this.count; ++i)
+        {
+          var checkbox = document.createElementNS(
+            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+            "checkbox");
+          this.vbox.appendChild(checkbox);
+          let allow = dashboard_overlay.localize("dashboard.loc9");
+          let deny = dashboard_overlay.localize("dashboard.loc10");
+          checkbox.setAttribute("label", this.hosts[i] + ", " +
+                   (this.permissions[i] ? allow : deny));
+          checkbox.setAttribute("checked", "false");
+          checkbox.host = this.hosts[i];
+          checkbox.addEventListener("command", function ()
+          {
+            let database = dashboard_overlay.database;
+            database.permissions.executeSimpleSQL(
+              "DELETE FROM moz_hosts WHERE type = 'geo' AND host = " +
+                   database.quote(this.host));
+            checkbox.parentNode.removeChild(checkbox);
+          }, false);
+        }
+
+        this.hosts = null;
+        this.permissions = null;
+      }
+    };
+
+    let database = dashboard_overlay.database;
+    let sql = "SELECT host, permission FROM moz_hosts WHERE type = 'geo'";
+
+    database.select(sql, database.permissions, handler);
+  },
+
+  // show where you are on the dashboard location tab
+  set_gelocation: function (position)
+  {
+    let loc_mode_el = document.getElementById("location_mode");
+    let mode = loc_mode_el.selectedIndex;
+
+    if (mode == 1)
+    {
+      let pos = document.getElementById("geoloc");
+      let address = "", addr = position.address;
+      let latitude = dashboard.round(position.coords.latitude, 1000);
+      let longitude = dashboard.round(position.coords.longitude, 1000);
+
+      if (addr)
+        address = "<html:br/>" + addr.street + ", " + addr.city + ", " + addr.country;
+
+      pos.innerHTML = dashboard.localize("dashboard.loc1")+": " + latitude + " " +
+                    dashboard.localize("dashboard.loc2")+": " + longitude + address;
+
+      let map = document.getElementById("map");
+      let gmap = "http://maps.google.com/maps/api/staticmap?";
+      let gparams = "sensor=false&zoom=14&size=600x380&maptype=roadmap"
+      let loc = latitude + "," + longitude;
+      map.setAttribute("src", gmap+gparams+"&center=" + loc + "&markers=" + loc);
+    }
+  },
+
+  geo_error: function (error)
+  {
+    let loc_mode_el = document.getElementById("location_mode");
+    let mode = loc_mode_el ? loc_mode_el.selectedIndex : 0;
+
+    if (mode == 1)
+    {
+      let pos = document.getElementById("geoloc");
+      pos.innerHTML = "<html:em>" + dashboard.localize("dashboard.loc3") +
+        (error.code == 1 ? " (" + dashboard.localize("dashboard.loc4")+")" : 
+        (error.code == 3 ?  " ("+ dashboard.localize("dashboard.loc5")+")"
+        : "")) + ".</html:em> " + dashboard.localize("dashboard.loc6");
+
+      let map = document.getElementById("map");
+      map.setAttribute("src", "");
+    }
+  },
+
+  select_query: function ()
+  {
+    let caption;
+    let query_menu = document.getElementById('query_menu');
+    let label = document.getElementById("query_textbox_label");
+    let textbox = document.getElementById("query_textbox");
+    let autocomplete = "history";
+    let unused = "2. unused";
+
+    switch (parseInt(query_menu.value))
+    {
+       case 0: // Which domains use long lasting cookies?
+           caption = unused;
+         break;
+
+       case 1: // Which domains use session cookies?
+           caption = unused;
+         break;
+
+       case 2: // Which domains use flash cookies?
+           caption = unused;
+         break;
+
+       case 3: // Which domains use DOM storage?
+           caption = unused;
+         break;
+
+       case 4: // Which domains use invisible images?
+           caption = unused;
+         break;
+
+       case 5: // Which domains use html5 ping attributes?
+           caption = unused;
+         break;
+
+       case 6: // Which domains use P3P?
+           caption = unused;
+         break;
+
+       case 7: // Which domains are 3rd parties?
+           caption = unused;
+         break;
+
+       case 8: // Which websites use a given 3rd party?
+           caption = dashboard.localize("dashboard.query1")+":";
+         break;
+
+       case 9: // Which internal 3rd parties are used by a given domain?
+           caption = dashboard.localize("dashboard.query1")+":";
+         break;
+
+       case 10: // Which external 3rd parties are used by a given domain?
+           caption = dashboard.localize("dashboard.query1")+":";
+         break;
+
+       case 11: // What cookies are used by a given domain?
+           caption = dashboard.localize("dashboard.query1")+":";
+         break;
+
+       case 12: // Which sites have I given access to my location?
+           caption = unused;
+         break;
+
+       case 13: // What data has been sent to a given domain?
+           caption = dashboard.localize("dashboard.query1")+":";
+         break;
+
+       case 14: // Which domains a given datum value has been sent to?
+           caption = dashboard.localize("dashboard.query2")+":";
+         break;
+
+       case 15: // Which domains a given datum name has been sent to?
+           caption = dashboard.localize("dashboard.query3")+":";
+           autocomplete = "form-history";  // WRONG - FIX ME
+         break;
+
+       case 16: // Which datum names are used for a given value?
+           caption = dashboard.localize("dashboard.query4")+":";
+           autocomplete = "form-history";
+         break;
+    }
+
+    label.setAttribute("value", caption);
+    textbox.disabled = (caption == unused ? true : false);
+    textbox.setAttribute("autocompletesearch", autocomplete);
+    //dashboard.history.set();
+  },
+
+  exec_query: function (history)
+  {
+    let query_menu = document.getElementById('query_menu');
+    let query_param = document.getElementById('query_textbox');
+    let query, names, param = query_param.value;
+    let database = window.database;
+    let db = database.db;
+
+    query = names = null;
+    let dom_storage_query = false;
+
+    // test needed to avoid calling set from back or forward actions
+    if (history)
+      dashboard.history.set();
+
+    switch (parseInt(query_menu.value))
+    {
+       case 0: // Which hosts use long lasting cookies?
+           //this.show_cookies(false);  // use cookie manager to enumerate cookies
+           query = "SELECT DISTINCT host FROM site_info WHERE lasting_cookies <> 0";
+           names = ["host"];
+         break;
+
+       case 1: // Which hosts use session cookies?
+           //this.show_cookies(true);
+           query = "SELECT DISTINCT host FROM site_info WHERE session_cookies <> 0";
+           names = ["host"];
+         break;
+
+       case 2: // Which hosts use flash cookies?
+           this.show_flash_cookies();
+         break;
+
+       case 3: // Which hosts use DOM storage?
+           db = database.dom_storage;
+           dom_storage_query = true;
+           query = "SELECT scope, key, value, secure FROM webappsstore2";
+           names = ["scope", "key", "value", "secure"];
+         break;
+
+       case 4: // Which hosts use invisible images?
+           query = "SELECT host FROM site_info WHERE invisible_images <> 0";
+           names = ["host"];
+         break;
+
+       case 5: // Which hosts use html5 ping attributes?
+           query = "SELECT host FROM site_info WHERE html5_pings <> 0";
+           names = ["host"];
+         break;
+
+       case 6: // Which hosts use P3P?
+           query = "SELECT host FROM site_info WHERE p3p <> 0";
+           names = ["host"];
+         break;
+
+       case 7: // Which domains are 3rd parties?
+           query = "SELECT DISTINCT third_party FROM parties WHERE offsite <> 0";
+           names = ["third_party"];
+         break;
+
+       case 8: // Which websites use a given 3rd party?
+           param = this.extract_host(param);
+           query = "SELECT DISTINCT page_host FROM parties WHERE offsite <> 0 " +
+                   "AND third_party LIKE '%" + dashboard.escape1(param) + "'";
+           names = ["page_host"];
+         break;
+
+       case 9: // Which internal 3rd parties are used by a given host?
+           param = this.extract_host(param);
+           query = "SELECT DISTINCT third_party FROM parties WHERE offsite = 1 " +
+                   "AND page_host LIKE '%" + dashboard.escape1(param) + "'";
+           names = ["third_party"];
+         break;
+
+       case 10: // Which external 3rd parties are used by a given host?
+           param = this.extract_host(param);
+           query = "SELECT DISTINCT third_party FROM parties WHERE offsite = 2 " +
+                   "AND page_host LIKE '%" + dashboard.escape1(param) + "'";
+           names = ["third_party"];
+         break;
+
+       case 11: // What cookies are used by a given domain?
+           this.show_cookies_for_host(param);  // from cookie manager
+         break;
+
+       case 12: // Which hosts have I given access to my location?
+           db = database.permissions;
+           query = "SELECT host, permission FROM moz_hosts WHERE type = 'geo'";
+           names = ["host", "permission"];
+         break;
+
+       case 13: // What data has been sent to a given host?
+           param = this.extract_host(param);
+           query = "SELECT name, value, form, time FROM http_data WHERE host LIKE '%"
+                   + dashboard.escape2(param) + "'";
+           names = ["name", "value", "form", "time"];
+         break;
+
+       case 14: // Which hosts a given datum value has been sent to?
+           query = "SELECT host, time FROM http_data WHERE value = '"
+                   + dashboard.escape1(param) + "'";
+           names = ["host", "time"];
+         break;
+
+       case 15: // Which hosts a given datum name has been sent to?
+           query = "SELECT host, time FROM http_data WHERE name = '"
+                   + dashboard.escape1(param) + "'";
+           names = ["host", "time"];
+         break;
+
+       case 16: // Which datum names are used for a given value?
+           query = "SELECT DISTINCT name FROM http_data WHERE value ='"
+                   + dashboard.escape1(param) + "'";
+           names = ["name"];
+         break;
+    }
+
+    if (query)
+    {
+      let tree = document.getElementById('query_tree');
+      let treecols = this.get_element(tree, "treecols");
+      let treechildren = this.get_element(tree, "treechildren");
+
+      this.strip_content(treecols);
+      this.strip_content(treechildren);
+      this.create_columns(treecols, names);
+      var self = this;
+
+      let handler =
+      {
+        process_row: function (row)
+        {
+          let treeitem = document.createElement("treeitem");
+          let treerow = document.createElement("treerow");
+          treeitem.appendChild(treerow);
+
+          for (let i = 0; i < names.length; ++i)
+          {
+            let cell = document.createElement("treecell");
+            let name = names[i];
+            let value = row.getResultByName(name);
+
+            // ugly hack to recover host name from scope field
+            if (dom_storage_query && name == "scope")
+              value = this.scope_to_host(value);
+
+            cell.setAttribute("label", value);
+            treerow.appendChild(cell);
+          }
+
+          treechildren.appendChild(treeitem);
+        },
+
+        finalize: function ()
+        {
+        }
+      };
+
+      database.select(query, db, handler);
+    }
+  },
+
+  create_columns: function (treecols, names)
+  {
+    let treecol, splitter;
+
+    for (let i = 0; i < names.length; ++i)
+    {
+      treecol = document.createElement("treecol");
+      treecol.setAttribute("label", names[i]);
+      treecol.setAttribute("flex", "1");
+      treecols.appendChild(treecol);
+
+      splitter = document.createElement("splitter");
+      splitter.setAttribute("class", "tree-splitter");
+      splitter.setAttribute("resizeafter", "grow");
+      treecols.appendChild(splitter);
+    }
+  },
+
+  // generate host from mozilla's reverse syntax for scope
+  scope_to_host: function (s)
+  {
+    let i = s.lastIndexOf(':');
+    let port = s.substr(i);
+    i = s.indexOf(':') - 1;
+    let host = s.substr(0, i);
+    host = host.split('').reverse();
+    return (host.join(''))+port;
+  },
+
+  extract_host: function (url)
+  {
+    let start = url.indexOf("http://");
+
+    if (start == 0)
+      url = url.substr(7);
+
+    let end = url.indexOf("/");
+ 
+    if (end > 0)
+      url = url.substr(0, end);
+
+    let port = url.indexOf(":")
+
+    if (port > 0)
+      url = url.substr(0, port);
+
+    return url;
+  },
+
+  set_flash_dir: function ()
+  {
+    // find and present flash storage directory
+    let el = document.getElementById("flashdir");
+    let misc = window.database.misc;
+    let dir = misc.get_flash_dir();
+
+    if (el && dir)
+      el.innerHTML = "Flash storage directory: " + dir.path;
+
+    // do same for the Firefox profile directory
+    // for webappsstore.sqlite used for DOM storage
+    el = document.getElementById("profiledir");
+    dir = Components.classes["@mozilla.org/file/directory_service;1"].
+               getService(Components.interfaces.nsIProperties).
+               get("ProfD", Components.interfaces.nsIFile);
+
+    if (el && dir)
+      el.innerHTML = "Firefox profile directory: " + dir.path;
+  },
+
+  show_flash_cookies: function ()
+  {
+    let names = ["Host", "File", "Size", "Last Modified"];
+    let tree = document.getElementById('query_tree');
+    let treecols = this.get_element(tree, "treecols");
+    let treechildren = this.get_element(tree, "treechildren");
+
+    this.strip_content(treecols);
+    this.strip_content(treechildren);
+    this.create_columns(treecols, names);
+
+    let misc = window.database.misc;
+    let hosts = misc.scan_flash_storage();
+
+    for (let host in hosts)
+    {
+      let files = hosts[host];
+
+      for (let j = 0; j < files.length; ++j)
+      {
+        let shared_object = files[j];
+        let treeitem = document.createElement("treeitem");
+        let treerow = document.createElement("treerow");
+        treeitem.appendChild(treerow);
+
+        let cell = document.createElement("treecell");
+        cell.setAttribute("label", host);
+        treerow.appendChild(cell);
+
+        cell = document.createElement("treecell");
+        cell.setAttribute("label", files[j].name);
+        treerow.appendChild(cell);
+
+        cell = document.createElement("treecell");
+        cell.setAttribute("label", files[j].size);
+        treerow.appendChild(cell);
+
+        cell = document.createElement("treecell");
+        cell.setAttribute("label", files[j].date);
+        treerow.appendChild(cell);
+
+        treechildren.appendChild(treeitem);
+      }
+    }
+  },
+
+  show_cookies: function (session)
+  {
+    let names = ["host", "path", "name", "policy", "expires", "last accessed"];
+    let tree = document.getElementById("query_tree");
+    let treecols = this.get_element(tree, "treecols");
+    let treechildren = this.get_element(tree, "treechildren");
+    let database = window.database;
+
+    this.strip_content(treecols);
+    this.strip_content(treechildren);
+    this.create_columns(treecols, names);
+
+    let cm = Components.classes['@mozilla.org/cookiemanager;1'].
+      getService(Components.interfaces.nsICookieManager2);
+
+    let cookies = cm.enumerator;
+    let results = [];
+
+    while (cookies.hasMoreElements())
+    {
+      let cookie = cookies.getNext();
+      cookie.QueryInterface(Components.interfaces.nsICookie2);
+
+      //if (cookie)
+      //  alert(cookie.host);
+
+      if (cookie.isSession)
+      {
+        if (session)
+          results.push(cookie);
+      }
+      else if (!session)
+        results.push(cookie);
+    }
+
+    for (let i = 0; i < results.length; ++i)
+    {
+      cookie = results[i];
+      var treeitem = document.createElement("treeitem");
+      var treerow = document.createElement("treerow");
+      treeitem.appendChild(treerow);
+
+      let host = cookie.host;
+
+      if (host.charAt(0) == '.')
+        host = '*' + host;
+
+      let cell = document.createElement("treecell");
+      cell.setAttribute("label", host);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.path);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.name);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.policy);
+      treerow.appendChild(cell);
+
+      // expires is in seconds since epoch
+      let date = new Date((cookie.expires)*1000);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", database.iso8061(date));
+      treerow.appendChild(cell);
+
+      // last accessed is in microseconds since epoch
+      date = new Date((cookie.lastAccessed)/1000);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", database.iso8061(date));
+      treerow.appendChild(cell);
+
+      treechildren.appendChild(treeitem);
+    }
+  },
+
+  show_cookies_for_host: function (host)
+  {
+    let domain = this.base_domain(host);
+    let names = ["host", "path", "name", "session", "secure", "P3P policy", 
+                 "P3P status", "expires", "created", "last accessed"];
+    let tree = document.getElementById("query_tree");
+    let treecols = this.get_element(tree, "treecols");
+    let treechildren = this.get_element(tree, "treechildren");
+    let database = window.database;
+    let date;
+
+    this.strip_content(treecols);
+    this.strip_content(treechildren);
+    this.create_columns(treecols, names);
+
+    let cm = Components.classes['@mozilla.org/cookiemanager;1'].
+      getService(Components.interfaces.nsICookieManager2);
+
+    let cookies = cm.enumerator;
+    let results = [];
+
+    while (cookies.hasMoreElements())
+    {
+      let cookie = cookies.getNext();
+      cookie.QueryInterface(Components.interfaces.nsICookie2);
+
+      let len = cookie.host.length;
+      let i = cookie.host.indexOf(domain);
+
+      // does host end with domain?
+      if (i >= 0 && len == i + domain.length)
+        results.push(cookie);
+    }
+
+    for (let i = 0; i < results.length; ++i)
+    {
+      let cookie = results[i];
+      let treeitem = document.createElement("treeitem");
+      let treerow = document.createElement("treerow");
+      treeitem.appendChild(treerow);
+
+      let host = cookie.host;
+
+      if (host.charAt(0) == '.')
+        host = '*' + host;
+
+      let cell = document.createElement("treecell");
+      cell.setAttribute("label", host);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.path);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.name);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.isSession);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.isSecure);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.policy);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", cookie.status);
+      treerow.appendChild(cell);
+
+      cell = document.createElement("treecell");
+
+      // expires is in seconds since epoch
+      if (cookie.isSession)
+        cell.setAttribute("label", "");
+      else
+      {
+        let date = new Date((cookie.expires)*1000);
+        cell.setAttribute("label", database.iso8061(date));
+      }
+
+      treerow.appendChild(cell);
+
+      // last accessed is in microseconds since epoch
+      let date = new Date((cookie.creationTime)/1000);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", database.iso8061(date));
+      treerow.appendChild(cell);
+
+      // last accessed is in microseconds since epoch
+      date = new Date((cookie.lastAccessed)/1000);
+
+      cell = document.createElement("treecell");
+      cell.setAttribute("label", database.iso8061(date));
+      treerow.appendChild(cell);
+
+      treechildren.appendChild(treeitem);
+    }
+  },
+
+  strip_content: function (element)
+  {
+    while (element.firstChild)
+      element.removeChild(element.firstChild);
+  },
+
+  create_element: function (tag, label) {
+    let element = document.createElement(tag);
+    element.setAttribute("label", label);
+    return element;
+  },
+
+  get_element: function (parent, tag)
+  {
+     let node;
+
+     for (node = parent.firstChild; node; node = node.nextSibling)
+     {
+       if (node.nodeType == 1 && node.nodeName == tag)
+         break;
+     }
+
+     return node;
+  },
+
+  base_domain: function (url) // a string not an nsIURI
+  {
+    if (url.substr(0,6) == "about:")
+      return "";
+
+    let eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"].
+              getService(Components.interfaces.nsIEffectiveTLDService);
+
+    let ioService = Components.classes["@mozilla.org/network/io-service;1"]  
+                            .getService(Components.interfaces.nsIIOService); 
+ 
+    try
+    {
+      if (! /^http.+/.test(url))
+        url = "http://" + url + "/";
+
+      let uri = ioService.newURI(url, null, null);
+      return eTLDService.getBaseDomain(uri);
+    }
+    catch (e)
+    {
+      return url;
+    }
+  },
+
+  // double up single quotes for SQL literals
+  escape1: function (s)
+  {
+    if (s.indexOf("'") < 0)
+      return s;
+
+    let r = "";
+
+    for (let i = 0; i < s.length; ++i)
+    {
+      let c = s.charAt(i);
+
+      r += c;
+
+      if (c == '\'')
+        r += c;
+    }
+
+    return r;
+  },
+
+  // double ' _ and % for SQL LIKE literals
+  escape2: function (s)
+  {
+    let r = "";
+
+    for (let i = 0; i < s.length; ++i)
+    {
+      let c = s.charAt(i);
+
+      r += c;
+
+      if (c == '\'' || c == '_' || c == '%')
+        r += c;
+    }
+
+    return r;
+  },
+
+  round: function (x, m)
+  {
+    x = Math.floor(x * m + 0.5);
+    x = x / m;
+    return x;
+  }
+};
+
+window.addEventListener("load", function() { dashboard.on_load(); }, false); 
+window.addEventListener("unload", function() { dashboard.on_unload(); }, false); 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/dashboard.xul	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,309 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+  -   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+  -
+  - The contents of this file are subject to the Mozilla Public License Version
+  - 1.1 (the "License"); you may not use this file except in compliance with
+  - the License. You may obtain a copy of the License at
+  - http://www.mozilla.org/MPL/
+  - 
+  - Software distributed under the License is distributed on an "AS IS" basis,
+  - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  - for the specific language governing rights and limitations under the
+  - License.
+  -
+  - The Original Code is Dashboard.
+  -
+  - The Initial Developer of the Original Code is
+  - Dave Raggett.
+  - Portions created by the Initial Developer are Copyright (C) 2010
+  - the Initial Developer. All Rights Reserved.
+  -
+  - Contributor(s):
+  -
+  - Alternatively, the contents of this file may be used under the terms of
+  - either the GNU General Public License Version 2 or later (the "GPL"), or
+  - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  - in which case the provisions of the GPL or the LGPL are applicable instead
+  - of those above. If you wish to allow use of your version of this file only
+  - under the terms of either the GPL or the LGPL, and not to allow others to
+  - use your version of this file under the terms of the MPL, indicate your
+  - decision by deleting the provisions above and replace them with the notice
+  - and other provisions required by the GPL or the LGPL. If you do not delete
+  - the provisions above, a recipient may use your version of this file under
+  - the terms of any one of the MPL, the GPL or the LGPL.
+  - 
+  - ***** END LICENSE BLOCK ***** -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css" ?>
+<?xml-stylesheet href="chrome://dashboard-common/skin/dashboard.css" type="text/css"?>
+<?xml-stylesheet href="chrome://dashboard/skin/dashboard.css" type="text/css"?>
+<!DOCTYPE page SYSTEM "chrome://dashboard/locale/dashboard.dtd">
+
+<window id="dashboard" title="&dashboard.dialog.title;"
+    xmlns:html="http://www.w3.org/1999/xhtml"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
+  <script src="dashboard.js" type="application/x-javascript"/>
+  <script src="assess.js" type="application/x-javascript"/>
+  <script src="p3p.js" type="application/x-javascript"/>
+  <stringbundleset id="stringbundleset">
+    <stringbundle id="dashboard-strings"
+     src="chrome://dashboard/locale/dashboard.properties"/>
+  </stringbundleset>
+  <vbox flex="1" class="outer">
+  <tabbox>
+    <tabs id="dashboard-tabs">
+      <tab label="&dashboard.dialog.datatrack.title;"
+       tooltiptext="&dashboard.dialog.datatrack.tooltip;"/>
+      <tab label="&dashboard.dialog.location.title;"
+       tooltiptext="&dashboard.dialog.location.tooltip;"/>
+      <tab label="&dashboard.dialog.current.title;"
+       tooltiptext="&dashboard.dialog.current.tooltip;"/>
+      <tab label="&dashboard.dialog.share.title;"
+       tooltiptext="&dashboard.dialog.share.tooltip;"/>
+      <tab label="&dashboard.dialog.about.title;"
+       tooltiptext="&dashboard.dialog.about.tooltip;"/>
+    </tabs>
+    <tabpanels selectedIndex="2">
+      <tabpanel id="dashboard_datatrack">
+        <vbox flex="1">
+          <hbox>
+          <description>
+          <html:h3>&dashboard.dialog.datatrack.heading;</html:h3>
+          <html:p>&dashboard.dialog.datatrack.descr;</html:p>
+          </description>
+          <splitter/>
+          <vbox flex="2">
+          <label value="&dashboard.dialog.datatrack.select.label;:"
+            tooltiptext="&dashboard.dialog.datatrack.select.tooltip;"/>
+          <menulist id="query_menu" oncommand="dashboard.select_query()">
+          <!-- labels use entities for localization, see dashboard.dtd -->
+          <menupopup id="query_popup" position="after_start">
+            <menuitem value="0" label="&dashboard.dialog.datatrack.item0;"/>
+            <menuitem value="1" label="&dashboard.dialog.datatrack.item1;"/>
+            <menuitem value="2" label="&dashboard.dialog.datatrack.item2;"/>
+            <menuitem value="3" label="&dashboard.dialog.datatrack.item3;"/>
+            <menuitem value="4" label="&dashboard.dialog.datatrack.item4;"/>
+            <menuitem value="5" label="&dashboard.dialog.datatrack.item5;"/>
+            <menuseparator/>
+            <menuitem value="6" label="&dashboard.dialog.datatrack.item6;"/>
+            <menuitem value="7" label="&dashboard.dialog.datatrack.item7;"/>
+            <menuitem value="8" label="&dashboard.dialog.datatrack.item8;"/>
+            <menuitem value="9" label="&dashboard.dialog.datatrack.item9;"/>
+            <menuitem value="10" label="&dashboard.dialog.datatrack.item10;"/>
+            <menuitem value="11" label="&dashboard.dialog.datatrack.item11;"/>
+            <menuitem value="12" label="&dashboard.dialog.datatrack.item12;"/>
+            <menuseparator/>
+            <menuitem value="13" label="&dashboard.dialog.datatrack.item13;"/>
+            <menuitem value="14" label="&dashboard.dialog.datatrack.item14;"/>
+            <menuitem value="15" label="&dashboard.dialog.datatrack.item15;"/>
+            <menuitem value="16" label="&dashboard.dialog.datatrack.item16;"/>
+          </menupopup>
+          </menulist>
+          <label id="query_textbox_label"
+           tooltiptext="&dashboard.dialog.datatrack.input.tooltip;"
+           value="&dashboard.dialog.datatrack.input.label;:" 
+           control="query_textbox"/>
+          <textbox id="query_textbox" flex="1"
+           tooltiptext="&dashboard.dialog.datatrack.input.tooltip;"
+           type="autocomplete" autocompletesearch="history"/>
+          <hbox id="nav">
+          <button id="backward" label="&#9664;" class="inactive"
+           tooltiptext="&dashboard.dialog.datatrack.backward.tooltip;" />
+          <spacer style="width: 0px" />
+          <button id="forward" label="&#9654;" class="inactive"
+           tooltiptext="&dashboard.dialog.datatrack.forward.tooltip;" />
+           <spacer style="width: 0px" />
+          <button  id="exec_query_button" flex="1"
+           label="&dashboard.dialog.datatrack.button.label;"
+           tooltiptext="&dashboard.dialog.datatrack.button.tooltip;"/>
+          </hbox>
+          </vbox>
+          </hbox>
+          <label control="query_tree">&dashboard.dialog.datatrack.table;</label>
+          <tree id="query_tree" rows="18" hidecolumnpicker="true">
+            <treecols><treecol flex="1"/></treecols>
+            <treechildren flex="1"/>
+          </tree>
+        </vbox>
+      </tabpanel>
+      <tabpanel id="dashboard_location" class="show_sites">
+        <vbox>
+        <html:h3>&dashboard.dialog.location.heading;</html:h3>
+        <html:p>&dashboard.dialog.location.descr;<html:br/><html:br/>
+        <radiogroup id="location_mode">
+          <radio id="geosites"
+           label="&dashboard.dialog.location.sites.label;"
+           accesskey="&dashboard.dialog.location.sites.key;"
+           tooltiptext="&dashboard.dialog.location.sites.tooltip;"/>
+          <radio id="geolocation"
+           label="&dashboard.dialog.location.map.label;"
+           accesskey="&dashboard.dialog.location.map.key;"
+           tooltiptext="&dashboard.dialog.location.map.tooltip;"/>
+        </radiogroup>
+        </html:p>
+        <vbox id="site_list"><description>site list</description></vbox>
+        <vbox id="your_location">
+          <html:p id="geoloc"></html:p>
+          <html:img id="map" src=""/>
+        </vbox>
+        </vbox>
+      </tabpanel>
+      <tabpanel id="dashboard_current" class="normal">
+        <vbox flex="1" style="overflow:auto; padding:10px">
+        <html:div id="p3p_policy"/>
+        <html:h3>&dashboard.dialog.current.heading;</html:h3>
+        <vbox  id="site_info">
+        <description>
+        <html:p>&dashboard.dialog.cursite.descr;</html:p>
+        <html:h2 id="curhost">&dashboard.dialog.cursite.unavailable;</html:h2>
+        </description>
+        <hbox id="site_description" class="hidden">
+        <description class="column">
+        <html:p id="website_has">&dashboard.dialog.cursite.site_has;:</html:p>
+        <html:ul id="curstatus"/>
+        </description>
+        <description class="column">
+        <html:p>&dashboard.dialog.cursite.prefs;:</html:p>
+        <vbox id="simple_prefs">
+        <radiogroup id="simple_choice">
+          <radio id="carefree" label="&dashboard.dialog.cursite.simple.carefree.label;"
+           accesskey="&dashboard.dialog.cursite.simple.carefree.key;"
+           tooltiptext="&dashboard.dialog.cursite.simple.carefree.tooltip;"/>
+          <radio id="thoughtful" label="&dashboard.dialog.cursite.simple.thoughtful.label;"
+           accesskey="&dashboard.dialog.cursite.simple.thoughtful.key;"
+           tooltiptext="&dashboard.dialog.cursite.simple.thoughtful.tooltip;"/>
+          <radio id="paranoid" label="&dashboard.dialog.cursite.simple.paranoid.label;"
+           accesskey="&dashboard.dialog.cursite.simple.paranoid.key;"
+           tooltiptext="&dashboard.dialog.cursite.simple.paranoid.tooltip;"/>
+        </radiogroup>
+        <button id="let_me_choose" label="&dashboard.dialog.cursite.button.complicate;"/>
+        </vbox>
+        <vbox id="detailed_prefs">
+        <checkbox id="prefs_never_block"
+          tooltiptext="&dashboard.dialog.cursite.prefs_never_block.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_never_block.label;" checked="false"/>
+
+        <checkbox id="prefs_do_not_track"
+          tooltiptext="&dashboard.dialog.cursite.prefs_do_not_track.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_do_not_track.label;" checked="false"/>
+
+        <checkbox id="prefs_ext_3rd_parties"
+          tooltiptext="&dashboard.dialog.cursite.prefs_ext_3rd_parties.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_ext_3rd_parties.label;" checked="false"/>
+
+        <checkbox id="prefs_ext_3rd_cookies"
+          tooltiptext="&dashboard.dialog.cursite.prefs_ext_3rd_cookies.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_ext_3rd_cookies.label;" checked="false"/>
+
+        <checkbox id="prefs_all_lasting_cookies"
+          tooltiptext="&dashboard.dialog.cursite.prefs_all_lasting_cookies.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_all_lasting_cookies.label;" checked="false"/>
+
+        <checkbox id="prefs_clear_flash_cookies"
+          tooltiptext="&dashboard.dialog.cursite.prefs_clear_flash_cookies.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_clear_flash_cookies.label;" checked="false"/>
+
+        <checkbox id="prefs_block_scripting"
+          tooltiptext="&dashboard.dialog.cursite.prefs_block_scripting.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_block_scripting.label;" checked="false"/>
+
+        <checkbox id="prefs_block_geolocation"
+          tooltiptext="&dashboard.dialog.cursite.prefs_block_geolocation.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_block_geolocation.label;" checked="false"/>
+
+        <checkbox id="prefs_disable_html5_pings"
+          tooltiptext="&dashboard.dialog.cursite.prefs_disable_html5_pings.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_disable_html5_pings.label;" checked="false"/>
+
+        <checkbox id="prefs_disable_referrer"
+          tooltiptext="&dashboard.dialog.cursite.prefs_disable_referrer.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_disable_referrer.label;" checked="false"/>
+
+        <checkbox id="prefs_disable_dom_storage"
+          tooltiptext="&dashboard.dialog.cursite.prefs_disable_dom_storage.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_disable_dom_storage.label;" checked="false"/>
+
+        <checkbox id="prefs_disable_flash"
+          tooltiptext="&dashboard.dialog.cursite.prefs_disable_flash.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_disable_flash.label;" checked="false"/>
+
+        <checkbox id="prefs_disable_java"
+          tooltiptext="&dashboard.dialog.cursite.prefs_disable_java.tooltip;"
+          label="&dashboard.dialog.cursite.prefs_disable_java.label;" checked="false"/>
+
+        <button id="simplify_choices" label="&dashboard.dialog.cursite.button.simplify;"/>
+        <html:p/>
+        <checkbox id="prefs_use_by_default"
+          tooltiptext="&dashboard.dialog.cursite.default.tooltip;"
+          label="&dashboard.dialog.cursite.default.label;" checked="false"/>
+        </vbox>
+        </description>
+        </hbox>
+        <description id="help_positioner">
+        <panel id="context_help_box">
+          <html:p id="context_help_label"/>
+        </panel>
+        </description>
+        <html:hr/>
+        <html:p>&dashboard.dialog.cursite.test;</html:p>
+        <hbox>
+        <button id="exec_norton_button" label="&dashboard.dialog.cursite.test.norton.label;"/>
+        <button id="exec_freetrust_button" label="&dashboard.dialog.cursite.test.freetrust.label;"/>
+        <button id="exec_truste_button" label="&dashboard.dialog.cursite.test.truste.label;"/>
+        </hbox>
+        </vbox>
+        </vbox>
+      </tabpanel>
+      <tabpanel id="dashboard_share">
+        <description style="overflow:auto">
+        <vbox>
+        <html:p><html:img src="chrome://dashboard-common/skin/logo.png"/></html:p>
+        <html:h2 id="dashboard_share_heading">&dashboard.dialog.share.heading;</html:h2>
+        <html:p>&dashboard.dialog.share.p1;</html:p>
+        <hbox>
+        <checkbox id="sharing" label="&dashboard.dialog.share.checkbox;" checked="false"/>
+        <textbox id="share_to" tooltiptext="&dashboard.dialog.share.server.tooltip;"
+        value="http://primelife.eu/dashboard-data"/>
+        </hbox>
+        <html:p>&dashboard.dialog.share.p2;</html:p>
+        </vbox>
+        </description>
+      </tabpanel>
+      <tabpanel id="dashboard_about">
+        <description style="overflow:auto">
+        <html:p><html:img src="chrome://dashboard-common/skin/logo.png"/></html:p>
+        <html:h2 id="dashboard_heading">&dashboard.dialog.about.heading;</html:h2>
+        <html:p>&dashboard.dialog.about.p1;</html:p>
+
+        <html:ul>
+        <html:li style="background-image: url('chrome://dashboard/skin/glasses-cool.png')">websites
+        that take good care of your privacy</html:li>
+        <html:li style="background-image: url('chrome://dashboard/skin/disappointed.png')">websites
+        which collect some information, but lack a machine readable privacy policy</html:li>
+        <html:li style="background-image: url('chrome://dashboard/skin/mad-tongue.png')">websites
+        that enable third parties to track you across the web</html:li>
+        </html:ul>
+
+        <html:p>&dashboard.dialog.about.p2;</html:p>
+
+        <html:p><html:a id="primelife_website">PrimeLife</html:a> &dashboard.dialog.about.p3;</html:p>
+
+        <html:p>&dashboard.dialog.about.p4; <html:a
+        id="dashboard_website">&dashboard.dialog.about.link2;</html:a>.</html:p>
+        <html:address>
+        Dave Raggett, W3C<html:br/>
+        Peter Wolkerstorfer, CURE<html:br/>
+        Copyright &#169; 2010 W3C <html:sup>&#xAE;</html:sup>
+        (MIT, ERCIM, Keio)</html:address>
+        </description>
+      </tabpanel>
+    </tabpanels>
+  </tabbox>
+<!--
+  <statusbar>
+    <statusbarpanel flex="1"/>
+  </statusbar>
+-->
+  </vbox>
+</window>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/database.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,934 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Dashboard.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+// sqlite database for data track
+// together with common queries
+dashboard_overlay.register_database({
+  db: null,  // handle to dashboard database
+  permissions: null, // handle to permissions database
+
+  start_up: function (context)
+  {
+    this.context = context;
+
+    // ensure that only one database connection exists for all windows
+    if (context.window_count == 0)
+    {
+      // open handle to dashboard database
+      var file = Components.classes["@mozilla.org/file/directory_service;1"]  
+                     .getService(Components.interfaces.nsIProperties)  
+                     .get("ProfD", Components.interfaces.nsIFile);
+
+      file.append("dashboard.sqlite");  
+  
+      var storage = Components.classes["@mozilla.org/storage/service;1"]  
+                               .getService(Components.interfaces.mozIStorageService);
+
+      // Creates the file if it does not exist
+      context.dashboard_db = storage.openDatabase(file);
+
+      // also open handle for Firefox's permissions database
+      var file2 = Components.classes["@mozilla.org/file/directory_service;1"]  
+                     .getService(Components.interfaces.nsIProperties)  
+                     .get("ProfD", Components.interfaces.nsIFile);
+
+      file2.append("permissions.sqlite");  
+
+      context.dashboard_permissions = storage.openDatabase(file2);
+
+      // and open handle for Firefox's DOM storage database
+      var file3 = Components.classes["@mozilla.org/file/directory_service;1"]  
+                     .getService(Components.interfaces.nsIProperties)  
+                     .get("ProfD", Components.interfaces.nsIFile);
+
+      file3.append("webappsstore.sqlite");  
+
+      context.dashboard_dom_storage = storage.openDatabase(file3);
+
+      if (!context.dashboard_dom_storage)
+        alert("couldn't open dom storage db");
+
+      // create data track tables if they don't exist
+      this.create_data_track(context.dashboard_db);
+    }
+
+    this.db = context.dashboard_db;
+    this.permissions = context.dashboard_permissions;
+    this.dom_storage = context.dashboard_dom_storage;
+  },
+
+  shut_down: function (context)
+  {
+    // MUST first call finalize() on all remaining
+    // mozIStorageStatement objects.
+    if (context.window_count == 0)
+    {
+      try
+      {
+        this.db.close();
+        this.db = null;
+
+        this.permissions.close();
+        this.permissions = null;
+
+        this.dom_storage.close();
+        this.dom_storage = null;
+     }
+     catch (e) { };
+    }
+  },
+
+  reset: function (all)
+  {
+    this.drop_data_track(this.db, all);
+    this.create_data_track(this.db);
+  },
+
+  // poll Firefox private browsing mode
+  // we use this to suppress logging
+  private_mode: function ()
+  {
+    var pbs = Components.classes["@mozilla.org/privatebrowsing;1"]  
+               .getService(Components.interfaces.nsIPrivateBrowsingService);  
+    return pbs.privateBrowsingEnabled; 
+  },
+
+  sync_command: function (sql, sql2)
+  {
+    var statement = this.db.createStatement(sql);
+    var transaction = false;
+
+    if (this.db.transactionInProgress)
+    {
+      transaction = true;
+      this.db.beginTransactionAs(this.db.TRANSACTION_DEFERRED);
+    }
+
+    try 
+    {
+      try
+      {
+        statement.execute();
+      }
+      finally
+      {
+        statement.reset();
+
+        if (statement.finalize)
+          statement.finalize();
+      }
+    }
+    catch (e)
+    { 
+      if (sql2)
+        dashboard_overlay.database.sync_command(sql2);
+    }
+
+    if (transaction)
+      this.db.commitTransaction();
+  },
+
+  // database command that doesn't involve generating
+  // results, uses a synchronous method for Firefox < 3.5
+  // and an asynch method for Firefox 3.5 and later
+  command: function (sql, sql2)
+  {
+    try {
+    var statement = this.db.createStatement(sql);
+    } catch (e) { alert(sql + "\n\n" + e); return; }
+
+    if (typeof statement.executeAsync != "undefined")
+    {
+
+      statement.executeAsync({
+        // no results, so this is a no-op
+        handleResult: function(aResultSet) 
+        {
+        },
+
+        handleError: function(aError)
+        {
+          // alert("Error: " + aError.message + "\n"+sql);
+        },
+
+        // called after last row has been passed to handleResult
+        handleCompletion: function(aReason)
+        {
+          if (aReason != Components.interfaces.mozIStorageStatementCallback.REASON_FINISHED)
+          {
+            if (sql2)
+              dashboard_overlay.database.command(sql2);
+            else
+              alert("Query canceled or aborted! " + sql +
+                    ", " + dashboard_overlay.database.db.lastErrorString);
+          }
+        }
+      });
+    }
+    else // synchronous execution of sql
+    {
+      var transaction = false;
+
+      if (this.db.transactionInProgress)
+      {
+        transaction = true;
+        this.db.beginTransactionAs(this.db.TRANSACTION_DEFERRED);
+      }
+
+      try 
+      {
+        try
+        {
+          statement.execute();
+        }
+        finally
+        {
+          statement.reset();
+
+          if (statement.finalize)
+            statement.finalize();
+        }
+      }
+      catch (e)
+      { 
+        if (sql2)
+          dashboard_overlay.database.command(sql2);
+      }
+
+      if (transaction)
+        this.db.commitTransaction();
+    }
+  },
+
+  // database SELECT command that generates results
+  // this uses a synchronous method for Firefox < 3.5
+  // use asynchronous method for later versions
+  select: function (sql, db, handler)
+    {
+      let i, k, statement, transaction = false;
+
+      if (this.db == null)
+        this.start_up();
+
+      if (!db)
+        db = this.db;
+
+      try 
+      {
+        statement = db.createStatement(sql);
+      }
+      catch (e)
+      {
+        if (db != this.dom_storage)
+          alert("Couldn't execute: " + sql);
+        return;
+      }
+
+      if (typeof statement.executeAsync != "undefined")
+      {
+        statement.executeAsync({
+          // called multiple times with one or more rows each time
+          handleResult: function(aResultSet)
+          {
+            for (let row = aResultSet.getNextRow(); row; row = aResultSet.getNextRow())
+              handler.process_row(row);
+          },
+
+          handleError: function(aError) 
+          {
+            statement.reset();
+
+            if (statement.finalize)
+              statement.finalize();
+
+            alert("Error: " + aError.message);
+          },
+
+          // called after last row has been passed to handleResult
+          handleCompletion: function(aReason)
+          {
+            statement.reset();
+
+            if (statement.finalize)
+              statement.finalize();
+
+            handler.finalize();
+
+            if (aReason != Components.interfaces.mozIStorageStatementCallback.REASON_FINISHED)
+              alert("Query canceled or aborted! " + 
+                         dashboard_overlay.database.db.lastErrorString);
+          }
+        });
+      }
+      else // use synchronous query
+      {
+        if (db.transactionInProgress)
+        {
+          transaction = true;
+          db.beginTransactionAs(db.TRANSACTION_DEFERRED);
+        }
+
+        try
+        {
+          while (statement.executeStep())
+          {
+            var row = { "data": {} };
+
+            // mimic asynchronous interface for column values
+            row.getResultByName = function (name)
+            {
+              return row.datum[name];
+            }
+
+            for (i = 0, k = statement.columnCount; i < k; ++i)
+              row.data[statement.getColumnName(i)] = statement.getUTF8String(i);
+
+            handler.process_row(row);
+          }
+        }
+        finally
+        {
+          statement.reset();
+          statement.close();
+        }
+
+        if (transaction)
+          db.commitTransaction();
+
+        handler.finalize();
+      }
+    },
+
+  // used to create the sqlite tables for the data track
+  // this is done synchronously as it should be fast given
+  // that the tables will normally already exist
+  create_data_track: function (db)
+  {
+    // direct resource to resource relation
+    db.executeSimpleSQL(
+      "CREATE TABLE IF NOT EXISTS relations (" +
+          "parent TEXT," +
+          "child TEXT," +
+          "offsite INTEGER," +
+          "time DATE," +
+          "PRIMARY KEY (parent, child)" +
+      ")");
+
+    db.executeSimpleSQL(
+      "CREATE INDEX IF NOT EXISTS relations_parent_index ON relations (parent)");
+
+    // could replace this by DEFAULT CURRENT_TIMESTAMP on time column
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS insert_relations_time AFTER" +
+      " INSERT ON relations\n" +
+      "BEGIN\n" +
+      " UPDATE relations SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    // needed since DEFAULT only effects INSERT not UPDATE
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS update_relations_time AFTER" +
+      " UPDATE ON relations\n" +
+      "BEGIN\n" +
+      " UPDATE relations SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    // page to direct and indirect 3rd parties
+    db.executeSimpleSQL(
+      "CREATE TABLE IF NOT EXISTS parties (" +
+          "page_host TEXT," +
+          "third_party TEXT," +
+          "offsite INTEGER," +
+          "time DATE," +
+          "PRIMARY KEY (page_host, third_party)" +
+      ")");
+
+    db.executeSimpleSQL(
+      "CREATE INDEX IF NOT EXISTS parties_page_host_index ON parties (page_host)");
+
+    db.executeSimpleSQL(
+      "CREATE INDEX IF NOT EXISTS parties_third_party_index ON parties (third_party)");
+
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS insert_parties_time AFTER" +
+      " INSERT ON parties\n" +
+      "BEGIN\n" +
+      " UPDATE parties SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS update_parties_time AFTER" +
+      " UPDATE ON parties\n" +
+      "BEGIN\n" +
+      " UPDATE parties SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    // tracks data POSTed via HTTP
+    // redundant copies of domain and date for efficient
+    // queries on who you gave what data to and when
+    db.executeSimpleSQL(
+      "CREATE TABLE IF NOT EXISTS http_data (" +
+          "name TEXT," +
+          "value TEXT," +
+          "host TEXT," +
+          "posted INTEGER," +
+          "form INTEGER," +
+          "time TEXT" +
+      ")");
+
+    db.executeSimpleSQL(
+      "CREATE INDEX IF NOT EXISTS http_data_host_index ON http_data (host)");
+
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS insert_http_data_time AFTER" +
+      " INSERT ON http_data\n" +
+      "BEGIN\n" +
+      " UPDATE http_data SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS update_http_data_time AFTER" +
+      " UPDATE ON http_data\n" +
+      "BEGIN\n" +
+      " UPDATE http_data SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    db.executeSimpleSQL(
+      "CREATE TABLE IF NOT EXISTS site_info (" +
+          "host TEXT PRIMARY KEY," +
+          "visited INTEGER," +
+          "prefs INTEGER," +
+          "session_cookies INTEGER," +
+          "lasting_cookies INTEGER," +
+          "flash_cookies INTEGER," +
+          "int_3rd_parties INTEGER," +
+          "ext_3rd_parties INTEGER," +
+          "int_3rd_party_session_cookies INTEGER," +
+          "int_3rd_party_lasting_cookies INTEGER," +
+          "int_3rd_party_flash_cookies INTEGER," +
+          "ext_3rd_party_session_cookies INTEGER," +
+          "ext_3rd_party_lasting_cookies INTEGER," +
+          "ext_3rd_party_flash_cookies INTEGER," +
+          "dom_storage INTEGER," +
+          "html5_pings INTEGER," +
+          "invisible_images INTEGER," +
+          "suspicious_urls INTEGER," +
+          "geo_permission INTEGER," +
+          "p3p INTEGER," +
+          "time DATE" +
+      ")");
+
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS insert_site_info_time AFTER" +
+      " INSERT ON site_info\n" +
+      "BEGIN\n" +
+      " UPDATE parties SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+
+    db.executeSimpleSQL(
+      "CREATE TRIGGER IF NOT EXISTS update_site_info_time AFTER" +
+      " UPDATE OF visited ON site_info\n" +
+      "BEGIN\n" +
+      " UPDATE site_info SET time = DATETIME('NOW')\n" + 
+      " WHERE rowid = new.rowid;\n" +
+      "END");
+  },
+
+  drop_data_track: function (db, all)
+  {
+    db.executeSimpleSQL("DROP TABLE IF EXISTS http_data");
+    db.executeSimpleSQL("DROP TABLE IF EXISTS parties");
+    db.executeSimpleSQL("DROP TABLE IF EXISTS relations");
+
+    // keep site_info to retain user's site preferences?
+
+    if (all)
+      db.executeSimpleSQL("DROP TABLE IF EXISTS site_info");
+  },
+
+  iso8061: function (date)
+  {
+     if (!date)
+       date = new Date();
+
+     return date.getFullYear() + "-" + (1 + date.getMonth()) + "-" + date.getDate()
+       + " " + date.getHours() + ":" + date.getMinutes() + ":" +date.getSeconds();
+  },
+
+  log_3rd_parties: function (status)
+  {
+    let private_mode = this.private_mode();
+    let count = 0;
+    let page_host = status.page_host;
+    let page_domain = status.page_domain;
+    let parties = status.parties;
+
+    if (!private_mode)
+    {
+      for (party in parties)
+      {
+        ++count;
+        let sql = "INSERT OR REPLACE INTO parties " +
+                  "(page_host, third_party, offsite) " +
+                  "VALUES(" +
+                  this.quote(page_host) + "," +
+                  this.quote("" + party) + "," +
+                  parties[party] +
+                  ")";
+
+        this.command(sql);
+      }
+    }
+
+    status.parties = {}; // reclaim memory
+  },
+
+  // called by http observer on page load or timer
+  // may want to switch to execAsynch on statement
+  // which allows you to set error handler+continuation
+  // doc.baseURIObject is page's nsURI object and
+  // is used to identify 3rd party requests
+  // note this has been greatly trimmed down and
+  // could easily be expanded to log more details
+  log_http: function (requests)
+  {
+    let private_mode = this.private_mode();
+    var i, j, request, sql, param;
+
+    for (i = 0; i < requests.length; ++i)
+    {
+      request = requests[i];
+
+      // want to reduce redundant records
+      if (request.ref_host && request.host != request.ref_host)
+      {
+        sql = "INSERT OR REPLACE INTO relations " +
+              "(parent, child, offsite) " +
+              "VALUES(" +
+              this.quote(request.ref_host) + "," +
+              this.quote(request.host) + "," +
+              request.offsite +
+              ")";
+
+        if (!private_mode && request.page_host)
+          this.command(sql);
+      }
+
+      for (j = 0; j < request.query_params.length; ++j)
+      {
+        param = request.query_params[j];
+
+        sql = "INSERT INTO http_data " +
+              "(name, value, host, posted, form) " +
+              "VALUES(" +
+              this.quote(param.name) + "," +
+              this.quote(param.value) + "," +
+              this.quote(request.host) + "," +
+              0 + "," +      // from post?
+              param.form +   // from form?
+              ")";
+
+        if (!private_mode)
+          this.command(sql);
+      }
+
+      for (j = 0; j < request.post_params.length; ++j)
+      {
+        param = request.post_params[j];
+
+        if (param)
+        {
+          sql = "INSERT INTO http_data " +
+              "(name, value, host, posted, form) " +
+              "VALUES(" +
+              this.quote(param.name) + "," +
+              this.quote(param.value) + "," +
+              this.quote(request.host) + "," +
+              1 + "," +      // from post?
+              param.form +   // from form?
+              ")";
+
+          if (!private_mode)
+            this.command(sql);
+        }
+      }
+    }
+  },
+
+  // update site info for later sharing with others
+  // omits p3p which is saved asynchronously due to
+  // time involved in searching web for the policy
+  // sets feature counts to max(old, new)
+  update_site_info: function (host, status)
+  {
+    var sql = "SELECT " +
+       "session_cookies,lasting_cookies,flash_cookies," +
+       "int_3rd_parties,ext_3rd_parties," +
+       "int_3rd_party_session_cookies,int_3rd_party_lasting_cookies," +
+       "int_3rd_party_flash_cookies,ext_3rd_party_session_cookies," +
+       "ext_3rd_party_lasting_cookies,ext_3rd_party_flash_cookies," +
+       "dom_storage,html5_pings,invisible_images," +
+       "suspicious_urls,geo_permission" + " " +
+       "FROM site_info WHERE host=" + this.quote(host);
+
+    let found = false;
+    let database = this;
+
+    let handler = 
+    {
+      set_max: function (row, col)
+      {
+        let n = row.getResultByName(col);
+        let m = status[col];
+
+        if (n > m)
+          status[col] = n;
+      },
+
+      process_row: function (row)
+      {
+        let set = this.set_max;
+
+        set(row, "session_cookies");
+        set(row, "lasting_cookies");
+        set(row, "flash_cookies");
+        set(row, "int_3rd_parties");
+        set(row, "ext_3rd_parties");
+        set(row, "int_3rd_party_session_cookies");
+        set(row, "int_3rd_party_lasting_cookies");
+        set(row, "int_3rd_party_flash_cookies");
+        set(row, "ext_3rd_party_session_cookies");
+        set(row, "ext_3rd_party_lasting_cookies");
+        set(row, "ext_3rd_party_flash_cookies");
+        set(row, "dom_storage");
+        set(row, "html5_pings");
+        set(row, "invisible_images");
+        set(row, "suspicious_urls");
+        set(row, "geo_permission");
+
+        let sql = "UPDATE site_info SET " +
+          "session_cookies=" + status.session_cookies + "," +
+          "lasting_cookies=" + status.lasting_cookies + "," +
+          "flash_cookies=" + status.flash_cookies + "," +
+          "int_3rd_parties=" + status.int_3rd_parties + "," +
+          "ext_3rd_parties=" + status.ext_3rd_parties + "," +
+          "int_3rd_party_session_cookies=" + status.int_3rd_party_session_cookies + "," +
+          "int_3rd_party_lasting_cookies=" + status.int_3rd_party_lasting_cookies + "," +
+          "int_3rd_party_flash_cookies=" + status.int_3rd_party_flash_cookies + "," +
+          "ext_3rd_party_session_cookies=" + status.ext_3rd_party_session_cookies + "," +
+          "ext_3rd_party_lasting_cookies=" + status.ext_3rd_party_lasting_cookies + "," +
+          "ext_3rd_party_flash_cookies=" + status.ext_3rd_party_flash_cookies + "," +
+          "dom_storage=" + status.dom_storage + "," +
+          "html5_pings=" + status.html5_pings + "," +
+          "invisible_images=" + status.invisible_images + "," +
+          "suspicious_urls=" + status.suspicious_urls + "," +
+          "geo_permission= " + status.geo_permission + " " +
+          "WHERE host=" + database.quote(host);
+
+        database.command(sql);
+        found = true;
+      },
+
+      // called after all rows of query have been delivered
+      finalize: function ()
+      {
+        if (!found)
+        {
+          let sql = "INSERT INTO site_info (" +
+            "host,visited,prefs,session_cookies,lasting_cookies," +
+            "flash_cookies,int_3rd_parties,ext_3rd_parties," +
+            "int_3rd_party_session_cookies,int_3rd_party_lasting_cookies," +
+            "int_3rd_party_flash_cookies,ext_3rd_party_session_cookies," +
+            "ext_3rd_party_lasting_cookies,ext_3rd_party_flash_cookies," +
+            "dom_storage, html5_pings,invisible_images," +
+            "suspicious_urls,geo_permission" + 
+          ") VALUES(" +
+            database.quote(host) + "," +
+            1+ "," +
+            status.prefs + "," +
+            status.session_cookies + "," +
+            status.lasting_cookies + "," +
+            status.flash_cookies + "," +
+            status.int_3rd_parties + "," +
+            status.ext_3rd_parties + "," +
+            status.int_3rd_party_session_cookies + "," +
+            status.int_3rd_party_lasting_cookies + "," +
+            status.int_3rd_party_flash_cookies + "," +
+            status.ext_3rd_party_session_cookies + "," +
+            status.ext_3rd_party_lasting_cookies + "," +
+            status.ext_3rd_party_flash_cookies + "," +
+            status.dom_storage + "," +
+            status.html5_pings + "," +
+            status.invisible_images + "," +
+            status.suspicious_urls + "," +
+            status.geo_permission +
+          ")";
+
+          database.command(sql);
+        }
+      }
+    };
+
+    database.select(sql, this.db, handler);
+  },
+
+  // update or insert record for given host
+  // that only changes the given column name
+  // sqlite lacks insert or update on duplicate key
+  update_or_insert: function (host, property, value, sync)
+  {
+      // fails if record already present for this host
+      var sql1 = "INSERT INTO site_info (host,"+
+                property + ") VALUES(" +
+                this.quote(host) + "," +
+                value + 
+                ")";
+
+      // whereupon we execute the update statement
+      var sql2 = "UPDATE site_info SET " +
+                property + "=" + value +
+                " WHERE host=" + this.quote(host);
+
+      if (!this.private_mode())
+      {
+        if (sync)
+        {
+          this.sync_command(sql1, sql2);
+        }
+        else
+        {
+          this.command(sql1, sql2);
+        }
+      }
+  },
+
+  p3p_log: function (host, req)
+  {
+    var p3p = (req && req.responseXML) ? 1 : 0;
+
+    if (!this.private_mode())
+      this.update_or_insert(host, "p3p", p3p);
+  },
+
+/*
+  // find all session cookies with given name
+  // and check for match on http server host
+  // e.g. .lovefilm.com cf www.lovefilm.com
+  session_cookie: function (host, name) {
+    var host_len = host.length;
+
+    var sql = "SELECT host FROM cookies WHERE name = '" +
+              name + "' AND session <> 0";
+
+    var dataset = this.sync_get(sql, this.db);
+
+    for (var i = 0; dataset && i < dataset.length; ++i)
+    {
+      pattern = dataset[0]["host"];
+      var len = pattern.length;
+
+      if (len <= hostlen)
+      {
+        var hs = host.substring(hostlen - len);
+
+        if (hs == pattern)
+          return true;
+      }
+    }
+
+    return false;
+  },
+
+  // find all persistent cookies with given name
+  // and check for match on http server host
+  // e.g. .lovefilm.com cf www.lovefilm.com
+  persistent_cookie: function (host, name) {
+    var host_len = host.length;
+
+    var sql = "SELECT host FROM cookies WHERE name = '" +
+              name + "' AND session = 0";
+
+    var dataset = this.sync_get(sql, this.db);
+
+    for (var i = 0; dataset && i < dataset.length; ++i)
+    {
+      pattern = dataset[0]["host"];
+      var len = pattern.length;
+
+      if (len <= hostlen)
+      {
+        var hs = host.substring(hostlen - len);
+
+        if (hs == pattern)
+          return true;
+      }
+    }
+
+    return false;
+  },
+*/
+  // update site preferences where prefs is an integer value
+  // encoded as a bit vector
+  set_prefs: function (host, prefs)
+  {
+    this.update_or_insert(host, "prefs", prefs);
+
+    // and synchronise session cache
+    dashboard_overlay.sync_prefs(host, prefs);
+  },
+
+  // synchronous query returning bit vector of site preferences
+  get_prefs: function (host, db)
+  {
+    var prefs;
+
+    var sql = "SELECT prefs FROM site_info WHERE host = '" +
+              host + "'";
+
+    var dataset = this.sync_get(sql, db);
+
+    if (dataset && dataset.length > 0)
+      prefs = dataset[0]["prefs"];
+
+    return prefs;
+  },
+
+  // fast synchronous read and write
+  mark_visited: function (browser)
+  {
+    let status = browser.dashboard_status;
+    let visited = 0;
+
+    try
+    {
+      let host = status.page_host;
+
+      if (host)
+      {
+        var sql = "SELECT visited FROM site_info WHERE host = '" +
+                  host + "'";
+
+        var dataset = this.sync_get(sql, this.db);
+
+        if (dataset && dataset.length > 0)
+          visited = dataset[0]["visited"];
+
+       this.update_or_insert(status.page_host, "visited", ++visited, true);
+      }
+    }
+    catch (e) { }
+
+    status.visited = visited;
+  },
+
+  extract_host: function (referrer)
+  {
+    var page_host = null;
+    var i;
+
+    if (referrer && (i = referrer.indexOf("://")) > 0)
+    {
+      page_host = referrer.substr(i+3);
+
+      i = page_host.indexOf("/");
+      page_host = page_host.substr(0, i);
+    }
+
+    return page_host;
+  },
+
+  sync_get: function (sql, db)
+  {
+    var dataset;
+    var transaction = false;
+    var statement = db.createStatement(sql);  // throws exception on error
+
+    if (db.transactionInProgress)
+    {
+      transaction = true;
+      db.beginTransactionAs(db.TRANSACTION_DEFERRED);
+    }
+
+    try
+    {
+      var dataset = [];
+
+     while (statement.executeStep())
+     {
+       var row = [];
+
+       for (var i = 0, k = statement.columnCount; i < k; ++i)
+         row[statement.getColumnName(i)] = statement.getUTF8String(i);
+
+       dataset.push(row);
+     }
+    }
+    finally
+    {
+      statement.reset();
+      //statement.close();
+    }
+
+    if (transaction)
+      db.commitTransaction();
+
+    return dataset;
+  },
+
+  quote: function (s)
+  {
+     if (s)
+     {
+       s = s.replace(/\'/g, "''");
+       return "'" + s + "'";
+     }
+
+     return "null";
+  },
+
+  bool: function (v)
+  {
+     return (v ? "1" : "0");
+  }
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/misc.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,778 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Dashboard.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+dashboard_overlay.register_misc({
+  purge_flash_cookies: function ()
+  {
+    let handler = 
+    {
+      hosts: {},
+      count: 0,
+
+      process_row: function (row)
+      {
+        var host = row.getResultByName("host");
+        this.hosts[host] = host;
+        this.count++;
+      },
+
+      // ready to clear flash shared objects
+      finalize: function ()
+      {
+        dashboard_overlay.misc.delete_flash_cookies(this.hosts);
+        this.hosts = null;
+      }
+    };
+
+    // get a list of hosts for which we should clear flash cookies
+    handler.misc = this;
+    let database = dashboard_overlay.database;
+    let sql = "SELECT host FROM site_info WHERE (prefs & 6)";
+
+    database.select(sql, database.db, handler);
+  },
+
+  delete_flash_cookies: function (hosts)
+  {
+    if (!this.flash_dir)
+      this.flash_dir = this.get_flash_dir();
+
+    // append /Flash_Player/macromedia.com/support/flashplayer/sys
+    let dir = this.flash_dir.clone();
+
+    dir.append("Flash_Player");
+    dir.append("macromedia.com");
+    dir.append("support");
+    dir.append("flashplayer");
+    dir.append("sys");
+
+    let folder = Components.classes["@mozilla.org/file/local;1"].  
+                     createInstance(Components.interfaces.nsILocalFile);
+
+    folder.initWithPath(dir.path);
+    let flash_hosts = this.delete_hosts(folder, hosts);
+
+    // append /Flash_Player/#SharedObjects
+    dir = this.flash_dir.clone();
+    dir.append("Flash_Player");
+    dir.append("#SharedObjects");
+
+    folder = Components.classes["@mozilla.org/file/local;1"].  
+                     createInstance(Components.interfaces.nsILocalFile);
+
+    folder.initWithPath(dir.path);
+
+    // #SharedObjects contains named folder within which
+    // reside the host specific folders we want to delete
+
+    if (folder.exists() && folder.isDirectory() && folder.isReadable())
+    {
+       let content = folder.directoryEntries;
+
+       while (content.hasMoreElements())
+       {
+         var file = content.getNext();
+         file.QueryInterface(Components.interfaces.nsILocalFile);
+
+         if (file.isDirectory())
+           this.delete_hosts(file, hosts);
+       }
+    }
+  },
+
+  delete_hosts: function (folder, hosts)
+  {
+     let content = folder.directoryEntries;
+     let file = content.hasMoreElements() ? content.getNext() : null;
+
+     while (file)
+     {
+       let next = content.hasMoreElements() ? content.getNext() : null;
+
+       file.QueryInterface(Components.interfaces.nsILocalFile);
+
+       let name = file.leafName;
+
+       if (name.charAt(0) == "#")
+         name = name.substr(1);
+
+       if (hosts[name])
+         file.remove(true);
+
+       file = next;
+     }
+  },
+
+  scan_flash_storage: function ()
+  {
+    if (!this.flash_dir)
+      this.flash_dir = this.get_flash_dir();
+
+    // append /Flash_Player/macromedia.com/support/flashplayer/sys
+    let dir = this.flash_dir.clone();
+
+    dir.append("Flash_Player");
+    dir.append("macromedia.com");
+    dir.append("support");
+    dir.append("flashplayer");
+    dir.append("sys");
+
+    let folder = Components.classes["@mozilla.org/file/local;1"].  
+                     createInstance(Components.interfaces.nsILocalFile);
+
+    folder.initWithPath(dir.path);
+    let flash_hosts = this.collect_hosts(folder, []);
+
+    // append /Flash_Player/#SharedObjects
+    dir = this.flash_dir.clone();
+    dir.append("Flash_Player");
+    dir.append("#SharedObjects");
+
+    folder = Components.classes["@mozilla.org/file/local;1"].  
+                     createInstance(Components.interfaces.nsILocalFile);
+
+    folder.initWithPath(dir.path);
+
+    if (folder.exists() && folder.isDirectory() && folder.isReadable())
+    {
+       let content = folder.directoryEntries;
+       let file;
+
+       while (content.hasMoreElements())
+       {
+         file = content.getNext();
+         file.QueryInterface(Components.interfaces.nsILocalFile);
+
+         if (file.isDirectory())
+           this.collect_hosts(file, flash_hosts);
+       }
+    }
+
+    return flash_hosts;
+  },
+
+  collect_hosts: function (folder, hosts)
+  {
+    if (folder.exists() && folder.isDirectory() && folder.isReadable())
+    {
+       let content = folder.directoryEntries;
+       let file, name, size, date, files;
+
+       while (content.hasMoreElements())
+       {
+         file = content.getNext();
+         file.QueryInterface(Components.interfaces.nsILocalFile);
+
+         if (file.isDirectory())
+         {
+           name = file.leafName;
+
+           if (name.charAt(0) == "#")
+             name = name.substr(1);
+
+           files = hosts[name];
+
+           if (files)
+             hosts[name] = hosts[name].concat(this.collect_files(file, []));
+           else
+             hosts[name] = this.collect_files(file, []);
+         }
+       }
+    }
+
+    return hosts;
+  },
+
+  // dir is an nsILocalFile enumeration, collection is
+  // an array, scan nested directories for leaf files and
+  // collect their names, sizes and dates last modified
+  collect_files: function (dir, collection)
+  {
+    var content = dir.directoryEntries, file, lmd;
+
+    while (content.hasMoreElements())
+    {
+      file = content.getNext();
+      file.QueryInterface(Components.interfaces.nsILocalFile);
+
+      if (file.isFile())
+      {
+        lmd = dashboard_overlay.database.iso8061(new Date(file.lastModifiedTime));
+        collection.push({"name": file.leafName,
+                         "size": file.fileSize,
+                         "date": lmd});
+      }
+      else if (file.isDirectory())
+        collection = this.collect_files(file, collection);
+    }
+    return collection;
+  },
+
+  // this only looks at common locations
+  // and doesn't search rarer alternatives
+  get_flash_dir: function ()
+  {
+    let flash_dir = null;
+
+    // Windows
+    try 
+    {
+      flash_dir = this.get_root_dir("AppData");
+      flash_dir.append("Roaming");
+      flash_dir.append("Macromedia");
+
+      if (flash_dir.exists() && flash_dir.isDirectory())
+        return flash_dir;
+    }
+    catch (e)
+    {
+      // alert("couldn't find windows flash dir ");
+    }
+
+    try
+    {
+      flash_dir = this.get_root_dir("AppData");
+      flash_dir.append("Macromedia");
+
+      if (flash_dir.exists() && flash_dir.isDirectory())
+        return flash_dir;
+    }
+    catch (e)
+    {
+      // alert("couldn't find windows flash dir ");
+    }
+
+    // Apple
+    try
+    {
+      flash_dir = this.get_root_dir("ULibDir");
+      flash_dir.append("Preferences");
+      flash_dir.append("Macromedia");
+
+      if (flash_dir.exists() && flash_dir.isDirectory())
+        return flash_dir;
+    }
+    catch (e)
+    {
+      // alert("couldn't find mac1 flash dir ");
+    }
+
+    try
+    {
+      flash_dir = this.get_root_dir("UsrPrfs");
+      flash_dir.append("Macromedia");
+
+      if (flash_dir.exists() && flash_dir.isDirectory())
+        return flash_dir;
+    }
+    catch (e)
+    {
+      // alert("couldn't find mac2 flash dir ");
+    }
+
+    // Linux
+    try
+    {
+      flash_dir = this.get_root_dir("Home");
+      flash_dir.append(".macromedia");
+
+      if (flash_dir.exists() && flash_dir.isDirectory())
+        return flash_dir;
+    }
+    catch (e)
+    {
+      // alert("couldn't find linux flash dir ");
+    }
+
+    alert("couldn't find flash storage directory");
+    return null;
+  },
+
+  get_root_dir: function (folder)
+  {
+    return Components.classes["@mozilla.org/file/directory_service;1"].
+             getService(Components.interfaces.nsIProperties).
+             get(folder, Components.interfaces.nsILocalFile);
+  },
+
+  html5_ping: function (browser)
+  {
+    let count = 0;
+    let doc = browser.contentDocument;
+    let links = doc.getElementsByTagName("a");
+    let areas = doc.getElementsByTagName("a");
+    let status = browser.dashboard_status;
+
+    for (let i = 0; i < links.length; ++i)
+    {
+      if (links[i].getAttribute("ping"))
+        ++count;
+    }
+
+    for (let i = 0; i < areas.length; ++i)
+    {
+      if (areas[i].getAttribute("ping"))
+        ++count;
+    }
+
+    status.html5_pings = count;
+  },
+
+  // *** TO DO add check for compact policy in HTTP
+  // responses as part of HTTP observer code ***
+  get_p3p_policy: function (host, url, handler)
+  {
+    let req = new XMLHttpRequest();
+
+    if (!url)
+      url = "http://"+host+"/w3c/p3p.xml";
+
+    req.open("GET", url, true);
+    req.onreadystatechange = function (evt)
+    {
+
+      if (req.readyState == 4)
+      {
+        if (req.status == 200)
+          handler(host, req);
+        else if (req.status == 404)  // look for P3P header
+        {
+          let p3p = req.getResponseHeader("P3P");
+
+          if (p3p)
+          {
+            let i = p3p.indexOf("policyref=");
+
+            if (i >= 0)
+            {
+              let c = p3p.charAt(10);
+              p3p = p3p.substr(11);
+              i = p3p.indexOf(c);
+              p3p = p3p.substring(0, i);
+              return dashboard_overlay.misc.get_p3p_policy(host, p3p, handler);
+            }
+          }
+
+          handler(host, null);
+        }
+        else
+          handler(host, null);
+      }
+    };
+
+    req.send(null);
+  },
+
+  // 3rd parties covers direct and indirect resources
+  // p3p only covers current site ditto for geo, ping, dom storage
+  // cookies covers current site and 3rd parties
+  rate_site: function (browser)
+  {
+    var uri = browser.currentURI;
+    var status = browser.dashboard_status;
+
+    // status is null for about:* pages
+    var host = (status ? status.page_host : null);
+    
+    if (host)
+    {
+      this.query_3rd_parties(browser, host);
+      this.query_p3p(browser, host);
+      this.query_geo(browser, host);
+      this.html5_ping(browser);
+      this.query_dom_storage(browser);
+
+      // allow page scripts to do their evil work before check
+      setTimeout( function ()
+      {
+        dashboard_overlay.misc.spot_web_bugs(browser);
+      }, 10 );
+    }
+    else // treat non-http pages as cool
+    {
+      let primelife_button = document.getElementById("dashboard-toolbar-button");
+      primelife_button.style.listStyleImage = 
+             'url("chrome://dashboard-common/skin/glasses-cool.png")';
+    }
+
+    let context = dashboard_overlay.context;
+    let dialog = context.dashboard_dialog;
+
+    // check if this is the current browser to avoid changing dashboard if it's not
+    if (dialog && dialog.dashboard && browser == dashboard_overlay.current_browser())
+    {
+      dialog.dashboard_overlay = dashboard_overlay;
+      var update = dialog.dashboard.update_current_site;
+
+      // some of the above database operations are asynchronous
+      // and need time to complete before we display the results
+      // query p3p has its own means to refresh dashboard display
+      // since it involves external network traffic for policies
+      // 200mS should be plenty for SQLite database operations.
+      setTimeout(function ()
+      {
+        if (status)
+          update(status);
+      }, 200);
+    }
+  },
+
+  check_rating: function (browser)
+  {
+    let level = 0;
+    let status = browser.dashboard_status;
+    let host = status.page_host;
+
+    if (status.ext_3rd_party_lasting_cookies || status.ext_3rd_party_flash_cookies)
+      level = 2;
+    else if ((!status.p3p) &&
+       (status.lasting_cookies || status.flash_cookies || status.ext_3rd_parties))
+      level = 1;
+
+    dashboard_overlay.privacy_level = level;
+  
+    let primelife_button = document.getElementById("dashboard-toolbar-button");
+
+    if (level == 2)
+      primelife_button.style.listStyleImage =
+                   'url("chrome://dashboard-common/skin/mad-tongue.png")';
+    else if (level == 1)
+      primelife_button.style.listStyleImage = 
+                   'url("chrome://dashboard-common/skin/disappointed.png")';
+    else
+      primelife_button.style.listStyleImage =
+                   'url("chrome://dashboard-common/skin/glasses-cool.png")';
+
+    if (level > 0 && status.visited < 2 && !status.notified)
+    {
+      status.notified = true;
+      dashboard_overlay.show_notification_bar(host, level);
+    }
+
+    let singleton_window =
+        Components.classes["@mozilla.org/appshell/appShellService;1"].
+          getService(Components.interfaces.nsIAppShellService).hiddenDOMWindow;
+
+    singleton_window.dashboard_icon = primelife_button.style.listStyleImage;
+  },
+
+  // query for list of 3rd parties for this site
+  query_3rd_parties: function (browser, host)
+  {
+    let database = dashboard_overlay.database;
+    let query = "SELECT DISTINCT third_party, offsite FROM  parties " +
+                "WHERE page_host = " + database.quote(host);
+
+    let handler = 
+    {
+      browser: browser,
+      parties: [],
+      offsite: [],
+
+      process_row: function (row)
+      {
+        this.parties.push(row.getResultByName("third_party"));
+        this.offsite.push(row.getResultByName("offsite"));        
+      },
+
+      finalize: function () 
+      {
+        dashboard_overlay.misc.rate_cookies(browser, this.parties, this.offsite);
+      }
+    };
+
+    database.select(query, database.db, handler);
+  },
+
+  query_dom_storage: function (browser)
+  {
+    let database = dashboard_overlay.database;
+    let status = browser.dashboard_status;
+    let uri = browser.currentURI;
+    let port = uri.port;
+
+    if (port < 0)
+    {
+      if (uri.scheme == "https")
+        port = 443;
+      else
+        port = "80";
+    }
+
+    let id = uri.host.split("").reverse().join("");
+    id = id + ".:" + uri.scheme + ":" + port;
+    
+    let query = "SELECT key FROM webappsstore2 WHERE " +
+              "scope=" + database.quote(id);
+
+    let handler =
+    {
+      keys: [],
+
+      process_row: function (row)
+      {
+        this.keys.push(row.getResultByName("key"));
+      },
+
+      finalize: function ()
+      {
+        status.dom_storage = this.keys.length;
+      }
+    };
+
+    database.select(query, database.dom_storage, handler);
+  },
+
+  // iterate through regular cookies counting different
+  // kinds for first party and third parties
+  // then do same check for flash supercookies
+  // linear in *total* number of cookies for all hosts
+  rate_cookies: function (browser, parties, offsite) 
+  {
+    let cm = Components.classes['@mozilla.org/cookiemanager;1'].
+      getService(Components.interfaces.nsICookieManager2);
+
+    let cookies = cm.enumerator;
+    let lasting_cookies = [];
+    let session_cookies = [];
+    let int_3rd_lasting_cookies = [];
+    let int_3rd_session_cookies = [];
+    let ext_3rd_lasting_cookies = [];
+    let ext_3rd_session_cookies = [];
+    let status = browser.dashboard_status;
+    let host = status.page_host;
+
+    // iterate through cookiemanager's list of current cookies
+    // and check against first and all third party sites, this
+    // would require *many* database queries using SQLite
+    while (cookies.hasMoreElements())
+    {
+      let cookie = cookies.getNext();
+      cookie.QueryInterface(Components.interfaces.nsICookie2);
+
+      if (host.indexOf(cookie.host) >= 0)
+      {
+        if (cookie.isSession)
+          session_cookies.push(cookie);
+        else
+          lasting_cookies.push(cookie);
+      }
+      else
+      {
+        // pump up the cost!
+        for (let i in parties)
+        {
+          if (parties[i].indexOf(cookie.host) >= 0)
+          {
+            if (offsite[i] == 2)
+            {
+              if (cookie.isSession)
+                ext_3rd_session_cookies.push(cookie);
+              else
+                ext_3rd_lasting_cookies.push(cookie);
+            }
+            else
+            {
+              if (cookie.isSession)
+                int_3rd_session_cookies.push(cookie);
+              else
+                int_3rd_lasting_cookies.push(cookie);
+            }
+          }
+        }
+      }
+    }
+
+    let fso = [];
+    let int_3rd_fso = [];
+    let ext_3rd_fso = [];
+
+    // now examine flash cookies
+    let hosts = this.scan_flash_storage();
+
+    for (let aHost in hosts)
+    {
+      let files = hosts[aHost];
+
+      if (aHost == host && hosts[aHost])
+        fso.push(files);
+      else if (parties[aHost])
+      {
+        if (offset[aHost] == 2)
+          ext_3rd_fso.push(files);
+        else
+          int_3rd_fso.push(files);
+      }
+    }
+
+    let int_3rd_parties = 0;
+    let ext_3rd_parties = 0;
+
+    for (let i = 0; i < offsite.length; ++i)
+    {
+      if (offsite[i] == 2)
+        ++ext_3rd_parties;
+      else
+        ++int_3rd_parties;
+    }
+
+    status.session_cookies = session_cookies.length;
+    status.lasting_cookies = lasting_cookies.length;
+    status.flash_cookies = fso.length;
+    status.int_3rd_parties = int_3rd_parties;
+    status.ext_3rd_parties = ext_3rd_parties;
+    status.int_3rd_party_session_cookies = int_3rd_session_cookies.length;
+    status.int_3rd_party_lasting_cookies = int_3rd_lasting_cookies.length;
+    status.ext_3rd_party_session_cookies = ext_3rd_session_cookies.length;
+    status.ext_3rd_party_lasting_cookies = ext_3rd_lasting_cookies.length;
+    status.int_3rd_party_flash_cookies = int_3rd_fso.length;
+    status.ext_3rd_party_flash_cookies = ext_3rd_fso.length;
+
+    this.check_rating(browser);
+  },
+
+  query_p3p: function (browser, host)
+  {
+    let database = dashboard_overlay.database;
+    let query = "SELECT p3p FROM site_info WHERE host = " + database.quote(host);
+    let handler =
+    {
+      data: [],
+
+      process_row: function (row)
+      {
+        this.data.push(row.getResultByName("p3p"));
+      },
+
+      finalize: function ()
+      {
+        dashboard_overlay.misc.rate_p3p(browser, this.data);
+      }
+    };
+
+    database.select(query, database.db, handler);
+  },
+
+  rate_p3p: function (browser, data)
+  {
+    browser.dashboard_status.p3p = data.length == 1 ? data[0] : 0;
+    this.check_rating(browser);
+  },
+
+  query_geo: function (browser, host)
+  {
+    let database = dashboard_overlay.database;
+    let query = "SELECT permission FROM moz_hosts WHERE type = 'geo' AND host = "
+                + database.quote(host);
+    let handler =
+    {
+      browser: browser,
+      data: [],
+
+      process_row: function (row)
+      {
+        this.data.push(row.getResultByName("permission"));
+      },
+
+      finalize: function ()
+      {
+        dashboard_overlay.misc.rate_geo(this.browser, this.data);
+      }
+    };
+
+    database.select(query, database.permissions, handler);
+  },
+
+  rate_geo: function (browser, data)
+  {
+    let status = browser.dashboard_status;
+    status.geo_permission = data.length == 1 ? data[0] : 0;
+    this.check_rating(browser);
+  },
+
+  // look for web bugs as images with one or less pixel dimension
+  // note visbility:hidden or display:none could be legitimate
+  // TO DO - add check for suspicious urls with ids in them
+  spot_web_bugs: function (browser)
+  {
+    let status = browser.dashboard_status;
+    status.invisible_images = 0;
+    let images = browser.contentDocument.getElementsByTagName("img");
+
+    for (let i = 0; i < images.length; ++i)
+    {
+      if (this.spot_img_web_bug(images[i]))
+        ++status.invisible_images;
+    }
+  },
+
+  spot_img_web_bug: function (image)
+  {
+    let bug = false;
+
+    if (image.width < 2 || image.height < 2)
+      bug = true;
+
+    let size = this.get_computed_style(image, "width");
+
+    if (parseInt(size) < 2)
+      bug = true;
+
+    let size = this.get_computed_style(image, "width");
+
+    if (parseInt(size) < 2)
+      bug = true;
+
+    if (this.get_computed_style(image, "display") == "none")
+      bug = true;
+
+    if (this.get_computed_style(image, "visibility") == "hidden")
+      bug = true;
+
+    return bug;
+  },
+
+  get_computed_style: function(elem, style)
+  {
+    let computed_style;
+
+    if (typeof elem.currentStyle != 'undefined')
+      computed_style = elem.currentStyle;
+    else
+      computed_style = document.defaultView.getComputedStyle(elem, null);
+
+    return computed_style[style];
+  }
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/observer.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,726 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Dashboard.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+// as this is called from the overlay, we get one per window
+// but we may want to instead stick to one per browser, hmmm
+
+// see also https://developer.mozilla.org/en/XPCOM_Interface_Reference/NsITraceableChannel
+// and http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
+
+dashboard_overlay.register_observer({
+  active: false,
+  timer: null,
+  database: null,
+
+  start_up: function (context)
+  {
+    this.database = dashboard_overlay.database;
+    this.context = context; // single instance of dashboard context for all windows
+
+    // ensure that only one set of observers exists for all windows
+    // note: context is a dashboard session object on a singleton window
+    if (context.window_count == 0)
+    {
+       let observerService =
+          Components.classes["@mozilla.org/observer-service;1"]
+                    .getService(Components.interfaces.nsIObserverService);
+      observerService.addObserver(this, "http-on-modify-request", false);
+      observerService.addObserver(this, "http-on-examine-response", false);
+
+      // register content policy
+
+      let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+      let policy = this.policy;
+
+      try
+      {
+       registrar.registerFactory(policy.classID,
+                        policy.classDescription, 
+                        policy.contractID, policy);
+      }
+      catch (e)
+      {
+        Cu.reportError(e);
+      }
+
+      let category_manager = Cc["@mozilla.org/categorymanager;1"]
+                              .getService(Ci.nsICategoryManager);  
+      category_manager.addCategoryEntry("content-policy",
+          policy.classDescription, policy.contractID, false, true);
+      }
+  },
+
+  // if this window hosts the observer, remove it
+  // if this window is not the only window pass ownership
+  shut_down: function (context)
+  {
+    if (context.window_count == 0)
+    {
+      let observerService =
+         Components.classes["@mozilla.org/observer-service;1"]
+                   .getService(Components.interfaces.nsIObserverService);
+
+      observerService.removeObserver(this, "http-on-examine-response");
+      observerService.removeObserver(this, "http-on-modify-request");
+
+      // is it sensible to delete the category entry here?
+      let category_manager = Cc["@mozilla.org/categorymanager;1"]
+                              .getService(Ci.nsICategoryManager);  
+      category_manager.deleteCategoryEntry("content-policy",
+                                 this.policy.classDescription, false);
+
+      // flush any log data to the database
+      this.dump();
+    }
+  },
+
+  // XPCOM interface for content-policy notifications that
+  // allows us to scrutinize requests to load pages, scripts,
+  // etc. before the http request is generated for them
+  policy:
+  {
+    classDescription: "Privacy Dashboard content policy",
+    classID: Components.ID("9762b15c-f233-11df-9eb4-002170f348ec"),
+    contractID: "@dave.raggett/policy;1",
+
+    // nsISupports interface implementation
+
+    QueryInterface: function(aIID)  
+    {
+      if (aIID.equals(Ci.nsIContentPolicy) ||  
+          aIID.equals(Ci.nsISupportsWeakReference) ||  
+          aIID.equals(Ci.nsISupports))  
+        return this;
+      throw Cr.NS_NOINTERFACE;  
+    },
+
+    // nsIFactory interface implementation
+    createInstance: function(outer, iid)
+    {
+      if (outer)
+        throw Cr.NS_ERROR_NO_AGGREGATION;
+
+      return this.QueryInterface(iid);
+    },
+
+    shouldLoad: function(contentType, contentLocation,
+                 requestOrigin, node, mimeTypeGuess, extra)
+    {
+      
+      if (contentType == Components.interfaces.nsIContentPolicy.TYPE_DOCUMENT &&
+             /^http.+/.test(contentLocation.spec) &&
+             node.nodeName == "xul:browser")
+      {
+        node.dashboard_status = 
+          dashboard_overlay.observer.init_status(contentLocation);
+      }
+
+      return Components.interfaces.nsIContentPolicy.ACCEPT;
+    },
+
+    shouldProcess: function(contentType, contentLocation,
+                   requestOrigin, insecNode, mimeType, extra)
+    {
+      return Components.interfaces.nsIContentPolicy.ACCEPT;
+    },
+  },
+
+  // called by the observer service
+  observe: function(subject, topic, data)
+  {
+    let observer = dashboard_overlay.observer;
+
+    if (topic == "http-on-modify-request")
+    {
+      let httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
+      let index = observer.get_tab_index_from_channel(httpChannel);
+      if (index < 0) console.log("*** couldn't get tab for " + httpChannel.URI.spec);
+      let browser = index >= 0 ? gBrowser.getBrowserAtIndex(index) : null;
+
+      // browser is null for requests not involved in page load
+      // e.g. browser p3p request, favicon, and safe browsing
+      // we avoid logging these for now ....
+
+      if (!browser)
+      {
+        if (httpChannel.referrer)
+          console.log("*** couldn't get browser, but referrer is " + httpChannel.referrer.spec);
+        else
+          console.log("*** couldn't get browser on http-on-modify-request for " + httpChannel.URI.spec);
+      }
+
+      if (!browser)
+        return;
+
+      // due to threading and weird delays before notications
+      // are delivered, we can sometimes get the http request 
+      // notification *before* policy shouldLoad, and when
+      // this happens we need to initialize the status here
+
+      let status = browser.dashboard_status;
+
+      if (!status) // we've been called before shouldLoad
+      {
+        status = browser.dashboard_status = 
+          dashboard_overlay.observer.init_status(httpChannel.URI);
+        console.log("observe: creating status for " + httpChannel.URI.spec);
+      }
+
+      // deal with page redirects
+      if (status.new_url && httpChannel.URI.spec == status.new_url)
+      {
+        //alert("responding to redirect to " + httpChannel.URI.host);
+        status.old_uri = status.uri;
+        status.old_host = status.page_host;
+        status.uri = httpChannel.URI;
+        status.page_host = httpChannel.URI.host;
+        status.new_url = null;
+      }
+
+      let prefs = status.prefs;
+      let request_domain = dashboard_overlay.base_domain(httpChannel.URI);
+      let context = dashboard_overlay.context;
+      let target = httpChannel.URI;
+      let referrer = httpChannel.referrer;
+
+      let offsite = 0;
+      let block_3rd_party_cookie = false;
+      let block_lasting_cookies = false;
+      let block_3rd_parties = false;
+
+      // the experimental "do not track" headers, as explained in
+      // http://paranoia.dubfire.net/2011/01/history-of-do-not-track-header.html
+      if (prefs & (1 << 15))
+      {
+        httpChannel.setRequestHeader("X-Behavioral-Ad-Opt-Out", "1", false);
+        httpChannel.setRequestHeader("X-Do-Not-Track", "1", false);
+      }
+
+      block_lasting_cookies = prefs & (1<<5);
+
+      // 3 from bit position in assess:save_prefs   
+      block_3rd_parties = prefs & (1<<3);
+
+      // look for 3rd party requests
+      if (status.page_host && status.page_host != target.host)
+      {
+        offsite = 1; // internal third party request
+
+        if (status.page_domain != request_domain)
+        {
+          offsite = 2;  // external third party request
+
+          if (prefs & (1 << 4)) // 4 from bit position in assess:save_prefs  
+            block_3rd_party_cookie = true;
+
+          if (block_3rd_parties)
+          {
+            offsite = 2;
+            //alert("blocked: " + httpChannel.URI.spec);
+
+            let rp = dashboard_overlay.host_prefs(target.host);
+            let never = rp & (1 <<2); // never block content from this site
+
+            try
+            {
+              if (!never)
+                httpChannel.cancel(-1); //(NS_BINDING_ABORTED);  // -1
+            } 
+            catch (e)
+            {
+              alert("couldn't cancel http request: " + e);
+            }
+          }
+        }
+      }
+
+      // build set of direct and indirect 3rd parties for this page
+
+      let referrer_host = 
+        (referrer && /^http.+/.test(referrer.spec) ? referrer.host : null);
+      
+      if (referrer_host && offsite)
+      {
+        if (referrer_host == status.page_host || status.parties[referrer_host])
+        {
+          status.parties[target.host] = offsite;
+          status.pty.push(target.host);
+        }
+
+        //alert("now " + status.pty.length + " 3rd parties at " + Date.now());
+      }
+
+      // docURI is the previous page for the http request for a new page
+      // and the prefs aren't available until after the onlocationchange event
+
+      let record = new Object();
+      record.uri = target.spec;
+      record.host = target.host;
+      //record.path = target.path;
+      record.referrer = (httpChannel.referrer ? httpChannel.referrer.spec : null);
+      record.ref_host = (httpChannel.referrer ? httpChannel.referrer.host : null);
+      //record.basedomain = request_domain;
+      record.page_host = status.page_host;
+      //record.method = httpChannel.requestMethod;
+      record.offsite = offsite;
+
+      try
+      {
+        let d = httpChannel.getRequestHeader("If-Modified-Since")
+        record.cache_date = this.iso8061(new Date(d));
+       }
+      catch (e)
+      { }
+
+      try
+      {
+        record.content_type = httpChannel.getRequestHeader("Content-Type");
+      }
+      catch (e) 
+      { }
+
+      record.path = target.path;
+      record.query_params = [];
+      record.post_params = [];
+
+      // with name/values pairs in URI query string
+      let query = this.query_string(record.path);
+
+      if (query)
+      {
+        let trim = dashboard_overlay.trim;
+        let params = query.split("&");
+
+        for (let j = 0; j < params.length; ++j)
+        {
+          let param = params[j].split("=");
+          let name = trim(param[0]);
+          let value = param[1];
+          let form = this.is_form_field_name(record.browser, name);
+          record.query_params.push({"name": name, "value": value, "form": form});
+        }
+      }
+
+      // how to determine if the request contains a HTML Form data body?
+      // POST and no Content-Type header and same URI as submit event?
+
+      // code to read the POST body
+      if (httpChannel.requestMethod == "POST")
+      {
+        httpChannel.QueryInterface(Components.interfaces.nsIUploadChannel);
+        httpChannel.uploadStream.QueryInterface(Components.interfaces.nsISeekableStream);
+        var stream = Components.classes["@mozilla.org/scriptableinputstream;1"] 
+                         .createInstance(Components.interfaces.nsIScriptableInputStream); 
+        stream.init(httpChannel.uploadStream);
+        httpChannel.uploadStream.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
+
+        // read from stream: stream.read(...)
+        let len = stream.available();
+
+        // first read the content type which should be
+        // "Content-Type: application/x-www-form-urlencoded"
+        // following assumes single space and \r\n at end of line
+        let buf = stream.read(49);
+
+        // check if it is form data
+        if (/application\/x\-www\-form\-urlencoded/.test(buf))
+        {    
+          buf += stream.read(len-49);
+
+          // risky if requests don't use \r\n properly
+          let start = buf.indexOf("\r\n\r\n");
+
+          if (start > 0)
+            record.post_data = buf.substr(start+4);
+
+          // for simple form this gives something like
+          // your_name=Fred+Flintstone&userid=marvin&form_name=turingtest
+        }
+
+        // and rewind the stream
+        httpChannel.QueryInterface(Components.interfaces.nsIUploadChannel); 
+        httpChannel.uploadStream.QueryInterface(Components.interfaces.nsISeekableStream);
+        httpChannel.uploadStream.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
+
+        if (record.post_data)
+        {
+          let trim = dashboard_overlay.trim;
+          let params = record.post_data.split("&");
+
+          for (let j = 0; j < params.length; ++j)
+          {
+            let param = params[j].split("=");
+            let name = trim(param[0]);
+            let value = param[1];
+            let form = this.is_form_field_name(record.browser, name);
+            record.post_params.push({"name": name, "value": value, "form": form});
+          }
+        }
+      }
+
+      this.requests.push(record);
+
+      let self = this;  // used to ensure 'this' is correct in dump()
+      this.timer = setTimeout(function() {self.dump();}, 100);
+    }
+    else if (topic == "http-on-examine-response")
+    {
+      let httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
+
+    let type = "";
+    try {
+      type = httpChannel.getResponseHeader("Content-Type");
+    } catch (e) {
+      type = "undefined";
+    } 
+
+      console.log("loading (" + httpChannel.responseStatus +  ") " + type + " from " + httpChannel.URI.spec);
+
+      if (httpChannel && httpChannel.responseStatus == 200)
+      {
+        let index = observer.get_tab_index_from_channel(httpChannel);
+
+        let browser = index >= 0 ? gBrowser.getBrowserAtIndex(index) : null;
+
+        if (browser)
+        {
+          let status = browser.dashboard_status;
+          let target = httpChannel.URI;
+
+          let rp = dashboard_overlay.host_prefs(target.host);
+          let never = rp ? rp & (1 <<2) : false ; // never block content from this site
+
+          let block_3rd_parties = status.prefs & (1<<3);
+          let block_flash = status.prefs & (1<<12);
+          let block_java = status.prefs & (1<<13);
+
+          if (block_3rd_parties & !never)
+            observer.block_3rd_party_content(httpChannel);
+          else
+          {
+            if (block_flash & !never)
+              observer.block_flash_content(httpChannel);
+
+            if (block_java && !never)
+              observer.block_java_content(httpChannel);
+          }
+        }
+        else if (httpChannel.referrer)
+        {
+          // could not determine tab, so fall back to referrer
+          let host = httpChannel.referrer.host;
+
+          if (host)
+          {
+            let prefs = dashboard_overlay.host_prefs(host);
+            let target = httpChannel.URI;
+
+            let rp = dashboard_overlay.host_prefs(target.host);
+            let never = rp ? rp & (1 <<2) : false ; // never block content from this site
+
+            let block_3rd_parties = prefs & (1<<3);
+            let block_flash = prefs & (1<<12);
+            let block_java = prefs & (1<<13);
+
+            if (block_3rd_parties & !never)
+              observer.block_3rd_party_content(httpChannel);
+            else
+            {
+              if (block_flash && !never)
+                observer.block_flash_content(httpChannel);
+
+              if (block_java && !never)
+                observer.block_java_content(httpChannel);
+            }
+          }
+        }
+      }
+    }
+  },
+
+  parties: {},             // set of third parties for page
+  requests: new Array(),   // list of http request records
+
+  // determine which tab is associated with given http request
+  // this returns -1 for requests initiated by the browser
+  // e.g. p3p, favicon, safe browsing list, and sometimes for
+  // other requests for resources in web pages, :-(
+  get_tab_index_from_channel: function (aChannel)
+  {
+    try
+    {
+      if (aChannel.notificationCallbacks)
+      {
+        // this often generates an exception - no such interface
+        // if that happens, try again with load group
+        let ir = aChannel.notificationCallbacks.
+             QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+
+        let doc = ir.
+           getInterface(Components.interfaces.nsIDOMWindow).document;
+
+        return gBrowser.getBrowserIndexForDocument(doc);
+      }
+
+      return -2;
+    }
+    catch (e)
+    {
+      if (aChannel.loadGroup)
+      {
+        if (aChannel.loadGroup.notificationCallbacks)
+        {
+          try 
+          {
+            let ir = aChannel.loadGroup.notificationCallbacks.
+               QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+
+            let doc = ir.
+               getInterface(Components.interfaces.nsIDOMWindow).document;
+
+            // the index is occasionally -1 for top level request
+            // for page, but thereafter gives correct result
+            return gBrowser.getBrowserIndexForDocument(doc);
+          }
+          catch (e)
+          {
+            return -4;
+          }
+        }
+      }
+      return -3;
+    }
+  },
+
+  // assumes that existence of referrer has already been checked
+  block_3rd_party_content: function (httpChannel)
+  {
+    let page_host = httpChannel.referrer.host;
+
+    if (!page_host)
+      return false;
+
+    let page_domain = dashboard_overlay.base_domain(page_host);
+    let target_domain = dashboard_overlay.base_domain(httpChannel.URI.host);
+
+    if (page_domain != target_domain)
+    {
+      try
+      {
+        httpChannel.cancel(-1);  //(NS_BINDING_ABORTED);  // -1
+        console.log("blocking flash content");
+      } 
+      catch (e)
+      {
+        console.log("couldn't cancel http request: " + e);
+      }
+
+      return true;
+    }
+
+    return false;
+  },
+
+  block_flash_content: function (httpChannel)
+  {
+    let type = null;
+
+    try {
+      type = httpChannel.getResponseHeader("Content-Type");
+    } catch (e) { type = ""; }
+
+    if (type && (type.indexOf("flash") != -1 ||
+                 type.indexOf("shockwave") != -1 ||
+                 type.indexOf("futuresplash") != -1))
+    {
+      try
+      {
+        httpChannel.cancel(-1);  //(NS_BINDING_ABORTED);  // -1
+        console.log("blocking flash content");
+      } 
+      catch (e)
+      {
+        console.log("couldn't cancel http request: " + e);
+      }
+
+      return true;
+    }
+
+    return false;
+  },
+
+  block_java_content: function (httpChannel)
+  {
+    let type = httpChannel.getResponseHeader("Content-Type");
+
+    if (type && type.indexOf("java-") != -1)
+    {
+      try
+      {
+        httpChannel.cancel(-1); // (NS_BINDING_ABORTED);  // -1
+        console.log("blocking java content");
+      } 
+      catch (e)
+      {
+        console.log("couldn't cancel http request: " + e);
+      }
+
+      return true;
+    }
+
+    return false;
+  },
+
+  init_status: function (uri)
+  {
+    let scheme = uri.scheme;
+    let status = {};
+    status.loaded = false;
+    status.uri = uri;
+    status.page_host = /^http.+/.test(uri.spec) ? uri.host : null;
+    status.page_domain = dashboard_overlay.base_domain(uri);
+    status.prefs = dashboard_overlay.host_prefs(status.page_host);
+    status.parties = {};
+    status.pty = [];
+    status.session_cookies = 0;
+    status.lasting_cookies = 0;
+    status.flash_cookies = 0;
+    status.int_3rd_parties = 0;
+    status.ext_3rd_parties = 0;
+    status.int_3rd_party_session_cookies = 0;
+    status.int_3rd_party_lasting_cookies = 0;
+    status.int_3rd_party_flash_cookies = 0;
+    status.ext_3rd_party_session_cookies = 0;
+    status.ext_3rd_party_lasting_cookies = 0;
+    status.ext_3rd_party_flash_cookies = 0;
+    status.dom_storage = 0;
+    status.html5_pings = 0;
+    status.invisible_images = 0;
+    status.suspicious_urls = 0;
+    status.geo_permission = 0;
+    status.p3p = 0;
+    status.visited = 0;
+
+    var prefs = status.prefs;
+
+    // not sure of the interfaces used by apply_prefs
+    // are safe to call from shouldLoad, so play safe
+    // and apply browser preferences in clean context
+
+    setTimeout( function ()
+    {
+      dashboard_overlay.apply_prefs(prefs);
+    }, 0);
+    return status;
+  },
+
+  query_string: function (path) 
+  {
+    if (path)
+    {
+      let index = path.indexOf("?");
+
+      if (index >= 0)
+        return path.substr(index+1);
+    }
+
+    return null;
+  },
+
+  // return 1 if name is that of form field for this tab
+  is_form_field_name: function (browser, name)
+  {
+    if (!browser)
+      return 0;
+
+    let doc = browser.contentDocument;
+
+    if (!doc)
+      return 0;
+
+    let forms = doc.forms;
+
+    if (!forms)
+      return 0;
+
+    for (let i = 0; i < forms.length; ++i)
+    {
+      let fields = forms[i].elements;
+
+      //if (fields[name])  // boo hoo doesn't work
+      //  return 1;
+
+      for (let j = 0; j < fields.length; ++j)
+      {
+        let fieldName = fields[j].name;
+
+        if (fieldName && fieldName == name)
+          return 1;
+      }
+    }
+
+    return 0;
+  },
+
+  // log records to database and clear them
+  // doc.baseURIObject is the nsURI object for the page
+  dump: function ()
+  {
+    if (this.timer)
+    {
+      clearTimeout(this.timer);
+      this.timer = null;
+    }
+
+    // check if there is something to log
+    if (this.requests.length > 0)
+    {
+      this.database.log_http(this.requests);
+
+      if (this.requests.length > 0)
+        this.requests = new Array(); // reset
+    }
+  },
+
+  QueryInterface : function(aIID)
+  {  
+    if (aIID.equals(Components.interfaces.nsISupports) ||  
+        aIID.equals(Components.interfaces.nsIObserver))  
+      return this;  
+    throw Components.results.NS_NOINTERFACE;  
+  }  
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/overlay.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,769 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is dashboard_overlay.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your vdashboardersion of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+// these shortcuts are already defined for us :)
+// but fail for policy object after chrome reload
+//  const Cc = Components.classes;
+//  const Ci = Components.interfaces;
+//  const Cr = Components.results;
+//  const Cu = Components.utils;
+
+var console = {
+  service: null,
+  log: function(msg)
+  {
+    if (!console.service)
+      console.service = Components.classes["@mozilla.org/consoleservice;1"]
+                     .getService(Components.interfaces.nsIConsoleService);
+    console.service.logStringMessage(msg);
+  }
+};
+
+var dashboard_overlay =
+{
+  // register anonymous object as our observer module
+  // which deals with page, http and tab events
+  register_observer: function (module)
+  {
+    dashboard_overlay.observer = module;
+  },
+
+  // register anonymous object as our database module
+  // which deals with sqlite database support
+  register_database: function (module)
+  {
+    dashboard_overlay.database = module;
+  },
+
+  // register anonymous object as our misc module
+  // which deals with flash, dom storage etc.
+  register_misc: function (module)
+  {
+    dashboard_overlay.misc = module;
+  },
+
+  // register anonymous object as our share module
+  // which deals with uploading site information
+  // every few days for pooling information
+  register_share: function (module)
+  {
+    dashboard_overlay.share = module;
+  },
+
+  on_start: function()
+  {
+    // called when firefox opens new browser window
+    console.log("dashboard starting up for new window");
+    window.dashboard_overlay = this;
+
+    this.install_toolbar_icon();
+    this.find_version();
+
+    this.strings = document.getElementById("dashboard-strings");
+
+    // There can only be one hidden DOM window and
+    // we use it to avoid registering duplicate observers
+    let singleton_window =
+        Components.classes["@mozilla.org/appshell/appShellService;1"].
+          getService(Components.interfaces.nsIAppShellService).hiddenDOMWindow;
+
+    let container = gBrowser.tabContainer;
+
+    container.addEventListener("TabSelect", 
+      function(e) {dashboard_overlay.tab_selected(e);}, false);
+
+    window.addEventListener("click",
+      function(e) {dashboard_overlay.on_window_click(e);}, false);
+
+    // extra work if our window is the first window
+    if (!singleton_window.privacy_dashboard_context)
+    {
+      let context = {};
+      context.window_count = 0;
+      context.last_page = null;
+      context.preferences = {}; // cache of preferences
+      context.pages = {}; // set of currently loading pages
+
+      // long name to avoid collisions with other extensions
+      singleton_window.privacy_dashboard_context = context;
+      singleton_window.dashboard_browser_window = window;
+    }
+
+    this.context = singleton_window.privacy_dashboard_context;   
+    this.strings = document.getElementById("dashboard-strings");
+
+    this.database.misc = this.misc;  // used by dashboard to access misc
+    this.database.start_up(this.context); // set up sqlite
+    this.observer.start_up(this.context); // set up http observers
+    this.share.start_up(this.context); // initialize share module
+
+    // increment window count *after* starting database/observers
+    ++this.context.window_count;
+
+    // set up handlers for web page load and unload events
+    // DOMContentLoaded is fired before image content has all
+    // been loaded, i.e. before "loaded" event is fired
+    let appcontent = document.getElementById("appcontent");   // browser?
+
+    if(appcontent)
+    {
+      appcontent.addEventListener("DOMContentLoaded",
+        function(e)
+        {
+          dashboard_overlay.on_page_load(e);
+        }, true);
+
+      appcontent.addEventListener("unload",
+        function(e)
+        {
+          dashboard_overlay.on_page_unload(e);
+        }, true);
+
+      appcontent.addEventListener("submit",
+        function(e)
+        {
+          dashboard_overlay.on_form_submit(e);
+        }, true);
+    }
+
+    this.misc.purge_flash_cookies();
+
+    // dashbot support
+    this.site_index = 0;
+
+    if (top_sites)  // see top-sites.js
+    {
+      let ioService = Components.classes["@mozilla.org/network/io-service;1"]
+                              .getService(Components.interfaces.nsIIOService);
+
+      var referrer = ioService.newURI("about:blank", null, null);
+      console.log("starting visiting");
+      setTimeout(function ()
+      {
+        dashboard_overlay.visit_site(referrer);
+      }, 4000);
+    }
+  },
+
+  on_exit: function()
+  {
+    // called when firefox closes window
+    let context = this.context;
+
+    let container = gBrowser.tabContainer;
+    container.removeEventListener("TabSelect", this.tab_selected, false);
+
+    let dialog = context.dashboard_dialog;
+
+    if (dialog)
+      dialog.close();
+
+    if (context.window_count)
+      --context.window_count;
+
+    if (context.window_count == 0)
+    {
+      this.observer.shut_down(context);
+      this.database.shut_down(context);
+    }
+  },
+
+  // look for first run, or an upgraded version
+  find_version: function ()
+  {
+    // get new version from the extension's install.rdf
+    console.log("checking for dashboard version");
+    try  // assume Firefox 4
+    {
+      Components.utils.import("resource://gre/modules/AddonManager.jsm");
+      AddonManager.getAddonByID("[email protected]",
+                  function(addon) { this.check_version(addon.version); });
+    }
+    catch (e) // fall back for Firefox 3.6 and earlier
+    {
+      let em = Components.classes["@mozilla.org/extensions/manager;1"]
+                        .getService(Components.interfaces.nsIExtensionManager);
+      let addon = em.getItemForID("[email protected]");
+      this.check_version(addon.version);
+    }
+  },
+
+  check_version: function (cur_ver)
+  {
+    // get previously installed version from preferences if any
+    let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                          .getService(Components.interfaces.nsIPrefService);
+    prefs = prefs.getBranch("[email protected]");
+
+    let prev_ver = null;
+
+    try
+    {
+      prev_ver = prefs.getCharPref("version");
+      console.log("previous version is " + prev_ver);
+    }
+    catch (e)
+    {
+      // nothing to do
+    }
+
+    if (!prev_ver)
+    {
+      // *** debug -- uncomment next line for release
+      //prefs.setCharPref("version", cur_ver);
+
+      // insert first run code here e.g. by visiting home page
+      window.setTimeout(function ()
+      {
+        console.log("first installation using version " + cur_ver);
+      }, 1000);
+    }
+    else if (prev_ver != curr_ver)
+    {
+      // let the user know about the new version e.g. by
+      // visiting the page describing the new features/fixes
+      window.setTimeout(function ()
+      {
+        console.log("upgrade from " + prev_ver + " to " + curr_ver);
+      }, 1000);
+    }
+  },
+
+  // visit top sites, one every 20 seconds
+  visit_site: function (referrer)
+  {
+    let domain = top_sites[this.site_index++];
+
+    console.log("*** visiting " + "http://www." + domain);
+    gBrowser.selectedBrowser.loadURI("http://www." + domain, referrer);
+
+    if (this.site_index < top_sites.length)
+    {
+      setTimeout(function ()
+      {
+        dashboard_overlay.visit_site(referrer);
+      }, 60000);
+    }
+  },
+
+  // invoked when user clicks on different tab
+  tab_selected: function (e)
+  {
+    var browser = gBrowser.getBrowserForTab(e.target)
+    let uri = browser.currentURI;
+ 
+    // introduce delay in calling update on current site status
+    // to complete logging of data associated with current page
+    setTimeout(function ()
+    {
+      dashboard_overlay.misc.rate_site(browser);
+    }, 200);
+  },
+
+  localize: function (key)
+  {
+    try
+    {
+      return dashboard_overlay.strings.getString(key);
+    }
+    catch (e)
+    {
+      alert("couldn't get localized string for " + key);
+      return key;
+    }
+  },
+
+  // install dashboard button in navigation toolbar if not already there
+  install_toolbar_icon: function ()
+  {
+    try
+    {
+      let firefoxnav = document.getElementById("nav-bar");
+      let curSet = firefoxnav.currentSet;
+
+      if (curSet.indexOf("dashboard-toolbar-button") == -1)
+      {
+        let set;
+
+        // Place the button before the urlbar
+
+        if (curSet.indexOf("urlbar-container") != -1)
+          set = curSet.replace(/urlbar-container/, "dashboard-toolbar-button,urlbar-container");
+        else  // at the end
+          set = curSet + ",dashboard-toolbar-button";
+
+        firefoxnav.setAttribute("currentset", set);
+        firefoxnav.currentSet = set;
+        document.persist("nav-bar", "currentset");
+
+        // If you don't do the following call, funny things happen
+
+        try 
+        {
+          BrowserToolboxCustomizeDone(true);
+        }
+        catch (e)
+        { }
+      }
+    }
+    catch(e)
+    { }
+  },
+
+  sync_prefs: function (host, prefs)
+  {
+    if (host && prefs)
+    {
+      this.context.preferences[host] = prefs;
+      this.apply_prefs(prefs);
+    }
+  },
+
+  host_prefs: function (host)
+  {
+    let prefs;  // initially undefined
+
+    if (!host)
+      return prefs;
+
+    prefs = this.context.preferences[host];
+    let database = this.database;
+
+    if (typeof(prefs) == "undefined")
+    {
+      if (database)
+      {
+         var prefs = database.get_prefs(host, database.db);
+
+         // check SQLite table row and field are both defined
+         if (typeof(prefs) == "undefined" || prefs == null)
+         {
+           // no, so prefs have yet to be recorded
+
+           if (typeof(this.default_prefs) == "undefined")
+             prefs = this.default_prefs = database.get_prefs("default", database.db);
+
+           // default hasn't been registered
+           if (typeof(prefs) == "undefined")
+             prefs = 0;
+         }
+      }
+
+      this.sync_prefs(host, prefs);
+    }
+
+    // if preferences are simple adjust bit vector accordingly
+    // to cater for carefree, thoughtful or paranoid defaults
+    let simple = prefs & 3;
+
+    if (simple < 3)
+    {
+      if (simple == 0)
+        prefs = 0;  // carefree: don't block anything
+      else if (simple == 1)
+        prefs = (1 << 4)|(1<<9);  // thoughtful
+      else  // paranoid, so block pretty much everything!
+        prefs = 2 | (1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<<10)|(1<<11)|(1<<12)|(1<<13);
+    }
+
+    return prefs;
+  },
+
+  // browser wide preference settings - this will cause problems
+  // when multiple pages are loaded concurrently e.g when the
+  // browser is restarting with multiple tabs, however, not much
+  // we can do to resolve this without an improved capability API
+  apply_prefs: function (prefs)
+  {
+    let browser_prefs = Components.classes["@mozilla.org/preferences-service;1"]
+          .getService(Components.interfaces.nsIPrefService).getBranch("browser.");
+    let http_prefs = Components.classes["@mozilla.org/preferences-service;1"]
+          .getService(Components.interfaces.nsIPrefService).getBranch("network.http.");
+    let storage_prefs = Components.classes["@mozilla.org/preferences-service;1"]
+          .getService(Components.interfaces.nsIPrefService).getBranch("dom.storage.");
+    let javascript_prefs = Components.classes["@mozilla.org/preferences-service;1"]
+         .getService(Components.interfaces.nsIPrefService).getBranch("javascript.");
+    let geo_prefs = Components.classes["@mozilla.org/preferences-service;1"]
+         .getService(Components.interfaces.nsIPrefService).getBranch("geo.");
+
+    if (typeof(prefs) != "undefined")
+    {
+      // see assess:save_prefs for bit position
+      let block_scripting = prefs & (1 << 7);
+      javascript_prefs.setBoolPref("enabled", !block_scripting);
+
+      let block_geo = prefs & (1 << 8);
+      geo_prefs.setBoolPref("enabled", !block_geo);
+
+      let disable_ping = prefs & (1 << 9);
+      let disable_referrer = prefs & (1 << 10);
+      let disable_storage = prefs & (1 << 11);
+      storage_prefs.setBoolPref("enabled", !disable_storage);
+    }
+    else
+    {
+      javascript_prefs.setBoolPref("enabled", true);
+      geo_prefs.setBoolPref("enabled", true);
+      browser_prefs.setBoolPref("send_pings", false);  // Firefox default
+      http_prefs.setIntPref("sendRefererHeader", 2);
+      storage_prefs.setBoolPref("enabled", true);
+    }
+  },
+
+  // open or close dashboard dialog window
+  open: function (open)
+  {
+    let browser = this.current_browser();
+    let status = browser.dashboard_status;
+
+    let uri = browser.currentURI;
+    let context = this.context;
+
+    if (context.dashboard_dialog && !open)
+    {
+      context.dashboard_dialog.close();
+      context.dashboard_dialog = null;
+    }
+    else if (!context.dashboard_dialog)
+    {
+      if (/^http.+/.test(uri.spec))
+      {
+        this.context.browser = browser;
+        dashboard_overlay.misc.rate_site(browser);
+      }
+
+      var dialog = context.dashboard_dialog = 
+         window.openDialog('chrome://dashboard/content/dashboard.xul',
+                'dashboard', 'chrome,dialog=no', status, window);
+      dialog.database = dashboard_overlay.database;
+      dialog.dashboard_overlay = dashboard_overlay;
+    }
+  },
+
+  clear_database: function (all)
+  {
+    let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+                    .getService(Components.interfaces.nsIPromptService);
+
+    let localize = dashboard_overlay.localize;
+    let title = localize("overlay.clear_data.title");
+    let text = all ? localize("overlay.clear_data.message")
+                   : localize("overlay.compact_data.message");
+    let save_text = localize("overlay.save_data.message");
+    let save = {"value":true};
+
+    if (prompts.confirmCheck(null, title, text, save_text, save))
+    {
+      dashboard_overlay.context.preferences = [];
+
+      if (save.value)  // true or false
+        database_overlay.share.share_and_clear(all);
+      else // clear database right now
+        dashboard_overlay.database.reset(all);
+    }
+  },
+
+  show_notification_bar: function (host, level)
+  {
+    var localize = dashboard_overlay.localize;
+
+    if (!host)
+      host = dashboard_overlay.current_page.host;
+
+    if (!level)
+      level = dashboard_overlay.privacy_level;
+
+    var label = localize("overlay.notification.label")+" "+host+"?";
+    var nb = gBrowser.getNotificationBox()
+    var n = nb.getNotificationWithValue('privacy-warning')
+
+    if(n)
+    {
+      n.label = label;
+    }
+    else
+    {
+      var buttons = [
+      {
+        label: localize("overlay.notification.accept.label"),
+        accessKey: localize("overlay.notification.accept.key"),
+        popup: null,
+        callback: function (bar)
+        {
+          var prefs = 0;
+          var dbo = dashboard_overlay;
+          dbo.sync_prefs(host, prefs);
+          dbo.database.set_prefs(host, prefs);
+          dbo.apply_prefs(prefs);
+        }
+      },
+      {
+        label: localize("overlay.notification.protect.label"),
+        accessKey: localize("overlay.notification.protect.key"),
+        popup: null,
+        callback: function (bar)
+        {
+          var prefs = 2|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<<10)|(1<<11);
+          var dbo = dashboard_overlay;
+          dbo.sync_prefs(host, prefs);
+          dbo.database.set_prefs(host, prefs);
+          dbo.apply_prefs(prefs);
+        }
+      },
+      {
+        label: localize("overlay.notification.more.label"),
+        accessKey: localize("overlay.notification.more.key"),
+        popup: null,
+        callback: function (bar)
+        {
+          dashboard_overlay.open(true);
+        }
+      }];
+
+      var icon;
+
+      if (level == 2)
+        icon = "chrome://dashboard-common/skin/mad-tongue.png";
+      else if (level == 1)
+        icon = "chrome://dashboard-common/skin/disappointed.png";
+      else
+        icon = "chrome://dashboard-common/skin/glasses-cool.png";
+
+      const priority = nb.PRIORITY_WARNING_MEDIUM;
+      nb.appendNotification(label, 'privacy-warning',
+                         icon, priority, buttons);
+    }
+  },
+
+  on_form_submit: function (e)
+  {
+    //alert("form submit");
+  },
+
+  // this is used to ensure that the dashboard is updated
+  // when clicking on a different browser window
+  on_window_click: function (aEvent)
+  {
+    var singleton_window =
+        Components.classes["@mozilla.org/appshell/appShellService;1"].
+          getService(Components.interfaces.nsIAppShellService).hiddenDOMWindow;
+
+    if (singleton_window.dashboard_browser_window != window)
+    {
+     // if (gBrowser.contentDocument.documentURIObject.scheme == "about")
+     //   alert("clicked on blank window");
+
+      var browser = this.current_browser();
+
+      // introduce delay in calling update on current site status
+      // to complete logging of data associated with current page
+      setTimeout(function ()
+      {
+        dashboard_overlay.misc.rate_site(browser);
+      }, 300);
+
+      singleton_window.dashboard_browser_window = window;
+    }
+  },
+
+
+  // further work needed to deal with iframes, for now ignore them
+  on_page_load: function(aEvent)
+  {
+    let doc = aEvent.originalTarget;
+    console.log("on page load event - " + doc.location.href);
+
+    if (doc instanceof HTMLDocument)
+    {
+      let win = doc.defaultView;
+
+      if (win.frameElement)
+      {
+        // find root document, needs further work!
+        win = win.top;
+        return;
+      }
+
+      // find browser for this document
+      var browser = this.find_browser_from_doc(doc);
+      var status = browser.dashboard_status;
+
+      // status is null for about:* pages
+      var host = (status ? status.page_host : null);
+
+      if (status)
+      {
+        status.loaded = true;
+
+        // some page redirects aren't caught by observer
+        // perhaps we can do without the observer check?
+        if (doc.documentURIObject.spec != doc.location.href)
+          alert("mismatch between documentURIObject and location!");
+
+        if (status.uri.spec != doc.documentURIObject.spec)
+        {
+          status.old_uri = status.uri;
+          status.old_host = status.page_host;
+          status.uri = doc.documentURIObject;
+          status.page_host = null;
+          status.new_url = null;
+
+          if (/^http.+/.test(status.uri.spec))
+            status.page_host = status.uri.host;
+        }
+      }
+      else if (/^http.+/.test(doc.location.protocol))
+        alert("overlay:page_load missing status for " + doc.location.href);
+
+      if (host)
+      {
+        // doc is document that triggered "onload" event  
+        // use this event to log observed http data
+        this.observer.dump();  // asynchronous local database operation
+
+        let uri = browser.currentURI;
+
+        // check if the user has visited this site before
+        this.database.mark_visited(browser);
+
+        // firefox raises exception on accessing location.host
+        // for URLs like about:home, so we first check protocol
+        if (/^http.+/.test(doc.location.protocol))
+        {
+          this.misc.get_p3p_policy(doc.location.host, null,
+          function (host, req)
+          {
+            dashboard_overlay.database.p3p_log(host, req);
+          });
+        }
+        // page load may be handled before last http request notify
+        // this is very surprising, and discovered by timing the events
+        setTimeout(function ()
+        {
+          dashboard_overlay.database.log_3rd_parties(status);
+        }, 200);
+  
+        // and a little later still log site rating to database
+        setTimeout(function ()
+        {
+          dashboard_overlay.database.update_site_info(host, status);
+          
+        }, 2000);
+      }
+  
+      // introduce delay in calling update on current site status
+      // to complete logging of data associated with current page
+      // and allow for web page scripts doing stuff on page load
+      setTimeout(function ()
+      {
+        dashboard_overlay.misc.rate_site(browser);
+      }, 500);
+    }
+  },
+  
+  on_page_unload: function(aEvent)
+  {  
+    var doc = aEvent.originalTarget;
+    // doc is document that triggered "onunload" event  
+    // do something with the unloaded page.
+
+    //gBrowser.removeProgressListener(this.progressListener);
+  },
+
+  // base domain of nsIURI is used for distinguishing
+  // internal and external third party websites
+  base_domain: function (uri)
+  {
+    let base;
+
+    try
+    {
+      var eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
+                  .getService(Components.interfaces.nsIEffectiveTLDService);
+      base = eTLDService.getBaseDomain(uri);
+    }
+    catch (e)
+    {
+      base == "";
+    }
+
+    return base;
+  },
+
+  current_browser: function ()
+  {
+    return gBrowser.getBrowserForTab(gBrowser.selectedTab);
+  },
+
+  // find browser given a document - used on page load
+  find_browser_from_doc: function (doc)
+  {
+    var n = gBrowser.browsers.length;  
+
+    for (var i = 0; i < n; i++)
+    {  
+      var b = gBrowser.getBrowserAtIndex(i);
+
+      if (b.contentDocument == doc)
+        return b;
+    }
+
+    return null;
+  },
+
+  // convenience function to trim leading/trailing spaces
+  trim: function (s)
+  {
+    let i, j;
+
+    for (i = 0; i < s.length && s.charCodeAt(i) == 32; ++i);
+    for (j = s.length-1; j > 0 && s.charCodeAt(j) == 32; --j);
+
+    if (i <= j)
+      return s.substr(i, j-i+1);
+
+    // otherwise leave string as is
+    return s;
+  }
+};
+
+// wait for window to load before accessing tabBrowser, see
+// https://developer.mozilla.org/En/Code_snippets/Tabbed_browser
+
+window.addEventListener("load", function() { dashboard_overlay.on_start(); }, false); 
+window.addEventListener("unload", function() { dashboard_overlay.on_exit(); }, false); 
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/overlay.xul	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+  -   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+  -
+  - The contents of this file are subject to the Mozilla Public License Version
+  - 1.1 (the "License"); you may not use this file except in compliance with
+  - the License. You may obtain a copy of the License at
+  - http://www.mozilla.org/MPL/
+  - 
+  - Software distributed under the License is distributed on an "AS IS" basis,
+  - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  - for the specific language governing rights and limitations under the
+  - License.
+  -
+  - The Original Code is Dashboard.
+  -
+  - The Initial Developer of the Original Code is
+  - Dave Raggett.
+  - Portions created by the Initial Developer are Copyright (C) 2010
+  - the Initial Developer. All Rights Reserved.
+  -
+  - Contributor(s):
+  -
+  - Alternatively, the contents of this file may be used under the terms of
+  - either the GNU General Public License Version 2 or later (the "GPL"), or
+  - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  - in which case the provisions of the GPL or the LGPL are applicable instead
+  - of those above. If you wish to allow use of your version of this file only
+  - under the terms of either the GPL or the LGPL, and not to allow others to
+  - use your version of this file under the terms of the MPL, indicate your
+  - decision by deleting the provisions above and replace them with the notice
+  - and other provisions required by the GPL or the LGPL. If you do not delete
+  - the provisions above, a recipient may use your version of this file under
+  - the terms of any one of the MPL, the GPL or the LGPL.
+  - 
+  - ***** END LICENSE BLOCK ***** -->
+
+<?xml-stylesheet href="chrome://dashboard-common/skin/overlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://dashboard/skin/overlay.css" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://dashboard/locale/dashboard.dtd">
+<overlay id="dashboard-overlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <!-- <script src="top-sites.js" type="application/x-javascript"/> -->
+  <script src="overlay.js" type="application/x-javascript"/>
+  <script src="observer.js" type="application/x-javascript"/>
+  <script src="database.js" type="application/x-javascript"/>
+  <script src="misc.js" type="application/x-javascript"/>
+  <script src="share.js" type="application/x-javascript"/>
+  <stringbundleset id="stringbundleset">
+    <stringbundle id="dashboard-strings" src="chrome://dashboard/locale/dashboard.properties"/>
+  </stringbundleset>
+
+  <menupopup id="menu_ToolsPopup">
+    <menuitem label="&dashboard.menu.bar.label;" insertafter="devToolsSeparator"
+              oncommand="dashboard_overlay.show_notification_bar();"/>
+    <menuitem label="&dashboard.menu.compact.label;" insertafter="devToolsSeparator"
+              oncommand="dashboard_overlay.clear_database(false);"/>
+    <menuitem label="&dashboard.menu.clear.label;" insertafter="devToolsSeparator"
+              oncommand="dashboard_overlay.clear_database(true);"/>
+    <menuitem label="&dashboard.menu.dialog.label;" insertafter="devToolsSeparator"
+              oncommand="dashboard_overlay.open(false);"/>
+  </menupopup>
+
+  <toolbarpalette id="BrowserToolbarPalette">
+    <toolbarbutton id="dashboard-toolbar-button"
+      label="&dashboard.toolbar.label;"
+      tooltiptext="&dashboard.toolbar.tooltip;"
+      oncommand="dashboard_overlay.open(false);"
+      class="toolbarbutton-1 chromeclass-toolbar-additional custombutton"/>
+  </toolbarpalette>
+</overlay>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/p3p.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,719 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Dashboard.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+var dashboard_p3p =
+{
+  // *** TO DO add check for compact policy in HTTP
+  // responses as part of HTTP observer code ***
+  render_policy: function (uri, path, depth)
+  {
+    if (depth > 4)  // endless loop detector
+      return alert("couldn't find correct policy for path: " + path);
+
+    var req = new XMLHttpRequest();
+
+    req.open("GET", uri, true);
+    req.onreadystatechange = function (evt)
+    {
+
+      if (req.readyState == 4)
+      {
+        if (req.status == 200)
+        {
+          if (req.responseXML)
+            dashboard_p3p.find_applicable_policy(path, req.responseXML, depth);
+          else
+          {
+/*
+            if (req.channel.contentType == "text/html");
+            {
+              var parent_window = window.arguments ? window.arguments[1] : null;
+              var browser = parent_window.gBrowser;
+              browser.selectedTab =  browser.addTab(uri);
+            }
+*/
+            alert("malformed P3P policy for url: " + uri +
+                  "\nmedia type is " + req.channel.contentType);
+          }
+        }
+        else if (req.status == 404)
+        {
+          let p3p = req.getResponseHeader("P3P");
+
+          if (p3p)
+          {
+            let i = p3p.indexOf("policyref=");
+
+            if (i >= 0)
+            {
+              let c = p3p.charAt(10);
+              p3p = p3p.substr(11);
+              i = p3p.indexOf(c);
+              p3p = p3p.substring(0, i);
+              return dashboard_p3p.render_policy(p3p, path, depth+1);
+            }
+          }
+
+          alert("http status = " + req.status + " for url = " + uri);
+        }
+        else
+          alert("http status = " + req.status + " for url = " + uri);
+      }
+    };
+    req.send(null);
+  },
+
+// from XUL overlay, we can access req.channel.URI.resolve(rel_url)
+// but not from regular web page script, so we use hack for now
+// should match path to include and exclude statements, hack for now
+
+  find_applicable_policy: function (path, doc, depth)
+  {
+    let uri = null, el, pattern;
+    let root = doc.documentElement;
+    let includes = root.getElementsByTagName("INCLUDE");
+
+    for (let i = 0; includes && i < includes.length; ++i)
+    {
+      el = includes[i];
+      pattern = el.firstChild.nodeValue;  // should trim space - FIX ME
+
+      if (pattern == "/*")
+      {
+        uri = includes[i].parentNode.getAttribute("about");
+        break;
+      }
+    }
+
+    if (!uri)
+    {
+      let pr = root.getElementsByTagName("POLICY-REF");
+
+      if (pr && pr.length > 0)
+        uri = pr[0].getAttribute("about");
+    }
+
+    if (uri)
+    {
+      let host = assess.host;
+      let hash = uri.indexOf("#");
+
+      if (hash == 0)
+        dashboard_p3p.show_policy(path, root);
+      else
+      {
+        if (hash >= 0)
+          uri = uri.substr(0, hash);
+
+        if (uri.substr(0,4) != "http")
+        {
+          if (uri.charAt(0) == "/")
+            uri = "http://"+host+uri;
+          else
+            uri = "http://"+host+"/w3c/"+uri;
+        }
+
+        //alert("path for referenced policy: " + uri);
+/*
+        var base_uri = req.channel.URI;
+        alert("base policy: " + base_uri.spec);
+
+        var uri = base_uri.resolve(uri).spec;   
+        alert("uri: " + uri);  
+*/
+        dashboard_p3p.render_policy(uri, path, depth+1);
+      }
+    }
+    else
+    {
+      // otherwise render policy
+      if (root.nodeName != "parsererror")
+        dashboard_p3p.show_policy(path, root);
+      else
+        alert("malformed XML");
+    }
+  },
+
+  show_policy: function (path, root) {
+    let p3p = dashboard_p3p;
+
+    if (!p3p.description)
+      p3p.description = document.getElementById("p3p_policy");
+
+    let el, child;
+
+    for (el = p3p.description; el.firstChild; el.removeChild(el.firstChild));
+
+    el = document.createElementNS("http://www.w3.org/1999/xhtml", "em");
+    p3p.description.appendChild(el);
+
+    el.innerHTML = dashboard.localize("p3p_render.comment");
+
+    let policies = root.getElementsByTagName("POLICY");
+    let policy = policies ? policies[0] : null;
+
+    if (!policy && root.nodeName == "POLICY")
+      policy = root;
+
+    if (!policy)
+    {
+      alert("couldn't find POLICY element");
+      return;
+    }
+
+    let discuri = policy.getAttribute("discuri");
+    let tab = document.getElementById("dashboard_current");
+    tab.className = "policy";
+
+    // use server's own rendering of policy
+    if (false)
+    {
+       if (discuri)
+       {
+         location.href = discuri;
+         return;
+       }
+    }
+
+    // deal with contact info
+    let contact = p3p.get_contact_group(policy);
+    p3p.dump_contact(contact, discuri);
+
+    // deal with access info
+    let access = policy.getElementsByTagName("ACCESS");
+
+    if (access && access.length > 0)
+      access = access[0];
+
+    // deal with dispute info
+    // hmm, let's ignore this for now
+
+    // deal with statements
+    let statements = policy.getElementsByTagName("STATEMENT");
+    for (let i = 0; i < statements.length; ++i)
+      p3p.dump_statement(statements[i]);
+  },
+
+  get_contact_group: function (policy)
+  {
+    let ents = policy.getElementsByTagName("ENTITY");
+  
+    if (ents && ents.length > 0)
+    {
+      let dg = ents[0].getElementsByTagName("DATA-GROUP");
+ 
+      if (dg && dg.length > 0)
+        return dg[0];
+    }
+
+    return null;
+  },
+
+  dump_contact: function (contact, discuri)
+  {
+    let obj = {};
+
+    for (let data = contact.firstChild; data; data = data.nextSibling)
+    {
+      if (data.nodeType == 1)
+      {
+        let ref = data.getAttribute("ref");
+        let value = data.firstChild.nodeValue;
+        this.analyse_info(obj, ref, value);
+      }
+    }
+
+    let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+    //div.setAttribute("xmlns:html", "http://www.w3.org/1999/xhtml");
+    let s = "";
+
+    s += "<html:h2>"+obj.business.name+" P3P Privacy Policy</html:h2>\n";
+
+    s += "<html:p><html:em>See also site's <html:a id='shrp'>" +
+         "human readable policy</html:a></html:em></html:p>\n";
+
+    s += "<html:p>\n";
+
+    s += obj.business.contact_info.postal.street + "<html:br />\n";
+    s += obj.business.contact_info.postal.city + ", ";
+
+    if (obj.business.contact_info.postal.stateprov)
+      s += obj.business.contact_info.postal.stateprov + " ";
+  
+    if (obj.business.contact_info.postal.postalcode)
+      s += obj.business.contact_info.postal.postalcode + "<html:br />\n";
+
+    s += obj.business.contact_info.postal.country + "<html:br />\n";
+
+    if (obj.business.contact_info.online.email)
+    {
+      s += "<html:br />email: \n";
+      s += obj.business.contact_info.online.email;
+    }
+
+    if (obj.business.contact_info.telecom &&
+           obj.business.contact_info.telecom.telephone)
+    {
+      s += "<html:br />phone: \n";
+      if (obj.business.contact_info.telecom.telephone.intcode)
+      {
+        s += "+"  + obj.business.contact_info.telecom.telephone.intcode;
+        s += "."  + obj.business.contact_info.telecom.telephone.loccode;
+        s += "."  + obj.business.contact_info.telecom.telephone.number;
+      }
+      else if (obj.business.contact_info.telecom.telephone.number)
+      {
+         if (obj.business.contact_info.telecom.telephone.number.substr(0,2) == "00")
+           s += "+" + obj.business.contact_info.telecom.telephone.substr(2);
+         else
+           s += obj.business.contact_info.telecom.telephone.number;
+      }
+      else if (typeof (obj.business.contact_info.telecom.telephone) == "string")
+      {
+         if (obj.business.contact_info.telecom.telephone.substr(0,2) == "00")
+           s += "+" + obj.business.contact_info.telecom.telephone.substr(2);
+         else
+           s += obj.business.contact_info.telecom.telephone;
+      }
+    }
+
+    s += "\n</html:p>\n";
+
+    this.description.appendChild(div);
+    div.innerHTML += s;
+
+    // hypertext link in XUL dialog normally opens in that dialog
+    // we override that behavior here to force load into new tab
+    // on the parent browser window, and to then select that tab
+    var parent_window = window.arguments ? window.arguments[1] : null;
+    let link = document.getElementById("shrp");
+    link.addEventListener("click", function (e) {
+       let browser = parent_window.gBrowser;
+       browser.selectedTab =  browser.addTab(discuri);
+       e.cancel = true;
+       e.stopPropagation();
+       e.preventDefault();
+       return false;
+    }, false);
+  },
+
+  // map ref string into object tree
+  analyse_info: function (obj, ref, value)
+  {
+    ref = ref.substr(1).split(".");
+
+    let part = obj;
+    let name;
+
+    for (let i = 0; i < ref.length - 1; ++i)
+    {
+      name = ref[i];
+      name = name.replace(/-/g, "_");
+
+      if (!part[name])
+        part[name] = {};
+
+      part = part[name];
+    }
+
+    name = ref[ref.length - 1];
+    part[name] = value;
+  },
+
+  dump_statement: function (statement)
+  {
+    let div =  document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+    this.description.appendChild(div);
+
+    let p =document.createElementNS("http://www.w3.org/1999/xhtml", "p");
+    p.innerHTML = this.get_consequence(statement);
+    div.appendChild(p);
+
+    p = document.createElementNS("http://www.w3.org/1999/xhtml", "p");
+    div.appendChild(p); 
+    p.innerHTML = "The follow kinds of data are collected";
+
+    let ul = document.createElementNS("http://www.w3.org/1999/xhtml", "ul");
+    div.appendChild(ul); 
+
+    let el = statement.getElementsByTagName("DATA-GROUP");
+
+    if (el && el.length > 0)
+    {
+      let data = el[0].getElementsByTagName("DATA");
+      let li;
+      let desc;
+
+      for (let i = 0; i < data.length; ++i)
+      {
+        let ref = data[i].getAttribute("ref").substr(1);
+        let desc = this.get_description(ref);
+        if (!desc) desc = ref;
+        li = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+        ul.appendChild(li);
+        li.innerHTML = desc;
+      }
+    }
+
+    p = document.createElementNS("http://www.w3.org/1999/xhtml", "p");
+    p.innerHTML = "This data is used for";
+    div.appendChild(p); 
+
+    let ul = document.createElementNS("http://www.w3.org/1999/xhtml", "ul");
+    ul.className = "long";
+    div.appendChild(ul); 
+
+    el = statement.getElementsByTagName("PURPOSE");
+
+    if (el && el.length > 0)
+    {
+      for (el = el[0].firstChild; el; el = el.nextSibling)
+      {
+        if (el.nodeType != 1)
+          continue;
+
+        let purpose = this.purposes[el.nodeName];
+        if (!purpose) purpose = el.nodeName;
+
+        let li = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+        ul.appendChild(li);
+        try 
+        {
+          li.innerHTML = purpose+this.opt_in_out(el);
+        }
+        catch (e) 
+        {
+          alert("bad: " + purpose+this.opt_in_out(el));
+        }
+      }
+    }
+
+    p = document.createElementNS("http://www.w3.org/1999/xhtml", "p");
+    div.appendChild(p); 
+    p.innerHTML = "This data is used by";
+
+    let ul = document.createElementNS("http://www.w3.org/1999/xhtml", "ul");
+    ul.className = "long";
+    div.appendChild(ul); 
+
+    el = statement.getElementsByTagName("RECIPIENT");
+
+    if (el && el.length > 0)
+    {
+      for (el = el[0].firstChild; el; el = el.nextSibling)
+      {
+        if (el.nodeType != 1)
+          continue;
+
+        let recipient = this.recipients[el.nodeName];
+        if (!recipient) purpose = el.nodeName;
+
+        let li = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+        ul.appendChild(li);
+        li.innerHTML = recipient+this.opt_in_out(el)+
+           this.recipient_description(el);
+      }
+    }
+
+    p = document.createElementNS("http://www.w3.org/1999/xhtml", "p");
+    p.innerHTML = "Data retention:";
+    div.appendChild(p);
+
+    let ul = document.createElementNS("http://www.w3.org/1999/xhtml", "ul");
+    ul.className = "long";
+    div.appendChild(ul); 
+
+    el = statement.getElementsByTagName("RETENTION");
+
+    if (el && el.length > 0)
+    {
+      for (el = el[0].firstChild; el; el = el.nextSibling)
+      {
+        if (el.nodeType != 1)
+          continue;
+
+        let retention = this.retention[el.nodeName];
+        if (!retention) purpose = el.nodeName;
+
+        let li = document.createElementNS("http://www.w3.org/1999/xhtml", "li");
+        ul.appendChild(li);
+        li.innerHTML = retention;
+      }
+    }
+  },
+
+  opt_in_out: function (el)
+  {
+    let req = el.getAttribute("required");
+
+    if (req == "opt-in")
+      return " (opt in)";
+
+    if (req == "opt-out")
+      return " (opt out)";
+
+    return "";
+  },
+
+  recipient_description: function (el)
+  {
+    el = el.firstElementChild;
+
+    if (el)
+      return ", but note that " + el.firstChild.nodeValue;
+
+    return "";
+  },
+
+  get_consequence: function (statement)
+  {
+    let el = statement.getElementsByTagName("CONSEQUENCE");
+
+    if (el && el.length > 0)
+      return el[0].firstChild.nodeValue;
+
+    return null;
+  },
+
+  purposes:
+  {
+    "current" :
+      "Completion and Support of Activity For Which Data Was " +
+      "Provided<html:span>: Information may be used by the service provider " +
+      "to complete the activity for which it was provided, whether " +
+      "a one-time activity such as returning the results from a Web " +
+      "search, forwarding an email message, or placing an order; or " +
+      "a recurring activity such as providing a subscription service, " +
+      "or allowing access to an online address book or electronic " +
+      "wallet.</html:span>",
+    "admin" :
+      "Web Site and System Administration<html:span>: Information may be used " +
+      "for the technical support of the Web site and its computer " +
+      "system. This would include processing computer account " +
+      "information, information used in the course of securing and " +
+      "maintaining the site, and verification of Web site activity " +
+      "by the site or its agents.</html:span>",
+    "develop" :
+      "Research and Development<html:span>: Information may be used to " +
+      "enhance, evaluate, or otherwise review the site, service, " +
+      "product, or market. This does not include personal information " +
+      "used to tailor or modify the content to the specific individual " +
+      "nor information used to evaluate, target, profile or contact " +
+      "the individual.</html:span>",
+    "tailoring" :
+      "One-time Tailoring<html:span>: Information may be used to tailor " +
+      "or modify content or design of the site where the information " +
+      "is used only for a single visit to the site and not used for " +
+      "any kind of future customization. For example, an online store " +
+      "might suggest other items a visitor may wish to purchase based " +
+      "on the items he has already placed in his shopping basket.</html:span>",
+    "pseudo-analysis" :
+      "Pseudonymous Analysis<html:span>: Information may be used to create " +
+      "or build a record of a particular individual or computer that " +
+      "is tied to a pseudonymous identifier, without tying identified " +
+      "data (such as name, address, phone number, or email address) " +
+      "to the record. This profile will be used to determine the habits, " +
+      "interests, or other characteristics of individuals for purpose " +
+      "of research, analysis and reporting, but it will not be used to " +
+      "attempt to identify specific individuals. For example, a marketer " +
+      "may wish to understand the interests of visitors to different " +
+      "portions of a Web site.</html:span>",
+    "pseudo-decision" :
+      "Pseudonymous Decision<html:span>: Information may be used to create " +
+      "or build a record of a particular individual or computer that " +
+      "is tied to a pseudonymous identifier, without tying identified " +
+      "data (such as name, address, phone number, or email address) to " +
+      "the record. This profile will be used to determine the habits, " +
+      "interests, or other characteristics of individuals to make a " +
+      "decision that directly affects that individual, but it will not " +
+      "be used to attempt to identify specific individuals. For example, " +
+      "a marketer may tailor or modify content displayed to the browser " +
+      "based on pages viewed during previous visits.</html:span>",
+    "individual-analysis" :
+      "Individual Analysis<html:span>: Information may be used to determine " +
+      "the habits, interests, or other characteristics of individuals " +
+      "and combine it with identified data for the purpose of research, " +
+      "analysis and reporting. For example, an online Web site for a " +
+      "physical store may wish to analyze how online shoppers make " +
+      "offline purchases.</html:span>",
+    "individual-decision" :
+      "Individual Decision<html:span>:  Information may be used to determine " +
+      "the habits, interests, or other characteristics of individuals " +
+      "and combine it with identified data to make a decision that " +
+      "directly affects that individual.  For example, an online store " +
+      "suggests items a visitor may wish to purchase based on items he " +
+      "has purchased during previous visits to the Web site.</html:span>",
+    "contact" :
+      "Contacting Visitors for Marketing of Services or Products<html:span>: " +
+      "Information may be used to contact the individual, through a " +
+      "communications channel other than voice telephone, for the " +
+      "promotion of a product or service. This includes notifying " +
+      "visitors about updates to the Web site. This does not include a " +
+      "direct reply to a question or comment or customer service for a " +
+      "single transaction -- in those cases, <current/> would be used. " +
+      "In addition, this does not include marketing via customized Web " +
+      "content or banner advertisements embedded in sites the user is " +
+      "visiting.</html:span>",
+    "historical" :
+      "Historical Preservation<html:span>: Information may be archived or " +
+      "stored for the purpose of preserving social history as governed " +
+      "by an existing law or policy. This law or policy MUST be " +
+      "referenced in the <DISPUTES> element and MUST include a specific " +
+      "definition of the type of qualified researcher who can access the " +
+      "information, where this information will be stored and specifically " +
+      "how this collection advances the preservation of history.</html:span>",
+    "telemarketing" :
+      "Contacting Visitors for Marketing of Services or Products Via " +
+      "Telephone<html:span>: Information may be used to contact the individual " +
+      "via a voice telephone call for promotion of a product or service. " +
+      "This does not include a direct reply to a question or comment or " +
+      "customer service for a single transaction.</html:span>",
+    "other-purpose" : "Additional purposes."
+  },
+
+  recipients:
+  {
+    "ours" :
+      "Ourselves and/or entities acting as our agents or entities for " +
+      "whom we are acting as an agent<html:span>: An agent in this instance " +
+      "is defined as a third party that processes data only on behalf " +
+      "of the service provider for the completion of the stated purposes. " +
+      "(e.g., the service provider and its printing bureau which prints " +
+      "address labels and does nothing further with the information.)</html:span>",
+    "delivery" :
+      "Delivery services possibly following different practices<html:span>: " +
+      "Legal entities performing delivery services that may use data for " +
+      "purposes other than completion of the stated purpose. This should " +
+      "also be used for delivery services whose data practices are " +
+      "unknown.</html:span>",
+    "same" :
+      "Legal entities following our practices<html:span>: Legal entities who " +
+      "use the data on their own behalf under equable practices. (e.g., " +
+      "consider a service provider that grants the user access to collected " +
+      "personal information, and also provides it to a partner who uses " +
+      "it once but discards it. Since the recipient, who has otherwise " +
+      "similar practices, cannot grant the user access to information " +
+      "that it discarded, they are considered to have equable " +
+      "practices.)</html:span>",
+    "other-recipient" :
+      "Legal entities following different practices<html:span>: Legal entities "+
+      "that are constrained by and accountable to the original service " +
+      "provider, but may use the data in a way not specified in the " +
+      "service provider's practices (e.g., the service provider collects " +
+      "data that is shared with a partner who may use it for other " +
+      "purposes. However, it is in the service provider's interest to " +
+      "ensure that the data is not used in a way that would be considered " +
+      "abusive to the users' and its own interests.)</html:span>",
+    "unrelated" :
+      "Unrelated third parties<html:span>: Legal entities whose data usage " +
+      "practices are not known by the original service provider.</html:span>",
+    "public" :
+      "Public fora<html:span>: Public fora such as bulletin boards, public " +
+      "directories, or commercial CD-ROM directories.</html:span>"
+  },
+
+  retention:
+  {
+    "no-retention" :
+      "Information is not retained for more than a brief period of time " +
+      "necessary to make use of it during the course of a single online " +
+      "interaction. <html:span>Information MUST be destroyed following this " +
+      "interaction and MUST NOT be logged, archived, or otherwise stored. " +
+      "This type of retention policy would apply, for example, to services " +
+      "that keep no Web server logs, set cookies only for use during a " +
+      "single session, or collect information to perform a search but do " +
+      "not keep logs of searches performed.</html:span>",
+    "stated-purpose" :
+      "Information is retained to meet the stated purpose. <html:span>This " +
+      "requires information to be discarded at the earliest time possible. " +
+      "Sites MUST have a retention policy that establishes a destruction " +
+      "time table. The retention policy MUST be included in or linked " +
+      "from the site's human-readable privacy policy.</html:span>",
+    "legal-requirement" :
+      "As required by law or liability under applicable law<html:span>: " +
+      "Information is retained to meet a stated purpose, but the " +
+      "retention period is longer because of a legal requirement or " +
+      "liability. For example, a law may allow consumers to dispute " +
+      "transactions for a certain time period; therefore a business may " +
+      "for liability reasons decide to maintain records of transactions, " +
+      "or a law may affirmatively require a certain business to maintain " +
+      "records for auditing or other soundness purposes. Sites MUST have " +
+      "a retention policy that establishes a destruction time table. The " +
+      "retention policy MUST be included in or linked from the site's " +
+      "human-readable privacy policy.</html:span>",
+    "business-practices" :
+      "Determined by service provider's business practice<html:span>: " +
+      "Information is retained under a service provider's stated " +
+      "business practices. Sites MUST have a retention policy that " +
+      "establishes a destruction time table. The retention policy MUST " +
+      "be included in or linked from the site's human-readable privacy " +
+      "policy.</html:span>",
+    "indefinitely" :
+      "Indefinitely<html:span>: Information is retained for an indeterminate " +
+      "period of time. The absence of a retention policy would be " +
+      "reflected under this option. Where the recipient is a public " +
+      "fora, this is the appropriate retention policy.</html:span>"
+  },
+
+  get_description: function (name)
+  {
+    let desc = this.data_description[name];
+    if (!desc) desc = name;
+    return desc;
+  },
+
+  data_description:
+  {
+    "user.name" : "user's name",
+    "user.employer" : "user's employer",
+    "dynamic.clickstream" : "information typically found in Web " +
+      "server access logs, e.g.  the IP address or hostname of the user's " +
+      "computer, the URI of the resource requested, the time the request " +
+      "was made, the HTTP method used in the request, the size of the " +
+      "response, and the HTTP status code in the response.",
+    "dynamic.http" : "additional information carried in HTTP requests",
+    "dynamic.http.useragent" : "additional information carried " + 
+      "in HTTP user agent headers",
+    "dynamic.clientevents" : "information about how the user " +
+      "interacts with a web page, e.g. mouse movements and clicks",
+    "dynamic.searchtext" :"search terms entered by the user",
+    "dynamic.interactionrecord": "additional information on " +
+      "user interaction, e.g. account transactions, etc.",
+    "dynamic.cookies" : "the use of web page cookies for various " +
+      "purposes, e.g. session identifiers and status information"
+  }
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/content/share.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,291 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is dashboard_overlay.
+ *
+ * The Initial Developer of the Original Code is
+ *   Dave Raggett.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your vdashboardersion of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ * 
+ * ***** END LICENSE BLOCK ***** */
+
+// #### FIX date hack on line 98 ####
+
+dashboard_overlay.register_share({
+  start_up: function (context)
+  {
+    if (context.window_count == 0)
+    {
+      // Get the "extensions.myext." branch
+      var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                    .getService(Components.interfaces.nsIPrefService);
+      prefs = prefs.getBranch("extensions.privacydashboard.");
+
+      let sharing = false;
+
+      // allow for undefined preference
+
+      try {
+        sharing = prefs.getBoolPref("share.enabled");
+      } catch (e) {  }
+
+      this.set_mode(sharing);
+    }
+  },
+
+  set_mode: function (mode)
+  {
+    // Get the "extensions.myext." branch
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                  .getService(Components.interfaces.nsIPrefService);
+    prefs = prefs.getBranch("extensions.privacydashboard.");
+
+    prefs.setBoolPref("share.enabled", mode);
+
+    if (!mode)
+    {
+      if (this.sharing)
+      {
+        try {
+          clearInterval(this.interval_id);
+        } catch (e) { }
+      }
+
+      this.sharing = false;
+    }
+    else
+    {
+      var self = this;
+      self.sharing = true;
+
+      // allow for very long lived browser sessions
+      // test every hour as setInterval treats very
+      // long delay e.g. 28 days as zero :(
+      self.interval_id = setInterval(function ()
+      {
+        self.check_date();
+      }, 60*60*1000);
+    }
+  },
+
+  check_date: function ()
+  {
+    // Get the "extensions.myext." branch
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                  .getService(Components.interfaces.nsIPrefService);
+    prefs = prefs.getBranch("extensions.privacydashboard.");
+
+    var share_interval = prefs.getIntPref("share.interval"); // in days
+    var last_shared = prefs.getCharPref("share.last"); // parsable date
+
+    let now =  Date.now();
+
+    if (last_shared == "")
+      last_shared = now;
+    else
+      last_shared = Date.parse(last_shared);
+
+    var self = this;
+    let gap = Date.now() - last_shared;  // in milliseconds
+    share_interval *= 24*60*60*1000;  // days to milliseconds
+
+    var postbox = {};
+    postbox.delivered = function (table)
+    {
+    };
+
+    //if (gap > share_interval)
+    {
+      // wait 5 seconds and start upload
+      setTimeout(function ()
+      {
+        self.share_data(prefs, last_shared, postbox);
+      }, 1000);
+    }
+  },
+
+  // called after user has activated database clear or compact
+  // menu actions and wants to share the date before clearing.
+  // a postbox object is used to ensure that the clear operation
+  // only takes place when all three tables have been delivered
+  share_and_clear: function (all)
+  {
+    // Get the "extensions.myext." branch
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                  .getService(Components.interfaces.nsIPrefService);
+    prefs = prefs.getBranch("extensions.privacydashboard.");
+
+    var last_shared = prefs.getCharPref("share.last"); // parsable date
+
+    let now =  Date.now();
+
+    if (last_shared == "")
+      last_shared = now;
+    else
+      last_shared = Date.parse(last_shared);
+
+    var postbox = {};
+    postbox.table_count = 3; // number of tables to deliver
+    postbox.delivered = function (table)
+    {
+      if (--this.table_count == 0)
+        dashboard_overlay.database.reset(all);
+    };
+    
+    this.share_data(prefs, last_shared, postbox);
+  },
+
+  // SQLite date format "2010-11-23 18:39:46"
+  share_data: function (prefs, last, postbox)
+  {
+    postbox.to = prefs.getCharPref("share.with");
+
+    if (!postbox.to)
+      return;
+
+    console.log("sharing data with " + postbox.to);
+    prefs.setCharPref("share.last", Date() + "");
+
+    let date = this.get_date(/*last*/ "23 November 2010 18:39:47");
+
+    this.share_table("relations",
+        ["parent", "child", "offsite"], date, postbox);
+    this.share_table("parties",
+        ["page_host", "third_party", "offsite"], date, postbox);
+    this.share_table("site_info", 
+        ["host",
+         "visited",
+         "session_cookies", 
+         "lasting_cookies",
+         "flash_cookies",
+         "int_3rd_parties",
+         "ext_3rd_parties",
+         "int_3rd_party_session_cookies",
+         "int_3rd_party_lasting_cookies",
+         "int_3rd_party_flash_cookies",
+         "ext_3rd_party_session_cookies",
+         "ext_3rd_party_lasting_cookies",
+         "ext_3rd_party_flash_cookies",
+         "dom_storage",
+         "html5_pings",
+         "invisible_images",
+         "suspicious_urls",
+         "geo_permission",
+         "p3p"], date, postbox);
+
+  },
+
+  share_table: function (table, col_names, date, postbox)
+  {
+    let sql = "SELECT " + this.column_list(col_names) + " FROM " + table +
+              " WHERE time > '" + date +"'";
+ 
+    var self = this;
+    let database = dashboard_overlay.database;
+    let handler = 
+    {
+      result: "",
+
+      process_row: function (row)
+      {
+        let names = col_names;
+
+        for (let i = 0; i < names.length; ++i)
+        {
+          let delim = "";
+          let value = row.getResultByName(names[i]);
+
+          if (typeof(value) == "string")
+            delim = "'";
+
+          this.result += (i ? "," : "") + delim + value + delim;
+        }
+
+        this.result += "\n";
+      },
+
+      finalize: function ()
+      {
+        self.deliver(table, this.result, postbox);
+      }
+    };
+
+    database.select(sql, database.db, handler);
+  },
+
+  // include crc32 checksum as a very basic
+  // protection against spoofed data uploads
+  deliver: function (table, data, postbox)
+  {
+    var req = new XMLHttpRequest();
+    var self = this;
+
+    req.open("POST", postbox.to, true);
+    req.setRequestHeader("Content-Type", "text/plain");
+    req.setRequestHeader("X-Data-Check", this.adler32(data));
+    req.setRequestHeader("Dashboard-Table", table);
+    req.onreadystatechange = function (evt)
+    {
+      if (req.readyState == 4)
+        postbox.delivered(table); // notify delivery
+    }
+    req.send(data);
+  },
+
+  column_list: function (list)
+  {
+    let names = "";
+
+    for (let i = 0; i < list.length; ++i)
+      names += (i ? ", " : "") + list[i];
+
+    return names;
+  },
+
+  get_date: function (date)
+  {
+    date = new Date(date);
+    return date.getFullYear() + "-" + (1+date.getMonth()) + "-" +
+           date.getDate() + " " + date.getHours() + ":" +
+           date.getMinutes() + ":" + date.getSeconds();
+  },
+
+  adler32: function (str)
+  {
+    let a = 1, b = 0;
+
+    for (let i = 0; i < str.length; ++i)
+    {
+      a = (a + str.charCodeAt(i)) % 65521;
+      b = (b + a) % 65521;
+    }
+
+    return a + b * 65536;
+  }
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/locale/en-US/dashboard.dtd	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,219 @@
+<!-- overlay.xul -->
+<!ENTITY dashboard.menu.bar.label "Dashboard notification bar">
+<!ENTITY dashboard.menu.dialog.label "Privacy Dashboard">
+<!ENTITY dashboard.menu.compact.label "Compact Dashboard database">
+<!ENTITY dashboard.menu.clear.label "Clear Dashboard database ">
+
+<!ENTITY dashboard.toolbar.label "Privacy">
+<!ENTITY dashboard.toolbar.tooltip "Privacy assistant">
+
+<!-- dashboard.xul -->
+
+<!ENTITY dashboard.dialog.title "Privacy Dashboard">
+
+<!ENTITY dashboard.dialog.datatrack.title "Data Track">
+<!ENTITY dashboard.dialog.location.title "Location">
+<!ENTITY dashboard.dialog.current.title "Current Website">
+<!ENTITY dashboard.dialog.share.title "Share Findings">
+<!ENTITY dashboard.dialog.about.title "About">
+
+<!ENTITY dashboard.dialog.datatrack.tooltip "Explore what information has been collected and how">
+<!ENTITY dashboard.dialog.location.tooltip "See if your browser knows where you are">
+<!ENTITY dashboard.dialog.current.tooltip "View information on the current website and adjust its settings">
+<!ENTITY dashboard.dialog.share.tooltip "How to share your findings with others">
+<!ENTITY dashboard.dialog.about.tooltip "Further information on the Privacy Dashboard">
+
+<!ENTITY dashboard.dialog.datatrack.heading "What are websites collecting and how?">
+<!ENTITY dashboard.dialog.datatrack.descr
+ "See what data is being collected by the websites you visit, what cookies they set, and more.">
+<!ENTITY dashboard.dialog.datatrack.select.label "1. Select query">
+<!ENTITY dashboard.dialog.datatrack.select.tooltip "step 1 - select a query">
+<!ENTITY dashboard.dialog.datatrack.input.label "2. Website or URL">
+<!ENTITY dashboard.dialog.datatrack.input.tooltip
+ "step 2 - when needed provide a search key, e.g. www.example.com">
+<!ENTITY dashboard.dialog.datatrack.button.label "3. Execute query">
+<!ENTITY dashboard.dialog.datatrack.button.tooltip
+ "step 3 - click the button or press the Enter key to execute the query">
+<!ENTITY dashboard.dialog.datatrack.table "Query results appear below -- click on values to set the query parameter above">
+<!ENTITY dashboard.dialog.datatrack.backward.tooltip "go back one query">
+<!ENTITY dashboard.dialog.datatrack.forward.tooltip "go forward one query">
+
+<!ENTITY dashboard.dialog.datatrack.item0 "Which websites use long lasting cookies?">
+<!ENTITY dashboard.dialog.datatrack.item1 "Which websites use session cookies?">
+<!ENTITY dashboard.dialog.datatrack.item2 "Which websites use flash cookies?">
+<!ENTITY dashboard.dialog.datatrack.item3 "Which websites use DOM storage?">
+<!ENTITY dashboard.dialog.datatrack.item4 "Which websites use invisible images?">
+<!ENTITY dashboard.dialog.datatrack.item5 "Which websites use HTML5 ping attributes?">
+
+<!ENTITY dashboard.dialog.datatrack.item6 "Which websites provide P3P privacy policies?">
+<!ENTITY dashboard.dialog.datatrack.item7 "Which websites are 3rd parties?">
+<!ENTITY dashboard.dialog.datatrack.item8 "Which websites use a given 3rd party?">
+
+<!ENTITY dashboard.dialog.datatrack.item9 "Which internal 3rd parties are used by a given website?">
+<!ENTITY dashboard.dialog.datatrack.item10 "Which external 3rd parties are used by a given website?">
+<!ENTITY dashboard.dialog.datatrack.item11 "What http cookies are used by a given website?">
+<!ENTITY dashboard.dialog.datatrack.item12 "Which websites have permission to access my location?">
+
+<!ENTITY dashboard.dialog.datatrack.item13 "What data has been sent to a given website?">
+<!ENTITY dashboard.dialog.datatrack.item14 "Which websites a given datum value has been sent to?">
+<!ENTITY dashboard.dialog.datatrack.item15 "Which websites a given datum name has been sent to?">
+<!ENTITY dashboard.dialog.datatrack.item16 "Which datum names are used for a given value?">
+
+
+<!ENTITY dashboard.dialog.location.map.label "show your location">
+<!ENTITY dashboard.dialog.location.map.key "L">
+<!ENTITY dashboard.dialog.location.map.tooltip "if possible, show map of your current location">
+
+<!ENTITY dashboard.dialog.location.sites.label "show list of sites with permission to access your location">
+<!ENTITY dashboard.dialog.location.sites.key "P">
+<!ENTITY dashboard.dialog.location.sites.tooltip "makes it easy to revoke geolocation permissions">
+
+<!ENTITY dashboard.dialog.location.heading "Your geographic location">
+<!ENTITY dashboard.dialog.location.descr
+ "Websites may request access to your geographic location. Firefox determines this based upon the list of WiFi access points that your computer can see, using a trusted third party location service. Use the radio buttons below to switch between a map of your current location and the list of the sites for which you have given permission to access your location without asking you again.">
+
+<!ENTITY dashboard.dialog.current.heading "Information about the current website">
+<!ENTITY dashboard.dialog.cursite.unavailable "Sorry: current host is unavailable">
+<!ENTITY dashboard.dialog.cursite.site_has "This website has">
+<!ENTITY dashboard.dialog.cursite.prefs "Your preferences for this website">
+
+<!ENTITY dashboard.dialog.cursite.descr
+ "Review and adjust privacy options for the current website, including, cookies, P3P and more. You can find out more about this website using the queries on the 'Data Track' tab above.">
+
+<!ENTITY dashboard.dialog.cursite.simple.carefree.label "Carefree">
+<!ENTITY dashboard.dialog.cursite.simple.carefree.key "C">
+<!ENTITY dashboard.dialog.cursite.simple.carefree.tooltip "I'm not worried, bring it on...">
+<!ENTITY dashboard.dialog.cursite.simple.thoughtful.label "Thoughtful">
+<!ENTITY dashboard.dialog.cursite.simple.thoughtful.key "T">
+<!ENTITY dashboard.dialog.cursite.simple.thoughtful.tooltip "Give me some protection against tracking">
+<!ENTITY dashboard.dialog.cursite.simple.paranoid.label "Paranoid">
+<!ENTITY dashboard.dialog.cursite.simple.paranoid.key "P">
+<!ENTITY dashboard.dialog.cursite.simple.paranoid.tooltip "Protect my privacy even if webpages don't work as well">
+
+<!ENTITY dashboard.dialog.cursite.button.complicate "Advanced View">
+
+<!ENTITY dashboard.dialog.cursite.prefs_never_block.tooltip 
+ "Use this for third party sites that you don't want to block">
+<!ENTITY dashboard.dialog.cursite.prefs_never_block.label
+ "Never block content from this site">
+
+<!ENTITY dashboard.dialog.cursite.prefs_do_not_track.tooltip 
+ "See FTC submission paper: http://goo.gl/1s7Kp">
+<!ENTITY dashboard.dialog.cursite.prefs_do_not_track.label
+ "Send 'do not track' HTTP headers">
+
+<!ENTITY dashboard.dialog.cursite.prefs_ext_3rd_parties.tooltip 
+ "Block content from third party sites, e.g. advertisements">
+<!ENTITY dashboard.dialog.cursite.prefs_ext_3rd_parties.label
+ "Block external 3rd parties">
+
+<!ENTITY dashboard.dialog.cursite.prefs_ext_3rd_cookies.tooltip
+ "Makes it hard for third party sites to tailor content to you">
+<!ENTITY dashboard.dialog.cursite.prefs_ext_3rd_cookies.label
+ "Block external 3rd party cookies">
+
+<!ENTITY dashboard.dialog.cursite.prefs_all_lasting_cookies.tooltip 
+ "Think carefully before setting this, as it may impair your browsing experience">
+<!ENTITY dashboard.dialog.cursite.prefs_all_lasting_cookies.label
+ "Block all lasting cookies">
+
+<!ENTITY dashboard.dialog.cursite.prefs_clear_flash_cookies.tooltip 
+ "Think carefully before setting this, as it may impair your browsing experience">
+<!ENTITY dashboard.dialog.cursite.prefs_clear_flash_cookies.label
+ "Clear flash cookies">
+
+<!ENTITY dashboard.dialog.cursite.prefs_block_scripting.tooltip 
+ "Think carefully before setting this, as it may impair your browsing experience. This won't take effect until the page is reloaded">
+<!ENTITY dashboard.dialog.cursite.prefs_block_scripting.label
+ "Disable web page scripting">
+
+<!ENTITY dashboard.dialog.cursite.prefs_block_geolocation.tooltip 
+ "This prevents websites from accessing your geolocation">
+<!ENTITY dashboard.dialog.cursite.prefs_block_geolocation.label
+ "Disable access to your geolocation">
+
+<!ENTITY dashboard.dialog.cursite.prefs_disable_html5_pings.tooltip 
+ "Don't send pings to sites listed in ping attributes when following links">
+<!ENTITY dashboard.dialog.cursite.prefs_disable_html5_pings.label
+ "Disable HTML5 pings">
+
+<!ENTITY dashboard.dialog.cursite.prefs_disable_referrer.tooltip 
+ "Hide current web page address when sending requests for new pages or page content">
+<!ENTITY dashboard.dialog.cursite.prefs_disable_referrer.label
+ "Don't send HTTP referrer header">
+
+<!ENTITY dashboard.dialog.cursite.prefs_disable_dom_storage.tooltip 
+ "Don't allow a web page to store information on your computer">
+<!ENTITY dashboard.dialog.cursite.prefs_disable_dom_storage.label
+ "Disable web page access to DOM storage">
+
+<!ENTITY dashboard.dialog.cursite.prefs_disable_flash.tooltip 
+ "Don't allow a web page to load Flash and Shockwave content">
+<!ENTITY dashboard.dialog.cursite.prefs_disable_flash.label
+ "Disable Flash and Shockwave">
+
+<!ENTITY dashboard.dialog.cursite.prefs_disable_java.tooltip 
+ "Don't allow a web page to load Java based content">
+<!ENTITY dashboard.dialog.cursite.prefs_disable_java.label
+ "Disable Java">
+
+<!ENTITY dashboard.dialog.cursite.button.simplify "Simple View">
+<!ENTITY dashboard.dialog.cursite.default.label
+ "Use these by default for all websites">
+<!ENTITY dashboard.dialog.cursite.default.tooltip
+ "sets the default preferences for sites for which you haven't set overrides">
+
+<!ENTITY dashboard.dialog.cursite.test
+ "You can use the following buttons to check the current website in various ways">
+
+<!ENTITY dashboard.dialog.cursite.test.norton.label
+ "Check site with Norton SafeWeb...">
+<!ENTITY dashboard.dialog.cursite.test.freetrust.label
+ "Check site with Free Trust Seal...">
+<!ENTITY dashboard.dialog.cursite.test.truste.label
+ "Check site with TRUSTe...">
+
+<!-- share table info -->
+
+<!ENTITY dashboard.dialog.share.heading
+ "How to share your findings with others">
+
+<!ENTITY dashboard.dialog.share.p1
+ "The data collected by the Dashboard as you browse gives a view about a small part of the Web. By pooling data from many users it will be possible it build up a much more detailed picture of how sites are tracking users. If you
+would like to help with this effort please ensure the checkbox below is activated. The anonymized data will be periodically uploaded to the designated server. The uploaded data covers the properties of each site, e.g. whether it has third parties which set lasting cookies, but doesn't include anything that could be used to identify you.">
+
+<!ENTITY dashboard.dialog.share.checkbox "Share your findings with">
+
+<!ENTITY dashboard.dialog.share.server.tooltip "The URL for the uploaded data">
+
+<!ENTITY dashboard.dialog.share.p2
+ "Many thanks for helping to make the Web a bit more transparent!">
+
+<!-- about tab info -->
+
+<!ENTITY dashboard.dialog.about.heading
+ "PrimeLife Privacy Dashboard">
+
+<!ENTITY dashboard.dialog.about.p1
+ "This is a Firefox extension that has been designed to help you understand what personal information is being collected by websites, and to provide you with a means to control this on a per website basis. The Dashboard installs an icon on the navigation toolbar that changes according to the current website:">
+
+<!ENTITY dashboard.dialog.about.heading
+ "PrimeLife Privacy Dashboard">
+
+<!ENTITY dashboard.dialog.about.p2
+ "The Dashboard alerts you the first time you visit a website (except for nicely behaved ones) giving you the opportunity to set your preferences. Thereafter, you can review these by clicking the Dashboard icon when visiting the website. The 'Data Track' tab allows you to make a range of queries about sites. The Location tab deals with your physical (geo)location. The Current Website tab provides information on the current site and allows you to review and update your preferences. Note that the Dashboard's assessment of a website provides no guarantees as to the privacy friendliness of that site. You are advised to check the site's privacy policy.">
+
+
+<!ENTITY dashboard.dialog.about.p3
+ "is a pan-european research project focusing on bringing sustainable privacy and identity management to future networks and services. The Privacy Dashboard has been developed with funding from the European Union's 7th Framework Programme.">
+
+<!ENTITY dashboard.dialog.about.p4
+ "Further information is available on the">
+<!ENTITY dashboard.dialog.about.link2
+ "Dashboard website">
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/locale/en-US/dashboard.dtd~	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,25 @@
+<!ENTITY dashboard.label "Privacy Dashboard">
+<!ENTITY dashboard.toolbar.label "Privacy">
+<!ENTITY dashboard.toolbar.tooltip "Privacy assistant">
+
+<!ENTITY dashboard.title "Privacy Dashboard">
+
+<!ENTITY dashboard.sidebar.title "Privacy Dashboard">
+<!ENTITY dashboard.openSidebar.commandkey "E">
+<!ENTITY dashboard.openSidebar.modifierskey "shift accel">
+
+<!ENTITY dashboard.sidebar.siteinfo.label "Page Information">
+<!ENTITY dashboard.sidebar.siteinfo.tooltip "Page Information">
+
+<!ENTITY dashboard.sidebar.prefs.label "Privacy Preferences">
+<!ENTITY dashboard.sidebar.prefs.tooltip "Privacy Preferences">
+
+<!ENTITY dashboard.sidebar.about.label "About Privacy Dashboard">
+<!ENTITY dashboard.sidebar.about.tooltip "About Privacy Dashboard">
+
+<!ENTITY dashboard.sidebar.about.title "About">
+<!ENTITY dashboard.sidebar.prefs.title "Preferences">
+<!ENTITY dashboard.sidebar.datatrack.title "Data track">
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/locale/en-US/dashboard.properties	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,147 @@
+extensions.dashboard.description=A privacy assistant
+
+### overlay.js
+
+# notification bar label
+overlay.notification.label=Privacy alert: What do you want to do with
+
+# nofication bar button labels
+overlay.notification.accept.label=Accept always
+overlay.notification.protect.label=Protect me
+overlay.notification.more.label=Tell me more
+
+# access keys for button labels
+overlay.notification.accept.key=A
+overlay.notification.protect.key=P
+overlay.notification.more.key=M
+
+# pop up confirmation dialog
+overlay.clear_data.title=Clear Privacy Dashboard Database
+overlay.compact_data.message=Are you sure you really want to compact data held in the dashboard database? This clears information collected on relationships between sites, and the data you've submitted in forms, but retains the per site preferences and feature counts.
+overlay.clear_data.message=Are you sure you really want to clear ALL data held in the dashboard database including the per site preferences and feature counts?
+overlay.save_data.message=Upload data for sharing before saving?
+
+### dashboard.js
+
+dashboard.loc1=Your location is latitude
+dashboard.loc2=longitude
+dashboard.loc3=Sorry: your location is currently unavailable
+dashboard.loc4=permission denied
+dashboard.loc5=timeout
+
+dashboard.loc6=If your computer is currently connected to the Internet via a wired connection, you could try switching to a WiFi access point, but note that the location would only be available if Google or its affiliates have driven along a nearby road and were able to detect the access points your computer sees.
+
+dashboard.loc7=Use the checkboxes to revoke the access permission for a site, which then disappear from the list
+dashboard.loc8=There are no sites with permission to access your location
+dashboard.loc9=allow
+dashboard.loc10=deny
+
+dashboard.query1=2. Domain name or URL
+dashboard.query2=2. Datum value
+dashboard.query3=2. Domain name or URL
+dashboard.query4=2. Datum value
+
+# note that the data track table column names aren't localized (this would be hard)
+
+### access.js
+
+access.site=This website has
+
+access.number.zero=No
+access.number.one=One
+
+access.a=a
+
+access.caption.redirect=was redirected by
+access.tooltip.redirect="This is commonly done by search engines, but may be done by websites for administrative purposes"
+
+access.caption.p3p=P3P policy
+access.tooltip.p3p=Click to view policy
+
+access.caption.geolocation=Permission to access your location
+access.tooltip.geolocation=You previously granted this website permission to access your location on a permanent basis without asking you again
+
+access.caption.cookies.session1=a session cookie
+access.caption.cookies.session=session cookies
+access.caption.cookies.lasting1=a lasting cookie
+access.caption.cookies.lasting=lasting cookies
+access.caption.cookies.flash1=a flash cookie
+access.caption.cookies.flash=flash cookies
+
+access.caption.internal.third.parties1=an internal third party site
+access.caption.internal.third.parties=internal third party sites
+
+access.caption.external.third.parties1=an external third party site
+access.caption.external.third.parties=external third party sites
+
+access.caption.internal.third.parties.cookies.session1=an internal third party session cookie
+access.caption.internal.third.parties.cookies.session=internal third party session cookies
+
+access.caption.external.third.parties.cookies.session1=an external third party session cookie
+access.caption.external.third.parties.cookies.session=external third party session cookies
+
+access.caption.internal.third.parties.cookies.lasting1=an internal third party lasting cookie
+access.caption.internal.third.parties.cookies.lasting=internal third party lasting cookies
+
+access.caption.external.third.parties.cookies.lasting1=an external third party lasting cookie
+access.caption.external.third.parties.cookies.lasting=external third party lasting cookies
+
+access.caption.internal.third.parties.cookies.flash1=an internal third party flash cookie
+access.caption.internal.third.parties.cookies.flash=internal third party flash cookies
+
+access.caption.external.third.parties.cookies.flash1=an external third party flash cookie
+access.caption.external.third.parties.cookies.flash=external third party flash cookies
+
+
+access.caption.dom_storage1=a DOM storage object
+access.caption.dom_storage=DOM storage objects
+
+access.caption.ping1=HTML5 ping attribute
+access.caption.ping=HTML5 ping attributes
+
+access.caption.images1=an invisible image
+access.caption.images=invisible images
+
+access.caption.bad_urls1=a suspicious url
+access.caption.bad_urls=suspicious urls
+
+access.tooltip.ping=These are used by sites to track when you click on a link that takes you to another site, and avoids the need for them to use HTTP redirection and speeds page load times
+
+access.tooltip.images=Hidden images may sometimes be used to track you
+
+access.tooltip.bad_urls=Some sites embed tracking information in URLs
+
+access.tooltip.dom_storage=Objects saved locally that are an alternative to cookies, and may be used to track you
+
+access.tooltip.cookies.session=Sites use session cookies for a variety of purposes, including recording that you are logged into the current session, and the contents of your shopping basket. Many sites won't work if you block session cookies.
+
+access.tooltip.cookies.lasting=Lasting cookies are often used to remember your site preferences and where you were when you left off viewing a video. Blocking lasting cookies is likely to impair your browser experience.
+
+access.tooltip.cookies.flash=Flash cookies are not subject to your browser's cookie settings and are held indefinitely. They are often used to record the state of flash applications such as video players. Blocking flash cookies is likely to impair your browsing experience.
+
+
+access.tooltip.internal.third.parties=These are servers with the same base domain as that of the primary website. It is common practice to split different types of content across different servers to improve the overal performance.
+
+access.tooltip.internal.third.parties.cookies.session=These are session cookies set by internal third parties and intended to support the end-user experience during a browser session.
+
+access.tooltip.internal.third.parties.cookies.lasting=These are lasting cookies set by internal third parties and analogous to those set by the primary website.
+
+access.tooltip.internal.third.parties.cookies.flash=Flash cookies are not subject to your browser's cookie settings and are held indefinitely. These are set by internal third parties and analogous to those set by the primary website.
+
+
+access.tooltip.external.third.parties=Websites may include content from third parties for advertising and other services such as maps, search and so forth. Popular script libraries may be loaded from a third party like google to speedup page loading by increasing the chances that the script will already be in the brower's cache. You can try blocking external 3rd parties to see how the website is effected.
+
+access.tooltip.external.third.parties.cookies.session=Third parties can use session cookies to track your behavior across the Web during a single browser session with a view to providing advertisements that are targeted at people like yourself. These cookies can usually be blocked without harming your browsing experience.
+
+access.tooltip.external.third.parties.cookies.lasting=Third parties can use lasting cookies to track your behavior across the Web with a view to providing advertisements that are targeted at people like yourself. These cookies can usually be blocked without harming your browsing experience.
+
+access.tooltip.external.third.parties.cookies.flash=Flash cookies are not subject to your browser's cookie settings and are held indefinitely. Third parties may use them to record the state of  embedded flash applications such as video players. Blocking flash cookies is likely to impair your browsing experience, but try it and see how this website is effected.
+
+### p3p_render.js
+
+p3p_render.comment=click content or press "Esc" to revert to site preferences info
+
+
+# I still need to make the static text for rendering P3P policies localizable!
+
+
Binary file dashboard/chrome/skin/common/cat-globe.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/skin/common/dashboard.css	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,151 @@
+/* dashboard dialog window */
+
+#dashboard_current.normal #p3p_policy { visibility: hidden; display: none; }
+
+#dashboard_current.policy #site_info { visibility: hidden; display: none; }
+
+tabs { margin-bottom: 2px; }
+tabpanels { padding: 0; margin: 0 }
+tabpanel { padding-bottom: 6px }
+
+tabbox, tabpanel, splitter { background-image: url("chrome://dashboard/skin/texture.jpg"); }
+
+#dashboard_share p { width: 600px }
+#dashboard_share textbox { width: 350px }
+
+#dashboard_about p { width: 600px }
+#dashboard_about img { margin-right: 20px }
+#dashboard_location p { width: 600px }
+#dashboard_location img { display: block; width: 600px; height: 380px }
+#dashboard_current p { width: 600px }
+#dashboard_current .column p { width: 300px }
+#dashboard_prefs p { width: 600px }
+#dashboard_datatrack description { width: 200px }
+#sitelist p { width: 600px }
+
+#dashboard_current h3 {
+   margin-top: 0px;
+   padding-top: 0px;
+   padding-bottom: 0px;
+   margin-bottom: 0px;
+}
+
+#dashboard_current h2 {
+   margin-top: 0px;
+   padding-top: 0px;
+   padding-bottom: 0px;
+   margin-bottom: 0px;
+}
+
+#dashboard_location { padding: 10px }
+
+#dashboard_location.show_map #site_list { display: none; visibility: hidden }
+#dashboard_location.show_sites #your_location  { display: none; visibility: hidden }
+
+.hidden { visibility: hidden }
+
+#location_mode { display: -moz-inline-box; display: inline-block }
+#location_mode radio { margin-left: 2em; }
+#site_list checkbox { display: block; }
+
+/* make xul controls focusable for accessibility */
+button, checkbox, radio, menulist, #p3p_policy { -moz-user-focus: normal; }
+
+tab:focus { text-decoration: underline; color: navy; }
+button:focus { text-decoration: underline; color: navy;  }
+checkbox:focus { text-decoration: underline;  color: navy; }
+radio:focus { text-decoration: underline;  color: navy; }
+menulist:focus { color: navy; }
+a:focus { text-decoration: underline; }
+
+#dashboard_datatrack splitter { padding-left:8px;padding-right:8px; }
+
+#sitelist.hidden { display: none; visibility: hidden; }
+
+.bullet:before { content: "▪"; padding-right: 1em; }
+.bullet { margin-left: 2em; }
+
+listcol { max-width: 30% }
+
+button.inactive { color: #AAA }
+
+#backward {
+  min-width: 24px !important;
+  margin-left: 0px;
+  margin-right:0px;
+  padding-right: 3px;
+}
+
+#forward {
+  min-width: 24px !important;
+  margin-left:0px;
+  margin-right:0px;
+  padding-left: 3px;
+}
+
+textbox { padding-top: 0px; padding-bottom: 2px }
+
+/* pseudo magic for styling tree control for query results */
+treechildren { overflow: auto }
+tree,treecols { background: #CCC }
+treechildren::-moz-tree-row {
+  border: 1px solid white;
+  min-height: 25px;
+  height: 1.3em;
+  padding-left: 4px;
+}
+treechildren::-moz-tree-column {
+  border: 1px solid #888;
+  background-color: #DDD;
+}
+treecol { font-weight: bold }
+treechildren::-moz-tree-cell-text { color: black }
+treechildren::-moz-tree-row(even) { background: rgba(252,252,255,0.7)!important }
+treechildren::-moz-tree-row(odd)  { background: rgba(254,252,255,0.6)!important }
+treechildren::-moz-tree-row(selected) { background:rgba(200,200,255,0.5)!important }
+
+#curstatus li { margin-bottom: 0.5em }
+#curhost {
+ background-image: url("chrome://dashboard/skin/glasses-cool.png");
+ background-repeat: no-repeat;
+ background-position: 10px 2px;
+ padding-left: 40px;
+ line-height: 32px;
+}
+
+#dashboard_about ul {
+  margin: 0px;
+  padding-left: 20px;
+}
+#dashboard_about li {
+ list-style: none;
+ background-repeat: no-repeat;
+ background-position: 5px 0.3em;
+ line-height: 32px;
+ margin: 0.1em 0em 0.3em 0;
+ padding:  0 0 0 40px;
+}
+
+li img {
+  margin-left: 0.5em;
+  vertical-align: middle;
+  cursor: pointer;
+}
+button, radio, checkbox { cursor: pointer; }
+#context_help_box {
+  width: 40em;
+  height: auto;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+
+ul.long li { font-weight: bold }
+ul.long li span { font-weight: normal }
+
+a {
+  text-decoration: underline;
+  color: blue;
+  cursor: pointer;
+}
+
+a:hover { color: #44F }
Binary file dashboard/chrome/skin/common/dashboard.ico has changed
Binary file dashboard/chrome/skin/common/dashboard.png has changed
Binary file dashboard/chrome/skin/common/disappointed.png has changed
Binary file dashboard/chrome/skin/common/glasses-cool.png has changed
Binary file dashboard/chrome/skin/common/head.png has changed
Binary file dashboard/chrome/skin/common/help.png has changed
Binary file dashboard/chrome/skin/common/icon.png has changed
Binary file dashboard/chrome/skin/common/left.gif has changed
Binary file dashboard/chrome/skin/common/logo.png has changed
Binary file dashboard/chrome/skin/common/mad-tongue.png has changed
Binary file dashboard/chrome/skin/common/right.gif has changed
Binary file dashboard/chrome/skin/common/texture.jpg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/skin/common/toolbar-button.css	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,6 @@
+#dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
Binary file dashboard/chrome/skin/mac/disappointed.png has changed
Binary file dashboard/chrome/skin/mac/glasses-cool.png has changed
Binary file dashboard/chrome/skin/mac/icon.png has changed
Binary file dashboard/chrome/skin/mac/mad-tongue.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/skin/mac/overlay.css	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,13 @@
+#dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+/* need 16x16 version of button icon for this */
+toolbar[iconsize="small"] #dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 40px 16px 24px);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/skin/mac/toolbar-button.css	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,7 @@
+#dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+
Binary file dashboard/chrome/skin/win/disappointed.png has changed
Binary file dashboard/chrome/skin/win/glasses-cool.png has changed
Binary file dashboard/chrome/skin/win/icon.png has changed
Binary file dashboard/chrome/skin/win/mad-tongue.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/skin/win/overlay.css	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,13 @@
+#dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+/* need 16x16 version of button icon for this */
+toolbar[iconsize="small"] #dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 40px 16px 24px);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/chrome/skin/win/toolbar-button.css	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,6 @@
+#dashboard-toolbar-button
+{
+  list-style-image: url("chrome://dashboard/skin/glasses-cool.png");
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
Binary file dashboard/dashboard.xpi has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/defaults/preferences/dashboard.js	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,6 @@
+// See https://developer.mozilla.org/en/Localizing_extension_descriptions
+pref("[email protected]", "chrome://dashboard/locale/dashboard.properties");
+pref("extensions.privacydashboard.share.with", "http://primelife.eu/dashboard-data");
+pref("extensions.privacydashboard.share.interval", 28);
+pref("extensions.privacydashboard.share.last", "");
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/install.rdf	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>[email protected]</em:id>
+    <em:version>0.9.2</em:version>
+    <em:localized>
+      <Description> <!-- example localization via google translate -->
+        <em:locale>fr-FR</em:locale>
+        <em:name>Confidentialité Dashboard</em:name>
+        <em:creator>Dave Raggett</em:creator>
+       <em:description>Un assistant de la vie privée mis au point par le consortium PrimeLife</em:description>
+      </Description>
+    </em:localized>
+    <em:name>Privacy Dashboard</em:name>
+    <em:creator>Dave Raggett</em:creator>
+    <em:description>A privacy assistant developed by the PrimeLife consortium</em:description>
+    <em:iconURL>chrome://dashboard-common/skin/cat-globe.png</em:iconURL>
+    <em:homepageURL>http://www.primelife.eu/</em:homepageURL>
+    <em:optionsURL>chrome://dashboard/content/dashboard.xul</em:optionsURL>
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox -->
+        <em:minVersion>3.0.3</em:minVersion>
+        <em:maxVersion>4.4.*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dashboard/readme.txt	Thu Apr 28 17:55:08 2011 +0100
@@ -0,0 +1,29 @@
+PrimeLife Privacy Dashboard:
+
+This extension collects information on how websites track us as we
+use the browser, e.g. the use of persistent cookies by third parties
+such as advertising sites. You can set per site preferences, e.g. to
+block third party cookies and content or even to disable scripting
+on a per site basis. The extension also supports sharing of the data
+across users to build up a detail landscape of the relationships
+between sites when it comes to tracking end-users.
+
+The original developer of the Privacy Dashboard is:
+
+   Dave Raggett <[email protected]>
+
+The Privacy Dashboard has been developed with funding for the
+PrimeLife project from the European Union's 7th Framework Programme.
+For more details, see: http://primelife.eu/
+
+This is open source software and available under one of three
+licenses: MPL 1.1, GPL 2.0 or LGPL 2.1
+
+The smiley icons are from the open source chat program: Pidgin
+
+To rebuild the Extension XPI file, see the shell script: build.sh
+
+For more information see:  http://code.w3.org/privacy-dashboard/
+
+
+