bug 490189 - Fennec should offer to be the default browser on Windows Mobile r=mfinkle a=blocking-fennec
authorAlex Pakhotin <alexp@mozilla.com>
Wed, 18 Nov 2009 13:09:01 -0800
changeset 65874 465a146c832bfde0fb3b8f05701343e12b0d6e35
parent 65873 c0bae9c97d38af3cd75dc318e9e483c77261e22a
child 65875 b94c57979259b5b55b896095052b572217293385
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, blocking-fennec
bugs490189
bug 490189 - Fennec should offer to be the default browser on Windows Mobile r=mfinkle a=blocking-fennec
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.xul
mobile/chrome/content/preferences.js
mobile/chrome/jar.mn
mobile/components/phone/nsIPhoneSupport.idl
mobile/components/phone/nsPhoneSupport.cpp
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -617,16 +617,28 @@ var BrowserUI = {
   
   switchTask: function switchTask() {
     try {
       let phone = Cc["@mozilla.org/phone/support;1"].createInstance(Ci.nsIPhoneSupport);
       phone.switchTask();
     } catch(e) { }
   },
   
+#ifdef WINCE
+  updateDefaultBrowser: function updateDefaultBrowser(aSet) {
+    try {
+      let phone = Cc["@mozilla.org/phone/support;1"].getService(Ci.nsIPhoneSupport);
+      if (aSet)
+        phone.setDefaultBrowser();
+      else
+        phone.restoreDefaultBrowser();
+    } catch(e) { }
+  },
+#endif
+  
   handleEvent: function (aEvent) {
     switch (aEvent.type) {
       // Browser events
       case "DOMWillOpenModalDialog":
         this._domWillOpenModalDialog(aEvent);
         break;
       case "DOMTitleChanged":
         this._titleChanged(aEvent.target);
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -384,16 +384,21 @@
               </setting>
               <setting id="prefs-uilanguage" title="&language.title;" type="control">
                 <menulist id="prefs-languages" oncommand="PreferencesView.updateLocale();">
                   <menupopup>
                     <menuitem id="prefs-languages-auto" label="&language.auto;" value="auto"/>
                   </menupopup>
                 </menulist>
                </setting>
+#ifdef WINCE
+              <setting id="prefs-default-browser" type="bool" title="Default Browser" oncommand="BrowserUI.updateDefaultBrowser(this.value);">
+                Make &brandShortName; your default browser
+              </setting>
+#endif
               <settings id="prefs-content" label="&content.title;">
                 <setting pref="permissions.default.image" title="&showImages.title;" type="boolint" on="1" off="2"/>
                 <setting pref="javascript.enabled" type="bool" title="&enableJavaScript.title;"/>
                 <setting pref="plugins.enabled" type="bool" title="&enablePlugins.title;" oninputchanged="Browser.setPluginState(this.value);"/>
               </settings>
               <settings id="prefs-privacy" label="&privacy.title;">
                 <setting pref="network.cookie.cookieBehavior" title="&allowCookies.title;" type="boolint" on="0" off="2"/>
                 <setting pref="signon.rememberSignons" title="&rememberPasswords.title;" type="bool"/>
--- a/mobile/chrome/content/preferences.js
+++ b/mobile/chrome/content/preferences.js
@@ -113,16 +113,21 @@ var PreferencesView = {
                             },
                             false);
   },
 
   _delayedInit: function ev__delayedInit() {
     if (this._list)
       return;
 
+#ifdef WINCE
+    let phone = Cc["@mozilla.org/phone/support;1"].getService(Ci.nsIPhoneSupport);
+    document.getElementById("prefs-default-browser").value = phone.isDefaultBrowser(false);
+#endif
+
     this._list = document.getElementById("prefs-languages");
     this._loadLocales();
   },
 
   _loadLocales: function _loadLocales() {
     // Query available and selected locales
     let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
     chrome.QueryInterface(Ci.nsIToolkitChromeRegistry);
--- a/mobile/chrome/jar.mn
+++ b/mobile/chrome/jar.mn
@@ -3,17 +3,17 @@
 chrome.jar:
 % content browser %content/
 * content/about.xhtml                  (content/about.xhtml)
   content/aboutCertError.xhtml         (content/aboutCertError.xhtml)
   content/aboutCertError.css           (content/aboutCertError.css)
   content/languages.properties         (content/languages.properties)
 * content/browser.xul                  (content/browser.xul)
 * content/browser.js                   (content/browser.js)
-  content/browser-ui.js                (content/browser-ui.js)
+* content/browser-ui.js                (content/browser-ui.js)
   content/commandUtil.js               (content/commandUtil.js)
   content/bindings.xml                 (content/bindings.xml)
   content/firstrun.xhtml               (content/firstrun.xhtml)
   content/firstRunAnimation.gif        (content/firstRunAnimation.gif)
   content/tabs.xml                     (content/tabs.xml)
   content/bindings/checkbox.xml        (content/bindings/checkbox.xml)
   content/notification.xml             (content/notification.xml)
   content/bindings/extensions.xml      (content/bindings/extensions.xml)
@@ -27,17 +27,17 @@ chrome.jar:
   content/checkerboard.png             (content/checkerboard.png)
 % content branding %content/branding/
 * content/sanitize.xul                 (content/sanitize.xul)
 * content/sanitize.js                  (content/sanitize.js)
 * content/BrowserView.js               (content/BrowserView.js)
   content/TileManager.js               (TileManager.js)
 * content/InputHandler.js              (content/InputHandler.js)
 * content/Util.js                      (content/Util.js)
-  content/preferences.js               (content/preferences.js)
+* content/preferences.js               (content/preferences.js)
   content/exceptions.js                (content/exceptions.js)
   content/extensions.js                (content/extensions.js)
   content/downloads.js                 (content/downloads.js)
   content/console.js                   (content/console.js)
   content/prompt/alert.xul             (content/prompt/alert.xul)
   content/prompt/confirm.xul           (content/prompt/confirm.xul)
   content/prompt/prompt.xul            (content/prompt/prompt.xul)
   content/prompt/promptPassword.xul    (content/prompt/promptPassword.xul)
--- a/mobile/components/phone/nsIPhoneSupport.idl
+++ b/mobile/components/phone/nsIPhoneSupport.idl
@@ -53,9 +53,25 @@ interface nsIPhoneSupport : nsISupports
    * before dialing   
    */
   void makeCall(in wstring telephoneNumber, in wstring telephoneDescription, in boolean aPrompt);
   
   /**
    * This method displays a UI to switch to (or launch) a different task
    */
   void switchTask();
+
+  /**
+   * Determines whether or not Fennec is the "Default Browser".
+   * This is simply whether or not Fennec is registered to handle http links.
+   */
+  boolean isDefaultBrowser();
+
+  /**
+   * Registers Fennec as the "Default Browser".
+   */
+  void setDefaultBrowser();
+
+  /**
+   * Restores previous "Default Browser".
+   */
+  void restoreDefaultBrowser();
 };
--- a/mobile/components/phone/nsPhoneSupport.cpp
+++ b/mobile/components/phone/nsPhoneSupport.cpp
@@ -18,16 +18,17 @@
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation <http://www.mozilla.org/>.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Doug Turner <dougt@meer.net>
  *  Nino D'Aversa <ninodaversa@gmail.com>
+ *  Alex Pakhotin <alexp@mozilla.com>
  *
  * 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
@@ -113,16 +114,317 @@ nsPhoneSupport::SwitchTask()
       dbus_connection_flush(conn);
   }
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
+#ifdef WINCE
+
+///////////////////////////////////////////////////////////////////////////////
+// Default Browser Registry Settings
+// Based on the browser\components\shell\src\nsWindowsShellService.cpp
+//
+// The following keys have to be changed for Windows Mobile:
+//
+// [HKEY_LOCAL_MACHINE\Software\Microsoft\Shell\Rai\:DEFBROWSER]
+// 0=Pocket IE
+// 1=:MSPIE
+// 
+// [HKEY_CLASSES_ROOT\file\Shell\Open\Command]
+// [HKEY_CLASSES_ROOT\ftp\Shell\Open\Command]
+// [HKEY_CLASSES_ROOT\http\Shell\Open\Command]
+// [HKEY_CLASSES_ROOT\https\Shell\Open\Command]
+// (Default)=iexplore.exe %1
+// 
+// --- Optional: ---
+// 
+// [HKEY_CLASSES_ROOT\htmlfile\Shell\Open\Command]
+// [HKEY_CLASSES_ROOT\xhtmlfile\Shell\Open\Command]
+// [HKEY_CLASSES_ROOT\xmlfile\Shell\Open\Command]
+// [HKEY_CLASSES_ROOT\xslfile\Shell\Open\Command]
+// (Default)=iexplore.exe file:%1
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#define MAX_BUF 1024
+
+#define REG_SUCCEEDED(val) \
+  (val == ERROR_SUCCESS)
+
+#define REG_FAILED(val) \
+  (val != ERROR_SUCCESS)
+
+typedef struct {
+  HKEY keyRoot;
+  char* keyName;
+  char* valueName;
+  char* valueData;
+  char* backupValueName;
+} SETTING;
+
+#define APP_REG_NAME "Fennec"
+#define BACKUP_KEY_ROOT HKEY_LOCAL_MACHINE
+#define BACKUP_KEY "Software\\Mozilla\\Fennec\\DefaultBrowserBackup"
+#define DEFBROWSERREG "Software\\Microsoft\\Shell\\Rai\\:DEFBROWSER"
+#define VAL_OPEN "\"%APPPATH%\" \"%1\""
+#define SHELL_OPEN "\\Shell\\Open\\Command"
+
+static SETTING gSettings[] = {
+  { HKEY_LOCAL_MACHINE, DEFBROWSERREG,         "0", APP_REG_NAME,    "Rai_DEFBROWSER_0" },
+  { HKEY_LOCAL_MACHINE, DEFBROWSERREG,         "1", "\"%APPPATH%\"", "Rai_DEFBROWSER_1" },
+  { HKEY_CLASSES_ROOT, "http" SHELL_OPEN,      "",  VAL_OPEN,        "http" },
+  { HKEY_CLASSES_ROOT, "https" SHELL_OPEN,     "",  VAL_OPEN,        "https" },
+  // Optional in Firefox
+  { HKEY_CLASSES_ROOT, "file" SHELL_OPEN,      "",  VAL_OPEN,        "file" },
+  { HKEY_CLASSES_ROOT, "ftp" SHELL_OPEN,       "",  VAL_OPEN,        "ftp" },
+  { HKEY_CLASSES_ROOT, "htmlfile" SHELL_OPEN,  "",  VAL_OPEN,        "htmlfile" },
+  { HKEY_CLASSES_ROOT, "xhtmlfile" SHELL_OPEN, "",  VAL_OPEN,        "xhtmlfile" },
+  { HKEY_CLASSES_ROOT, "xmlfile" SHELL_OPEN,   "",  VAL_OPEN,        "xmlfile" },
+  { HKEY_CLASSES_ROOT, "xslfile" SHELL_OPEN,   "",  VAL_OPEN,        "xslfile" },
+  // Opera changes these as well
+  { HKEY_CLASSES_ROOT, ".htm",                 "",  "htmlfile",      ".htm" },
+  { HKEY_CLASSES_ROOT, ".html",                "",  "htmlfile",      ".html" },
+  { HKEY_CLASSES_ROOT, ".xml",                 "",  "xmlfile",       ".xml" },
+  { HKEY_CLASSES_ROOT, ".xsl",                 "",  "xslfile",       ".xsl" },
+};
+
+const PRInt32 gSettingsCount = sizeof(gSettings)/sizeof(SETTING);
+
+static nsresult
+OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
+{
+  const nsString &flatName = PromiseFlatString(aKeyName);
+
+  DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
+  switch (res) {
+  case ERROR_ACCESS_DENIED:
+    return NS_ERROR_FILE_ACCESS_DENIED;
+  case ERROR_FILE_NOT_FOUND:
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+OpenKeyForWriting(HKEY aStartKey, const nsAString& aKeyName, HKEY* aKey)
+{
+  const nsString &flatName = PromiseFlatString(aKeyName);
+
+  DWORD dwDisp = 0;
+  DWORD res = ::RegCreateKeyExW(aStartKey, flatName.get(), 0, NULL,
+                                0, KEY_READ | KEY_WRITE, NULL, aKey,
+                                &dwDisp);
+  switch (res) {
+  case ERROR_ACCESS_DENIED:
+    return NS_ERROR_FILE_ACCESS_DENIED;
+  case ERROR_FILE_NOT_FOUND:
+    res = ::RegCreateKeyExW(aStartKey, flatName.get(), 0, NULL,
+                            0, KEY_READ | KEY_WRITE, NULL, aKey,
+                            NULL);
+    if (res != ERROR_SUCCESS)
+      return NS_ERROR_FILE_ACCESS_DENIED;
+  }
+
+  return NS_OK;
+}
+
+void
+SetRegValue(HKEY aKeyRoot, const nsString& aKeyName, const nsString& aValueName, const nsString& aValue)
+{
+  PRUnichar buf[MAX_BUF];
+  DWORD len = sizeof buf;
+
+  HKEY theKey;
+  nsresult rv = OpenKeyForWriting(aKeyRoot, aKeyName, &theKey);
+  if (NS_FAILED(rv))
+    return;
+
+  // Get the current value.
+  DWORD res = ::RegQueryValueExW(theKey, PromiseFlatString(aValueName).get(),
+                                 NULL, NULL, (LPBYTE)buf, &len);
+
+  // Set the new value if it doesn't exist or it is different than the current
+  // value.
+  nsAutoString current(buf);
+  if (REG_FAILED(res) || !current.Equals(aValue)) {
+    const nsString &flatValue = PromiseFlatString(aValue);
+
+    ::RegSetValueExW(theKey, PromiseFlatString(aValueName).get(),
+                     0, REG_SZ, (const BYTE *)flatValue.get(),
+                     (flatValue.Length() + 1) * sizeof(PRUnichar));
+  }
+
+  // Close the key we opened.
+  ::RegCloseKey(theKey);
+}
+
+DWORD
+GetRegValue(HKEY aKeyRoot, const nsString& aKeyName, const nsString& aValueName, nsString& aValue)
+{
+  aValue = L"";
+  PRUnichar currValue[MAX_BUF];
+  ::ZeroMemory(currValue, sizeof(currValue));
+  HKEY theKey;
+  DWORD res = OpenKeyForReading(aKeyRoot, aKeyName, &theKey);
+  if (REG_FAILED(res))
+    return res;
+
+  DWORD len = sizeof currValue;
+  res = ::RegQueryValueExW(theKey, PromiseFlatString(aValueName).get(),
+                                 NULL, NULL, (LPBYTE)currValue, &len);
+  // Close the key we opened.
+  ::RegCloseKey(theKey);
+
+  if (REG_SUCCEEDED(res))
+    aValue = currValue;
+
+  return res;
+}
+#endif // WINCE
+
+NS_IMETHODIMP
+nsPhoneSupport::IsDefaultBrowser(PRBool* aIsDefaultBrowser)
+{
+#ifdef WINCE
+  *aIsDefaultBrowser = PR_TRUE;
+
+  SETTING* settings;
+  SETTING* end = gSettings + gSettingsCount;
+
+  PRUnichar exePath[MAX_BUF];
+  if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
+    return NS_ERROR_FAILURE;
+
+  nsAutoString appPath(exePath);
+
+  nsresult rv;
+  PRUnichar currValue[MAX_BUF];
+  for (settings = gSettings; settings < end; ++settings) {
+    NS_ConvertUTF8toUTF16 dataPath(settings->valueData);
+    NS_ConvertUTF8toUTF16 key(settings->keyName);
+    NS_ConvertUTF8toUTF16 value(settings->valueName);
+    PRInt32 offset = dataPath.Find("%APPPATH%");
+    if (offset >= 0)
+      dataPath.Replace(offset, 9, appPath);
+
+    ::ZeroMemory(currValue, sizeof(currValue));
+    HKEY theKey;
+    rv = OpenKeyForReading(settings->keyRoot, key, &theKey);
+    if (NS_FAILED(rv)) {
+      *aIsDefaultBrowser = PR_FALSE;
+      return NS_OK;
+    }
+
+    DWORD len = sizeof currValue;
+    DWORD res = ::RegQueryValueExW(theKey, PromiseFlatString(value).get(),
+                                   NULL, NULL, (LPBYTE)currValue, &len);
+    // Close the key we opened.
+    ::RegCloseKey(theKey);
+    if (REG_FAILED(res) ||
+        !dataPath.Equals(currValue)) {
+      // Key wasn't set, or was set to something other than our registry entry
+      *aIsDefaultBrowser = PR_FALSE;
+      return NS_OK;
+    }
+  }
+
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsPhoneSupport::SetDefaultBrowser()
+{
+#ifdef WINCE
+  PRBool defaultBrowser = PR_FALSE;
+  nsresult result = IsDefaultBrowser(&defaultBrowser);
+  if (defaultBrowser || result != NS_OK)
+  {
+    // Already set as default browser
+    return result;
+  }
+
+  SETTING* settings;
+  SETTING* end = gSettings + gSettingsCount;
+
+  PRUnichar exePath[MAX_BUF];
+  if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
+    return NS_ERROR_FAILURE;
+
+  nsAutoString appPath(exePath);
+
+  for (settings = gSettings; settings < end; ++settings) {
+    NS_ConvertUTF8toUTF16 dataPath(settings->valueData);
+    NS_ConvertUTF8toUTF16 key(settings->keyName);
+    NS_ConvertUTF8toUTF16 value(settings->valueName);
+    NS_ConvertUTF8toUTF16 backupKey(BACKUP_KEY);
+    NS_ConvertUTF8toUTF16 backupValue(settings->backupValueName);
+    PRInt32 offset = dataPath.Find("%APPPATH%");
+    if (offset >= 0)
+      dataPath.Replace(offset, 9, appPath);
+
+    nsString dataOld;
+    if (REG_SUCCEEDED(GetRegValue(settings->keyRoot, key, value, dataOld))) {
+      SetRegValue(BACKUP_KEY_ROOT, backupKey, backupValue, dataOld);
+      SetRegValue(settings->keyRoot, key, value, dataPath);
+    }
+  }
+  // On Windows CE RegFlushKey can negatively impact performance if there are a
+  // lot of pending writes to the HKEY_CLASSES_ROOT registry hive but it is
+  // necessary to save the values in the case where the user performs a hard
+  // power off of the device.
+  ::RegFlushKey(HKEY_CLASSES_ROOT);
+  ::RegFlushKey(HKEY_LOCAL_MACHINE);
+
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsPhoneSupport::RestoreDefaultBrowser()
+{
+#ifdef WINCE
+  PRBool defaultBrowser = PR_FALSE;
+  nsresult result = IsDefaultBrowser(&defaultBrowser);
+  if (!defaultBrowser || result != NS_OK)
+  {
+    // Not default browser - nothing to restore
+    return result;
+  }
+
+  SETTING* settings;
+  SETTING* end = gSettings + gSettingsCount;
+
+  for (settings = gSettings; settings < end; ++settings) {
+    NS_ConvertUTF8toUTF16 key(settings->keyName);
+    NS_ConvertUTF8toUTF16 value(settings->valueName);
+    NS_ConvertUTF8toUTF16 backupKey(BACKUP_KEY);
+    NS_ConvertUTF8toUTF16 backupValue(settings->backupValueName);
+
+    nsString dataOld;
+    if (REG_SUCCEEDED(GetRegValue(BACKUP_KEY_ROOT, backupKey, backupValue, dataOld)))
+      SetRegValue(settings->keyRoot, key, value, dataOld);
+  }
+  ::RegFlushKey(HKEY_CLASSES_ROOT);
+  ::RegFlushKey(HKEY_LOCAL_MACHINE);
+
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
 //------------------------------------------------------------------------------
 //  XPCOM REGISTRATION BELOW
 //------------------------------------------------------------------------------
 
 #define nsPhoneSupport_CID                          \
 { 0x2a08c9e4, 0xf853, 0x4f02,                       \
 {0x88, 0xd8, 0xd6, 0x2f, 0x27, 0xca, 0x06, 0x85} }