[OS/2] Bug 305061: use RWS to improve platform integration on OS/2 (base RWS support and helper app handling), p=Rich Walsh and me, testing=abwillis1,daveryeo,wuno, r=mkaply
authormozilla@weilbacher.org
Wed, 09 Jan 2008 14:24:05 -0800
changeset 10099 b7fd2219ae8835c0ac2597974a3efa7db46ca929
parent 10098 f7d6e832403f693bcd439472c8d63b97fd19aa8a
child 10100 705271c14808909dab335acf2b9d5adeb221c251
push idunknown
push userunknown
push dateunknown
reviewersmkaply
bugs305061
milestone1.9b3pre
[OS/2] Bug 305061: use RWS to improve platform integration on OS/2 (base RWS support and helper app handling), p=Rich Walsh and me, testing=abwillis1,daveryeo,wuno, r=mkaply
toolkit/locales/en-US/chrome/mozapps/downloads/unknownContentType.properties
uriloader/exthandler/Makefile.in
uriloader/exthandler/os2/nsMIMEInfoOS2.cpp
uriloader/exthandler/os2/nsMIMEInfoOS2.h
uriloader/exthandler/os2/nsOSHelperAppService.cpp
uriloader/exthandler/os2/nsOSHelperAppService.h
widget/public/Makefile.in
widget/public/nsIRwsService.idl
widget/src/os2/Makefile.in
widget/src/os2/nsRwsService.cpp
widget/src/os2/nsRwsService.h
widget/src/os2/nsWidgetFactory.cpp
--- a/toolkit/locales/en-US/chrome/mozapps/downloads/unknownContentType.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/downloads/unknownContentType.properties
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 # Doron Rosenberg.
 # Portions created by the Initial Developer are Copyright (C) 2001
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ben Goodger <ben@bengoodger.com>
+#   Rich Walsh <dragtext@e-vertise.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
@@ -41,8 +42,25 @@ saveDialogTitle=Enter name of file to save to…
 defaultApp=%S (default)
 chooseAppFilePickerTitle=Choose Helper Application
 badApp=The application you chose ("%S") could not be found.  Check the file name or choose another application.
 badApp.title=Application not found
 selectDownloadDir=Select Download Folder
 unknownAccept.label=Save File
 unknownCancel.label=Cancel
 fileType=%S file
+
+# LOCALIZATION NOTE (wpsDefaultOS2): OS/2 only, WPS refers to the Workplace Shell and should probably not be translated
+wpsDefaultOS2=WPS Default
+# LOCALIZATION NOTE (classViewerOS2): OS/2 only
+classViewerOS2=Viewer for Class %S
+# LOCALIZATION NOTE (mmImageViewerOS2): OS/2 only, default operation of image files with OS/2 multimedia support installed
+mmImageViewerOS2=Image Viewer
+# LOCALIZATION NOTE (mmAudioPlayerOS2): OS/2 only, default operation of audio files with OS/2 multimedia support installed
+mmAudioPlayerOS2=Audio Player
+# LOCALIZATION NOTE (mmVideoPlayerOS2): OS/2 only, default operation of video files with OS/2 multimedia support installed
+mmVideoPlayerOS2=Video Player
+# LOCALIZATION NOTE (mmMidiPlayerOS2): OS/2 only, default operation of MIDI files with OS/2 multimedia support installed
+mmMidiPlayerOS2=MIDI Player
+# LOCALIZATION NOTE (odZipFolderOS2): OS/2 only, refers to ZipFolder of Object Desktop
+odZipFolderOS2=ZipFolder
+# LOCALIZATION NOTE (odTextViewOS2): OS/2 only, refers to TextView of Object Desktop
+odTextViewOS2=TextView
--- a/uriloader/exthandler/Makefile.in
+++ b/uriloader/exthandler/Makefile.in
@@ -120,16 +120,17 @@ endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 OSHELPER  += nsMIMEInfoWin.cpp
 LOCAL_INCLUDES += -I$(srcdir)/win
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 OSHELPER  += nsMIMEInfoOS2.cpp
+REQUIRES  += widget
 endif
 
 EXPORTS = \
 		$(OSDIR)/nsOSHelperAppService.h \
 			$(NULL)
 
 XPIDLSRCS = \
 	nsCExternalHandlerService.idl	\
--- a/uriloader/exthandler/os2/nsMIMEInfoOS2.cpp
+++ b/uriloader/exthandler/os2/nsMIMEInfoOS2.cpp
@@ -1,11 +1,11 @@
-/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 et cin: */
+/* ***** 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,
@@ -19,145 +19,287 @@
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
  *   Boris Zbarsky <bzbarsky@mit.edu>  (Added mailcap and mime.types support)
  *   Christian Biesinger <cbiesinger@web.de>
+ *   Rich Walsh <dragtext@e-vertise.com>
+ *   Peter Weilbacher <mozilla@Weilbacher.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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 ***** */
 
+#ifdef MOZ_OS2_HIGH_MEMORY
+// os2safe.h has to be included before os2.h, needed for high mem
+#include <os2safe.h>
+#endif
+
 #include "nsMIMEInfoOS2.h"
 #include "nsOSHelperAppService.h"
 #include "nsExternalHelperAppService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsReadableUtils.h"
 #include "nsIProcess.h"
+#include "nsNetUtil.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIVariant.h"
+#include "nsArrayEnumerator.h"
+#include "nsIRwsService.h"
 #include <stdlib.h>
 
+//------------------------------------------------------------------------
+
 #define SALT_SIZE 8
 #define TABLE_SIZE 36
 static const PRUnichar table[] = 
   { 'a','b','c','d','e','f','g','h','i','j',
     'k','l','m','n','o','p','q','r','s','t',
     'u','v','w','x','y','z','0','1','2','3',
     '4','5','6','7','8','9'};
 
+// reduces overhead by preventing calls to nsRwsService when it isn't present
+static PRBool sUseRws = PR_TRUE;
+
+//------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsMIMEInfoOS2, nsMIMEInfoBase, nsIPropertyBag)
 
 nsMIMEInfoOS2::~nsMIMEInfoOS2()
 {
 }
 
-NS_IMETHODIMP nsMIMEInfoOS2::LaunchWithURI(nsIURI* aURI,
-                                           nsIInterfaceRequestor* aWindowContext)
+//------------------------------------------------------------------------
+// if the helper application is a DOS app, create an 8.3 filename
+static nsresult Make8Dot3Name(nsIFile *aFile, nsACString& aPath)
+{
+  nsCAutoString leafName;
+  aFile->GetNativeLeafName(leafName);
+  const char *lastDot = strrchr(leafName.get(), '.');
+
+  char suffix[8] = "";
+  if (lastDot) {
+    strncpy(suffix, lastDot, 4);
+    suffix[4] = '\0';
+  }
+
+  nsCOMPtr<nsIFile> tempPath;
+  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempPath));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString saltedTempLeafName;
+  do {
+    saltedTempLeafName.Truncate();
+
+    // this salting code was ripped directly from the profile manager.
+    // turn PR_Now() into milliseconds since epoch 1058 & salt rand with that
+    double fpTime;
+    LL_L2D(fpTime, PR_Now());
+    srand((uint)(fpTime * 1e-6 + 0.5));
+
+    for (PRInt32 i=0; i < SALT_SIZE; i++)
+      saltedTempLeafName.Append(table[(rand()%TABLE_SIZE)]);
+
+    AppendASCIItoUTF16(suffix, saltedTempLeafName);
+    rv = aFile->CopyTo(tempPath, saltedTempLeafName);
+  } while (NS_FAILED(rv));
+
+  nsCOMPtr<nsPIExternalAppLauncher>
+    helperAppService(do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
+  if (!helperAppService)
+    return NS_ERROR_FAILURE;
+
+  tempPath->Append(saltedTempLeafName);
+  helperAppService->DeleteTemporaryFileOnExit(tempPath);
+  tempPath->GetNativePath(aPath);
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// opens a file using the selected program or WPS object
+
+NS_IMETHODIMP nsMIMEInfoOS2::LaunchWithFile(nsIFile *aFile)
 {
   nsresult rv = NS_OK;
 
-  nsCOMPtr<nsILocalFile> docToLoad;
-  rv = GetLocalFileFromURI(aURI, getter_AddRefs(docToLoad));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCAutoString path;
-  docToLoad->GetNativePath(path);
-  
   nsCOMPtr<nsIFile> application;
   if (mPreferredAction == useHelperApp) {
     nsCOMPtr<nsILocalHandlerApp> localHandlerApp =
       do_QueryInterface(mPreferredApplication, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = localHandlerApp->GetExecutable(getter_AddRefs(application));
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (mPreferredAction == useSystemDefault) {
     application = mDefaultApplication;
   } else {
     return NS_ERROR_INVALID_ARG;
   }
 
-  // The nsIMIMEInfo should have either the default or preferred 
-  // application handler attribute set to match the preferredAction!
+  nsCAutoString filePath;
+  aFile->GetNativePath(filePath);
+
+  // if there's no program, use the WPS to open the file
   if (!application) {
-    HOBJECT hobject = WinQueryObject(path.get());
-    if (WinSetObjectData( hobject, "OPEN=DEFAULT" ))
-      return NS_OK;
-    else
-      return NS_ERROR_FAILURE;
+    rv = NS_ERROR_FAILURE;
+
+    // if RWS is enabled, see if nsOSHelperAppService provided a handle for
+    // the app associated with this file;  if so, use it to open the file;
+    if (sUseRws) {
+      PRUint32 appHandle;
+      GetDefaultAppHandle(&appHandle);
+      if (appHandle) {
+        nsCOMPtr<nsIRwsService> rwsSvc(do_GetService("@mozilla.org/rwsos2;1"));
+        if (!rwsSvc) {
+          sUseRws = PR_FALSE;
+        } else {
+          // this call is identical to dropping the file on a program's icon;
+          // it ensures filenames with multiple dots are handled correctly
+          rv = rwsSvc->OpenWithAppHandle(filePath.get(), appHandle);
+        }
+      }
+    }
+
+    // if RWS isn't present or fails, open it using a PM call
+    if (NS_FAILED(rv)) {
+      if (WinSetObjectData(WinQueryObject(filePath.get()), "OPEN=DEFAULT"))
+        rv = NS_OK;
+    }
+
+    return rv;
   }
-  
+
+  // open the data file using the specified program file
+  nsCAutoString appPath;
+  if (application) {
+    application->GetNativePath(appPath);
+  }
+
   ULONG ulAppType;
-  nsCAutoString apppath;
-  application->GetNativePath(apppath);
-  DosQueryAppType(apppath.get(), &ulAppType);
+  DosQueryAppType(appPath.get(), &ulAppType);
   if (ulAppType & (FAPPTYP_DOS |
                    FAPPTYP_WINDOWSPROT31 |
                    FAPPTYP_WINDOWSPROT |
                    FAPPTYP_WINDOWSREAL)) {
-    // if the helper application is a DOS app, create an 8.3 filename
-    // we do this even if the filename is valid because it's 8.3, who cares
-    nsCOMPtr<nsPIExternalAppLauncher> helperAppService (do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
-    if (helperAppService)
-    {
-      nsCAutoString leafName; 
-      docToLoad->GetNativeLeafName(leafName);
-      const char* lastDot = strrchr(leafName.get(), '.');
-      char suffix[CCHMAXPATH + 1] = "";
-      if (lastDot)
-      {
-          strcpy(suffix, lastDot);
-      }
-      suffix[4] = '\0';
-      
-      nsAutoString saltedTempLeafName;
-      do {
-          saltedTempLeafName.Truncate();
-          // this salting code was ripped directly from the profile manager.
-          // turn PR_Now() into milliseconds since epoch 1058 // and salt rand with that. 
-          double fpTime;
-          LL_L2D(fpTime, PR_Now());
-          srand((uint)(fpTime * 1e-6 + 0.5));
-          PRInt32 i;
-          for (i=0;i<SALT_SIZE;i++) {
-            saltedTempLeafName.Append(table[(rand()%TABLE_SIZE)]);
-          }
-          AppendASCIItoUTF16(suffix, saltedTempLeafName);
-          rv = docToLoad->MoveTo(nsnull, saltedTempLeafName);
-      } while (NS_FAILED(rv));
-      helperAppService->DeleteTemporaryFileOnExit(docToLoad);
-      docToLoad->GetNativePath(path);
+    rv = Make8Dot3Name(aFile, filePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  filePath.Insert('\"', 0);
+  filePath.Append('\"');
+
+  // if RWS is enabled, have the WPS open the file using the selected app;
+  // this lets the user specify commandline args in the exe's WPS notebook
+  rv = NS_ERROR_FAILURE;
+  if (sUseRws) {
+    nsCOMPtr<nsIRwsService> rwsSvc(do_GetService("@mozilla.org/rwsos2;1"));
+    if (!rwsSvc) {
+      sUseRws = PR_FALSE;
+    } else {
+      rv = rwsSvc->OpenWithAppPath(filePath.get(), appPath.get());
     }
-  } else {
-    path.Insert('\"', 0);
-    path.Append('\"');
+  }
+
+  // if RWS isn't present or fails, use Moz facilities to run the program
+  if (NS_FAILED(rv)) {
+    nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID);
+    if (NS_FAILED(rv = process->Init(application)))
+      return rv;
+    const char *strPath = filePath.get();
+    PRUint32 pid;
+    return process->Run(PR_FALSE, &strPath, 1, &pid);
   }
-    
-  const char * strPath = path.get();
-  // if we were given an application to use then use it....otherwise
-  // make the registry call to launch the app
-  nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID);
-  if (NS_FAILED(rv = process->Init(application)))
-    return rv;
-  PRUint32 pid;
-  return process->Run(PR_FALSE, &strPath, 1, &pid);
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// if there's a description, there's a handler (which may be the WPS)
+
+NS_IMETHODIMP nsMIMEInfoOS2::GetHasDefaultHandler(PRBool *_retval)
+{
+  *_retval = !mDefaultAppDescription.IsEmpty();
+  return NS_OK;
 }
 
-nsresult nsMIMEInfoOS2::LoadUriInternal(nsIURI* aURL)
+//------------------------------------------------------------------------
+
+// copied directly from nsMIMEInfoImpl
+
+NS_IMETHODIMP
+nsMIMEInfoOS2::GetDefaultDescription(nsAString& aDefaultDescription)
+{
+  if (mDefaultAppDescription.IsEmpty() && mDefaultApplication)
+    mDefaultApplication->GetLeafName(aDefaultDescription);
+  else
+    aDefaultDescription = mDefaultAppDescription;
+
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------
+
+// Get() is new, Set() is an override;  they permit nsOSHelperAppService
+// to reorder the default & preferred app handlers
+
+void nsMIMEInfoOS2::GetDefaultApplication(nsIFile **aDefaultAppHandler)
+{
+  *aDefaultAppHandler = mDefaultApplication;
+  NS_IF_ADDREF(*aDefaultAppHandler);
+  return;
+}
+
+void nsMIMEInfoOS2::SetDefaultApplication(nsIFile *aDefaultApplication)
+{
+  mDefaultApplication = aDefaultApplication;
+  return;
+}
+
+//------------------------------------------------------------------------
+
+// gets/sets the handle of the WPS object associated with this mimetype
+
+void nsMIMEInfoOS2::GetDefaultAppHandle(PRUint32 *aHandle)
+{
+  if (aHandle) {
+    if (mDefaultAppHandle <= 0x10000 || mDefaultAppHandle >= 0x40000)
+      mDefaultAppHandle = 0;
+    *aHandle = mDefaultAppHandle;
+  }
+  return;
+}
+
+void nsMIMEInfoOS2::SetDefaultAppHandle(PRUint32 aHandle)
+{
+  if (aHandle <= 0x10000 || aHandle >= 0x40000)
+    mDefaultAppHandle = 0;
+  else
+    mDefaultAppHandle = aHandle;
+  return;
+}
+
+//------------------------------------------------------------------------
+
+nsresult nsMIMEInfoOS2::LoadUriInternal(nsIURI *aURL)
 {
   nsresult rv;
   nsCOMPtr<nsIPrefService> thePrefsService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
   if (!thePrefsService) {
     return NS_ERROR_FAILURE;
   }
 
   /* Convert SimpleURI to StandardURL */
@@ -404,8 +546,79 @@ nsresult nsMIMEInfoOS2::LoadUriInternal(
 
   PRUint32 pid;
   if (NS_FAILED(rv = process->Run(PR_FALSE, params, numParams, &pid)))
     return rv;
 
   return NS_OK;
 }
 
+//------------------------------------------------------------------------
+// nsIPropertyBag
+//------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsMIMEInfoOS2::GetEnumerator(nsISimpleEnumerator **_retval)
+{
+  nsCOMArray<nsIVariant> properties;
+
+  nsCOMPtr<nsIVariant> variant;
+  GetProperty(NS_LITERAL_STRING("defaultApplicationIconURL"), getter_AddRefs(variant));
+  if (variant)
+    properties.AppendObject(variant);
+
+  GetProperty(NS_LITERAL_STRING("customApplicationIconURL"), getter_AddRefs(variant));
+  if (variant)
+    properties.AppendObject(variant);
+
+  return NS_NewArrayEnumerator(_retval, properties);
+}
+
+//------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsMIMEInfoOS2::GetProperty(const nsAString& aName, nsIVariant **_retval)
+{
+  nsresult rv = NS_ERROR_FAILURE;
+
+  if (aName.EqualsLiteral(PROPERTY_DEFAULT_APP_ICON_URL)) {
+    rv = GetIconURLVariant(mDefaultApplication, _retval);
+  } else {
+    if (aName.EqualsLiteral(PROPERTY_CUSTOM_APP_ICON_URL) &&
+        mPreferredApplication) {
+      // find file from handler
+      nsCOMPtr<nsIFile> appFile;
+      nsCOMPtr<nsILocalHandlerApp> localHandlerApp =
+        do_QueryInterface(mPreferredApplication, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = localHandlerApp->GetExecutable(getter_AddRefs(appFile));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = GetIconURLVariant(appFile, _retval);
+    }
+  }
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsMIMEInfoOS2::GetIconURLVariant(nsIFile *aApplication, nsIVariant **_retval)
+{
+  nsresult rv = CallCreateInstance("@mozilla.org/variant;1", _retval);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString fileURLSpec;
+  if (aApplication)
+    NS_GetURLSpecFromFile(aApplication, fileURLSpec);
+  else {
+    GetPrimaryExtension(fileURLSpec);
+    fileURLSpec.Insert(NS_LITERAL_CSTRING("moztmp."), 0);
+  }
+
+  nsCAutoString iconURLSpec(NS_LITERAL_CSTRING("moz-icon://"));
+  iconURLSpec += fileURLSpec;
+  nsCOMPtr<nsIWritableVariant> writable(do_QueryInterface(*_retval));
+  writable->SetAsAUTF8String(iconURLSpec);
+
+  return NS_OK;
+}
--- a/uriloader/exthandler/os2/nsMIMEInfoOS2.h
+++ b/uriloader/exthandler/os2/nsMIMEInfoOS2.h
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 et cin: */
 /* ***** 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/
  *
@@ -14,16 +16,18 @@
  * The Original Code is the OS/2 MIME Info Implementation.
  *
  * The Initial Developer of the Original Code is
  * Christian Biesinger <cbiesinger@web.de>.
  * Portions created by the Initial Developer are Copyright (C) 2004
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *   Rich Walsh <dragtext@e-vertise.com>
+ *   Peter Weilbacher <mozilla@Weilbacher.org>
  *
  * 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
@@ -33,42 +37,61 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMIMEInfoOS2_h_
 #define nsMIMEInfoOS2_h_
 
 #include "nsMIMEInfoImpl.h"
+#include "nsIPropertyBag.h"
 
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsNetCID.h"
 #include "nsEscape.h"
 
 #define INCL_DOS
 #define INCL_DOSMISC
 #define INCL_DOSERRORS
 #define INCL_WINSHELLDATA
 #include <os2.h>
 
-class nsMIMEInfoOS2 : public nsMIMEInfoImpl
+class nsMIMEInfoOS2 : public nsMIMEInfoBase, public nsIPropertyBag
 {
   public:
-    nsMIMEInfoOS2(const char* aType = "") : nsMIMEInfoImpl(aType) {}
-    nsMIMEInfoOS2(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) {}
+    nsMIMEInfoOS2(const char *aType = "") :
+      nsMIMEInfoBase(aType), mDefaultAppHandle(0) {}
+    nsMIMEInfoOS2(const nsACString& aMIMEType) :
+      nsMIMEInfoBase(aMIMEType), mDefaultAppHandle(0) {}
     nsMIMEInfoOS2(const nsACString& aType, HandlerClass aClass) :
-      nsMIMEInfoImpl(aType, aClass) {}
+      nsMIMEInfoBase(aType, aClass), mDefaultAppHandle(0) {}
     virtual ~nsMIMEInfoOS2();
 
-    NS_IMETHOD LaunchWithURI(nsIURI* aURI,
-                             nsIInterfaceRequestor* aWindowContext);
+    NS_DECL_ISUPPORTS_INHERITED
+    NS_DECL_NSIPROPERTYBAG
+
+    NS_IMETHOD LaunchWithFile(nsIFile *aFile);
+
+    NS_IMETHOD GetHasDefaultHandler(PRBool *_retval);
+    NS_IMETHOD GetDefaultDescription(nsAString& aDefaultDescription);
+
+    void GetDefaultApplication(nsIFile **aDefaultAppHandler);
+    void SetDefaultApplication(nsIFile *aDefaultApplication);
+
+    void GetDefaultAppHandle(PRUint32 *aHandle);
+    void SetDefaultAppHandle(PRUint32 aHandle);
+
   protected:
     virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI);
-#ifdef DEBUG
-    virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile) {
+    // XXX should we do most of the work here and let LaunchWithFile() call this?
+    virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile *aFile) {
       NS_NOTREACHED("Do not call this, use LaunchWithFile");
       return NS_ERROR_UNEXPECTED;
     }
-#endif
+
+    NS_IMETHOD GetIconURLVariant(nsIFile *aApplication, nsIVariant **_retval);
+
+    nsCOMPtr<nsIFile> mDefaultApplication;
+    PRUint32 mDefaultAppHandle;
 };
 
 #endif
--- a/uriloader/exthandler/os2/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/os2/nsOSHelperAppService.cpp
@@ -1,11 +1,11 @@
-/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 et cin: */
+/* ***** 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,
@@ -19,16 +19,17 @@
  * Netscape Communications, Inc.
  * Portions created by the Initial Developer are Copyright (C) 1999
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Scott MacGregor <mscott@netscape.com>
  *   Boris Zbarsky <bzbarsky@mit.edu>  (Added mailcap and mime.types support)
  *   Peter Weilbacher <mozilla@Weilbacher.org>
+ *   Rich Walsh <dragtext@e-vertise.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -52,32 +53,42 @@
 #include "nsIProcess.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsHashtable.h"
 #include "nsCRT.h"
 #include "prenv.h"      // for PR_GetEnv()
 #include "nsMIMEInfoOS2.h"
 #include "nsAutoPtr.h"
+#include "nsIRwsService.h"
+#include "nsIStringBundle.h"
+#include "nsLocalHandlerApp.h"
 #include <stdlib.h>     // for system()
 
+//------------------------------------------------------------------------
+
+// reduces overhead by preventing calls to nsRws when it isn't present
+static PRBool sUseRws = PR_TRUE;
+
 static nsresult
 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
               const nsAString::const_iterator& aEnd_iter);
 static nsresult
 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
               nsAString::const_iterator& aMajorTypeStart,
               nsAString::const_iterator& aMajorTypeEnd,
               nsAString::const_iterator& aMinorTypeStart,
               nsAString::const_iterator& aMinorTypeEnd,
               const nsAString::const_iterator& aEnd_iter);
 
 inline PRBool
 IsNetscapeFormat(const nsACString& aBuffer);
 
+//------------------------------------------------------------------------
+
 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
 {
 }
 
 nsOSHelperAppService::~nsOSHelperAppService()
 {}
 
 /*
@@ -1340,16 +1351,222 @@ nsOSHelperAppService::GetFromType(const 
     mimeInfo->SetDefaultDescription(handler);
   } else {
     mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
   }
 
   return mimeInfo;
 }
 
+//------------------------------------------------------------------------
+
+// returns a localized string from unknownContentType.properties
+
+static nsresult
+GetNLSString(const PRUnichar *aKey, nsAString& result)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIStringBundleService> bundleSvc =
+    do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIStringBundle> bundle;
+  rv = bundleSvc->CreateBundle(
+    "chrome://mozapps/locale/downloads/unknownContentType.properties",
+    getter_AddRefs(bundle));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsXPIDLString string;
+  rv = bundle->GetStringFromName(aKey, getter_Copies(string));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  result.Assign(string);
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// returns the handle of the WPS object associated with a file extension;
+// if RWS isn't being used or there's no association, returns zero;
+// also constructs a description of the handler based on available info
+
+static PRUint32
+WpsGetDefaultHandler(const char *aFileExt, nsAString& aDescription)
+{
+  aDescription.Truncate();
+
+  if (sUseRws) {
+    nsCOMPtr<nsIRwsService> rwsSvc(do_GetService("@mozilla.org/rwsos2;1"));
+    if (!rwsSvc)
+      sUseRws = PR_FALSE;
+    else {
+      PRUint32 handle;
+      // the handle may be zero if the WPS class provides the default handler
+      if (NS_SUCCEEDED(rwsSvc->HandlerFromExtension(aFileExt, &handle, aDescription)))
+        return handle;
+    }
+  }
+
+  // no RWS or RWS failed to return at least a description
+  if (NS_FAILED(GetNLSString(NS_LITERAL_STRING("wpsDefaultOS2").get(),
+                             aDescription)))
+    aDescription.Assign(NS_LITERAL_STRING("WPS default"));
+
+  return 0;
+}
+
+//------------------------------------------------------------------------
+
+// this constructs the system-default entry when neither mailcap nor
+// mime.types provided any info
+
+static void
+WpsMimeInfoFromExtension(const char *aFileExt, nsMIMEInfoOS2 *aMI)
+{
+  // this identifies whether the mimetype is a bogus
+  // "application/octet-stream" generated when no match
+  // could be found for this extension
+  PRBool exists;
+  aMI->ExtensionExists(nsDependentCString(aFileExt), &exists);
+
+  // get the default app's description and WPS handle (if any)
+  nsAutoString ustr;
+  PRUint32 handle = WpsGetDefaultHandler(aFileExt, ustr);
+  aMI->SetDefaultDescription(ustr);
+  aMI->SetDefaultAppHandle(handle);
+
+  // if the mimeinfo is bogus, change the mimetype & extensions list
+  if (!exists) {
+    nsCAutoString extLower;
+    nsCAutoString cstr;
+    ToLowerCase(nsDependentCString(aFileExt), extLower);
+    cstr.Assign(NS_LITERAL_CSTRING("application/x-") + extLower);
+    aMI->SetMIMEType(cstr);
+    aMI->SetFileExtensions(extLower);
+  }
+
+  // if the mimetype is valid, perhaps we can supply a description;
+  // if it's bogus, replace the description
+  if (exists)
+    aMI->GetDescription(ustr);
+  else
+    ustr.Truncate();
+
+  if (ustr.IsEmpty()) {
+    nsCAutoString extUpper;
+    ToUpperCase(nsDependentCString(aFileExt), extUpper);
+    CopyUTF8toUTF16(extUpper, ustr);
+
+    nsAutoString fileType;
+    if (NS_FAILED(GetNLSString(NS_LITERAL_STRING("fileType").get(), fileType)))
+      ustr.Assign(NS_LITERAL_STRING("%S file"));
+    int pos = -1;
+    if ((pos = fileType.Find("%S")) > -1);
+      fileType.Replace(pos, 2, ustr);
+    aMI->SetDescription(fileType);
+  }
+}
+
+//------------------------------------------------------------------------
+
+// this is an override of nsExternalHelperAppService's method;
+// after the parent's method has looked for a handler, add
+// an entry for the WPS's handler if there's room and one exists;
+// it will never replace entries from mailcap or mimetypes.rdf
+
+NS_IMETHODIMP
+nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aMIMEType,
+                                              const nsACString& aFileExt,
+                                              nsIMIMEInfo **_retval)
+{
+  // let the existing code do its thing
+  nsresult rv = nsExternalHelperAppService::GetFromTypeAndExtension(
+                                            aMIMEType, aFileExt, _retval);
+  if (!(*_retval))
+    return rv;
+
+  // this is needed for Get/SetDefaultApplication()
+  nsMIMEInfoOS2 *mi = static_cast<nsMIMEInfoOS2*>(*_retval);
+
+  // do lookups using the original extension if present;
+  // otherwise use the extension derived from the mimetype
+  nsCAutoString ext;
+  if (!aFileExt.IsEmpty())
+    ext.Assign(aFileExt);
+  else {
+    mi->GetPrimaryExtension(ext);
+    if (ext.IsEmpty())
+      return rv;
+  }
+
+  nsCOMPtr<nsIFile> defApp;
+  nsCOMPtr<nsIHandlerApp> prefApp;
+  mi->GetDefaultApplication(getter_AddRefs(defApp));
+  mi->GetPreferredApplicationHandler(getter_AddRefs(prefApp));
+  nsCOMPtr<nsILocalHandlerApp> locPrefApp = do_QueryInterface(prefApp, &rv);
+
+  // if neither mailcap nor mimetypes.rdf had an entry,
+  // create a default entry using the WPS handler
+  if (!defApp && !locPrefApp) {
+    WpsMimeInfoFromExtension(ext.get(), mi);
+    return rv;
+  }
+
+  PRBool gotPromoted = PR_FALSE;
+
+  // both mailcap & mimetypes.rdf have an entry;  if they're
+  // different, exit;  otherwise, clear the default entry
+  if (defApp && locPrefApp) {
+    PRBool sameFile;
+    nsCOMPtr<nsIFile> app;
+    rv = locPrefApp->GetExecutable(getter_AddRefs(app));
+    defApp->Equals(app, &sameFile);
+    if (!sameFile)
+      return rv;
+
+    defApp = 0;
+    mi->SetDefaultApplication(0);
+    mi->SetDefaultDescription(EmptyString());
+    gotPromoted = PR_TRUE;
+  }
+
+  nsAutoString description;
+
+  // mailcap has an entry but mimetypes.rdf doesn't;
+  // promote mailcap's entry to preferred
+  if (defApp && !locPrefApp) {
+    mi->GetDefaultDescription(description);
+    nsLocalHandlerApp *handlerApp(new nsLocalHandlerApp(description, defApp));
+    mi->SetPreferredApplicationHandler(handlerApp);
+    gotPromoted = PR_TRUE;
+  }
+
+  // if the former default app was promoted to preferred app,
+  // update preferred action if appropriate
+  if (gotPromoted) {
+    nsHandlerInfoAction action;
+    mi->GetPreferredAction(&action);
+    if (action == nsIMIMEInfo::useSystemDefault) {
+      mi->SetPreferredAction(nsIMIMEInfo::useHelperApp);
+      mi->SetPreferredAction(nsIMIMEInfo::useHelperApp);
+    }
+  }
+
+  // use the WPS default as the system default handler
+  PRUint32 handle = WpsGetDefaultHandler(ext.get(), description);
+  mi->SetDefaultDescription(description);
+  mi->SetDefaultApplication(0);
+  mi->SetDefaultAppHandle(handle);
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
 
 already_AddRefed<nsIMIMEInfo>
 nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType,
                                         const nsACString& aFileExt,
                                         PRBool     *aFound) {
   *aFound = PR_TRUE;
   nsMIMEInfoOS2* retval = GetFromType(PromiseFlatCString(aType)).get();
   PRBool hasDefault = PR_FALSE;
--- a/uriloader/exthandler/os2/nsOSHelperAppService.h
+++ b/uriloader/exthandler/os2/nsOSHelperAppService.h
@@ -1,11 +1,11 @@
-/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 et cin: */
+/* ***** 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,
@@ -57,16 +57,19 @@ class nsMIMEInfoOS2;
 
 class nsOSHelperAppService : public nsExternalHelperAppService
 {
 public:
   nsOSHelperAppService();
   virtual ~nsOSHelperAppService();
 
   // method overrides for mime.types and mime.info look up steps
+  NS_IMETHOD GetFromTypeAndExtension(const nsACString& aMIMEType,
+                                     const nsACString& aFileExt,
+                                     nsIMIMEInfo **_retval);
   already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMimeType,
                                                   const nsACString& aFileExt,
                                                   PRBool     *aFound);
   already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
                                                          PRBool *found);
 
   // override nsIExternalProtocolService methods
   NS_IMETHODIMP GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
--- a/widget/public/Makefile.in
+++ b/widget/public/Makefile.in
@@ -113,12 +113,16 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 XPIDLSRCS	+= nsIPrintSettingsWin.idl
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 XPIDLSRCS	+= nsIPrintSettingsX.idl \
 		nsIPrintSessionX.idl
 endif
 
+ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
+XPIDLSRCS	+= nsIRwsService.idl
+endif
+
 EXPORTS		:= $(addprefix $(srcdir)/, $(EXPORTS))
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/widget/public/nsIRwsService.idl
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 OS/2 Remote Workplace Server interface.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard L Walsh.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(74a1105c-7e5a-418e-9a57-a0fc8cc992fc)]
+interface nsIRwsService : nsISupports
+{
+  /**
+   * Retrieves the icon given to files with the specified extension
+   *
+   * @param aExt        The extension without the leading dot;
+   *                    if the dot is present, it will be ignored.
+   * @param aNeedMini   Retrieve a mini icon if true; a standard icon if false.
+   * @return            An HPOINTER representing the icon.
+   */
+  unsigned long iconFromExtension(in string aExt, in boolean aNeedMini);
+
+  /**
+   * Retrieves the icon for a specific file or abstract object.
+   *
+   * @param aPath       The fully-qualified name of the file or object.
+   * @param aAbstract   False for filesystem objects, true for abstract objects.
+   * @param aNeedMini   Retrieve a mini icon if true; a standard icon if false.
+   * @return            An HPOINTER representing the icon.
+   */
+  unsigned long iconFromPath(in string aPath, in boolean aAbstract,
+                             in boolean aNeedMini);
+
+  /**
+   * Retrieves a file or abstract object's icon using its WPS object handle.
+   *
+   * @param aHandle     The file or object's WPS object handle.
+   * @param aNeedMini   Retrieve a mini icon if true; a standard icon if false.
+   * @return            An HPOINTER representing the icon.
+   */
+  unsigned long iconFromHandle(in unsigned long aHandle, in boolean aNeedMini);
+
+  /**
+   * Returns the name of the default handler for files with the
+   * specified extension.  If the handler is a program, it also
+   * retrieves the handler's WPS object handle;  if the default
+   * handler is provided by a WPS class, aHandle will be zero.
+   *
+   * @param aExt        The extension without the leading dot;
+   *                    if the dot is present, it will be ignored.
+   * @param aHandle     Returns a WPS object handle for programs or
+   *                    zero for class-based handlers.
+   * @return            Either the unqualified title of a program or
+   *                    the name of the WPS class' default viewer/player.
+   */
+  AString handlerFromExtension(in string aExt, out unsigned long aHandle);
+
+  /**
+   * Returns the name of the default handler for a specific file.
+   * If the handler is a program, it also retrieves the handler's
+   * WPS object handle;  if the default handler is provided by a
+   * WPS class, aHandle will be zero.
+   *
+   * @param aPath       The fully-qualified name of a file.
+   * @param aHandle     Returns a WPS object handle for programs or
+   *                    zero for class-based handlers.
+   * @return            Either the unqualified title of a program or
+   *                    the name of the WPS class' default viewer/player.
+   */
+  AString handlerFromPath(in string aPath, out unsigned long aHandle);
+
+  /**
+   * Returns the unqualified title of the specified object.
+   *
+   * @param aHandle     A WPS object handle.
+   * @return            The title of this object.
+   */
+  AString titleFromHandle(in unsigned long aHandle);
+
+  /**
+   * Displays the WPS object menu for the specified file or abstract object.
+   *
+   * @param aPath       The fully-qualified name of the file or object.
+   * @param aAbstract   False for filesystem objects, true for abstract objects.
+   */
+  void menuFromPath(in string aPath, in boolean aAbstract);
+
+  /**
+   * Opens a file using the specified program file.  The effect
+   * is identical to dropping the file on the program's icon.
+   *
+   * @param aFilePath   The fully-qualified name of a file.
+   * @param aAppPath    The fully-qualified name of a program file.
+   */
+  void openWithAppPath(in string aFilePath, in string aAppPath);
+
+  /**
+   * Opens a file using the specified WPS object handle.  The effect
+   * is identical to dropping the file on the object's icon.
+   *
+   * @param aFilePath   The fully-qualified name of a file.
+   * @param aAppHandle  A WPS object handle.
+   */
+  void openWithAppHandle(in string aFilePath, in unsigned long aAppHandle);
+};
+
--- a/widget/src/os2/Makefile.in
+++ b/widget/src/os2/Makefile.in
@@ -89,16 +89,17 @@ CPPSRCS		= \
 		nsToolkit.cpp \
 		nsWidgetFactory.cpp \
 		nsWindow.cpp \
 		nsDragService.cpp \
 		nsScreenOS2.cpp	\
 		nsScreenManagerOS2.cpp \
 		nsDeviceContextSpecOS2.cpp \
 		nsPrintOptionsOS2.cpp \
+		nsRwsService.cpp \
 		$(NULL)
 
 SHARED_LIBRARY_LIBS = \
 	../xpwidgets/$(LIB_PREFIX)xpwidgets_s.$(LIB_SUFFIX) \
 	$(NULL)
 
 EXTRA_DSO_LIBS	= gkgfx
 
new file mode 100644
--- /dev/null
+++ b/widget/src/os2/nsRwsService.cpp
@@ -0,0 +1,1259 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 OS/2 Remote Workplace Server interface.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard L Walsh.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+//------------------------------------------------------------------------
+
+#include "nsIFile.h"
+#include "nsIGenericFactory.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsLiteralString.h"
+#include "nsReadableUtils.h"
+#include "nsIStringBundle.h"
+
+#define INCL_WIN
+#define INCL_DOS
+#include <os2.h>
+
+// nsRwsService must be included _after_ os2.h
+#include "nsRwsService.h"
+#include "rwserr.h"
+#include "nsOS2Uni.h"
+
+#include <stdio.h>
+
+//------------------------------------------------------------------------
+//  debug macros
+//------------------------------------------------------------------------
+
+#ifdef DEBUG
+  #define RWS_DEBUG
+#endif
+
+// use this to force debug msgs in non-debug builds
+//#ifndef RWS_DEBUG
+//  #define RWS_DEBUG
+//#endif
+
+#ifdef RWS_DEBUG
+  #define ERRMSG(x,y)       { printf(y " failed - rc= %d\n", (int)x); }
+
+  #define ERRBREAK(x,y)     if (x) { ERRMSG(x,y); break; }
+
+  #define ERRPRINTF(x,y)    { printf(y "\n", x); }
+#else
+  #define ERRMSG(x,y)       ;
+
+  #define ERRBREAK(x,y)     if (x) break;
+
+  #define ERRPRINTF(x,y)    ;
+#endif
+
+//------------------------------------------------------------------------
+//  function prototypes
+//------------------------------------------------------------------------
+
+static nsresult IsDescendedFrom(PRUint32 wpsFilePtr, char *pszClassname);
+static nsresult CreateFileForExtension(const char *aFileExt, nsACString& aPath);
+static nsresult DeleteFileForExtension(const char *aPath);
+static void     AssignNLSString(const PRUnichar *aKey, nsAString& _retval);
+static nsresult AssignTitleString(const char *aTitle, nsAString& result);
+
+//------------------------------------------------------------------------
+//  module-level variables - not declared as members of nsRws
+//------------------------------------------------------------------------
+
+static nsRwsService *sRwsInstance = 0;   // our singleton instance
+static PRBool        sInit = FALSE;      // have we tried to load RWS
+
+// RWS administrative functions
+static ULONG (* _System sRwsClientInit)(BOOL);
+static ULONG (* _System sRwsGetTimeout)(PULONG, PULONG);
+static ULONG (* _System sRwsSetTimeout)(ULONG);
+static ULONG (* _System sRwsClientTerminate)(void);
+
+// RWS task-oriented functions
+static ULONG (* _System sRwsCall)(PRWSHDR*, ULONG, ULONG, ULONG, ULONG, ULONG, ...);
+static ULONG (* _System sRwsCallIndirect)(PRWSBLD);
+static ULONG (* _System sRwsFreeMem)(PRWSHDR);
+static ULONG (* _System sRwsGetResult)(PRWSHDR, ULONG, PULONG);
+static ULONG (* _System sRwsGetArgPtr)(PRWSHDR, ULONG, PRWSDSC*);
+
+//------------------------------------------------------------------------
+//  ExtCache - caches the default icons & handlers for file extensions
+//------------------------------------------------------------------------
+
+typedef struct _ExtInfo
+{
+  char       ext[8];
+  PRUint32   icon;
+  PRUint32   mini;
+  PRUint32   handler;
+  PRUnichar *title;
+} ExtInfo;
+
+#define kGrowBy         8
+#define kMutexTimeout   500
+
+class ExtCache
+{
+public:
+  ExtCache();
+  ~ExtCache();
+
+  nsresult GetIcon(const char *aExt, PRBool aNeedMini, PRUint32 *oIcon);
+  nsresult SetIcon(const char *aExt, PRBool aIsMini, PRUint32 aIcon);
+  nsresult GetHandler(const char *aExt, PRUint32 *oHandle, nsAString& oTitle);
+  nsresult SetHandler(const char *aExt, PRUint32 aHandle, nsAString& aTitle);
+  void     EmptyCache();
+
+protected:
+  ExtInfo *FindExtension(const char *aExt, PRBool aSet = PR_FALSE);
+
+  PRUint32 mPid;
+  PRUint32 mMutex;
+  PRUint32 mCount;
+  PRUint32 mSize;
+  ExtInfo *mExtInfo;
+};
+
+//------------------------------------------------------------------------
+//  nsIRwsService implementation
+//------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS2(nsRwsService, nsIRwsService, nsIObserver)
+
+nsRwsService::nsRwsService()
+{
+  mExtCache = new ExtCache();
+  if (!mExtCache)
+    ERRPRINTF("", "nsRwsService - unable to allocate mExtArray%s");
+}
+
+nsRwsService::~nsRwsService()
+{
+}
+
+//------------------------------------------------------------------------
+
+// provides the default icon associated with a given extension;  if the
+// icon isn't in the cache, it creates a temp file with that extension,
+// retrieves its icon, deletes the temp file, then caches the icon
+
+NS_IMETHODIMP
+nsRwsService::IconFromExtension(const char *aExt, PRBool aNeedMini,
+                                PRUint32 *_retval)
+{
+  if (!aExt || !*aExt || !_retval)
+    return NS_ERROR_INVALID_ARG;
+
+  nsresult rv = mExtCache->GetIcon(aExt, aNeedMini, _retval);
+  if (NS_SUCCEEDED(rv))
+    return rv;
+
+  nsCAutoString path;
+  rv = CreateFileForExtension(aExt, path);
+  if (NS_SUCCEEDED(rv)) {
+    rv = IconFromPath(path.get(), PR_FALSE, aNeedMini, _retval);
+    DeleteFileForExtension(path.get());
+    if (NS_SUCCEEDED(rv))
+      mExtCache->SetIcon(aExt, aNeedMini, *_retval);
+  }
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// retrieves an object's icon using its fully-qualified path;  aAbstract
+// indicates whether this is a Filesystem object or an Abstract or
+// Transient object;  locating non-file objects is fairly expensive
+
+NS_IMETHODIMP
+nsRwsService::IconFromPath(const char *aPath, PRBool aAbstract,
+                           PRBool aNeedMini, PRUint32 *_retval)
+{
+  if (!aPath || !*aPath || !_retval)
+    return NS_ERROR_INVALID_ARG;
+
+  PRUint32  rwsType;
+
+  if (aAbstract)
+    rwsType = (aNeedMini ? RWSC_OFTITLE_OMINI : RWSC_OFTITLE_OICON);
+  else
+    rwsType = (aNeedMini ? RWSC_OPATH_OMINI : RWSC_OPATH_OICON);
+
+  return RwsConvert(rwsType, (PRUint32)aPath, _retval);
+}
+
+//------------------------------------------------------------------------
+
+// retrieve's an object's icon using its persistent handle
+
+NS_IMETHODIMP
+nsRwsService::IconFromHandle(PRUint32 aHandle, PRBool aNeedMini,
+                             PRUint32 *_retval)
+{
+  if (!aHandle || !_retval)
+    return NS_ERROR_INVALID_ARG;
+
+  return RwsConvert( (aNeedMini ? RWSC_OHNDL_OMINI : RWSC_OHNDL_OICON),
+                     aHandle, _retval);
+}
+
+//------------------------------------------------------------------------
+
+// retrieve's an object's title using its persistent handle
+
+NS_IMETHODIMP
+nsRwsService::TitleFromHandle(PRUint32 aHandle, nsAString& _retval)
+{
+  if (!aHandle)
+    return NS_ERROR_INVALID_ARG;
+
+  return RwsConvert(RWSC_OHNDL_OTITLE, aHandle, _retval);
+}
+
+//------------------------------------------------------------------------
+
+// provides the default handler associated with a given extension;  if the
+// info isn't in the cache, it creates a temp file with that extension,
+// retrieves the handler's title & possibly its object handle, deletes the
+// temp file, then caches the info.
+
+NS_IMETHODIMP
+nsRwsService::HandlerFromExtension(const char *aExt, PRUint32 *aHandle,
+                                   nsAString& _retval)
+{
+  if (!aExt || !*aExt || !aHandle)
+    return NS_ERROR_INVALID_ARG;
+
+  nsresult rv = mExtCache->GetHandler(aExt, aHandle, _retval);
+  if (NS_SUCCEEDED(rv))
+    return rv;
+
+  nsCAutoString path;
+  rv = CreateFileForExtension(aExt, path);
+  if (NS_SUCCEEDED(rv)) {
+    rv = HandlerFromPath(path.get(), aHandle, _retval);
+    DeleteFileForExtension(path.get());
+    if (NS_SUCCEEDED(rv))
+      mExtCache->SetHandler(aExt, *aHandle, _retval);
+  }
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// identifies the default WPS handler for a given file;  if the handler
+// is an associated program, returns its title & object handle;  if the
+// handler is an integrated class viewer or player, it constructs a title
+// and sets the object handle to zero
+
+NS_IMETHODIMP
+nsRwsService::HandlerFromPath(const char *aPath, PRUint32 *aHandle,
+                              nsAString& _retval)
+{
+  if (!aPath || !*aPath || !aHandle)
+    return NS_ERROR_INVALID_ARG;
+
+  nsresult  rv = NS_ERROR_FAILURE;
+  PRWSHDR   pHdr = 0;
+  PRUint32  rc;
+
+  _retval.Truncate();
+  *aHandle = 0;
+
+  // this dummy do{...}while(0) loop lets us bail out early
+  // while ensuring pHdr gets freed
+  do {
+    // get the default view for this file
+    rc = sRwsCall(&pHdr,
+                  RWSP_MNAM, (ULONG)"wpQueryDefaultView",
+                  RWSR_ASIS,  0, 1,
+                  RWSI_OPATH, 0, (ULONG)aPath);
+    ERRBREAK(rc, "wpQueryDefaultView")
+
+    PRUint32 defView = sRwsGetResult(pHdr, 0, 0);
+    if (defView == (PRUint32)-1)
+      break;
+
+    // if the default view is OPEN_SETTINGS ('2'),
+    // change it to OPEN_RUNNING ('4')
+    if (defView == 2)
+      defView = 4;
+
+    // to improve efficiency, retrieve & reuse the file's wpObject
+    // ptr rather than repeatedly converting the name to an object
+    PRUint32 wpsFilePtr = sRwsGetResult(pHdr, 1, 0);
+
+    // free pHdr before the next call
+    sRwsFreeMem(pHdr);
+    pHdr = 0;
+
+    // for default view values less than OPEN_USER, see if there
+    // is a program or program object associated with this file
+    if (defView < 0x6500) {
+      rc = sRwsCall(&pHdr,
+                    RWSP_MNAM, (ULONG)"wpQueryAssociatedProgram",
+                    RWSR_OHNDL,  0, 6,
+                    RWSI_ASIS,   0, wpsFilePtr,
+                    RWSI_ASIS,   0, defView,
+                    RWSI_PULONG, 0, 0,
+                    RWSI_PBUF,  32, 0,
+                    RWSI_ASIS,   0, 32,
+                    RWSI_ASIS,   0, (ULONG)-1);
+
+      // if there's no associated program, create dummy values
+      // (if chosen, the WPS will open the file's Properties notebook)
+      if (rc) {
+        if (rc == RWSSRV_FUNCTIONFAILED) {
+          *aHandle = 0;
+          AssignNLSString(NS_LITERAL_STRING("wpsDefaultOS2").get(), _retval);
+          rv = NS_OK;
+        }
+        else
+          ERRMSG(rc, "wpQueryAssociatedProgram")
+        break;
+      }
+
+      // get a ptr to the return value's data structure;  then get
+      // both the object handle we requested & the raw WPS object ptr
+      PRWSDSC pRtn;
+      rc = sRwsGetArgPtr(pHdr, 0, &pRtn);
+      ERRBREAK(rc, "GetArgPtr")
+      *aHandle = *((PRUint32*)pRtn->pget);
+      PRUint32 wpsPgmPtr = pRtn->value;
+
+      // free pHdr before the next call
+      sRwsFreeMem(pHdr);
+      pHdr = 0;
+
+      // convert the object to its title (using Rws' conversion
+      // feature is more efficient than calling wpQueryTitle)
+      rc = sRwsCall(&pHdr,
+                    RWSP_CONV, 0,
+                    RWSR_ASIS, 0, 1,
+                    RWSC_OBJ_OTITLE, 0, wpsPgmPtr);
+      ERRBREAK(rc, "convert pgm object to title")
+
+      // retrieve the title, massage it & assign to _retval, then exit
+      // N.B. no need to free pHdr here, it will be freed below
+      char *pszTitle = (char*)sRwsGetResult(pHdr, 1, 0);
+      if (pszTitle != (char*)-1)
+        rv = AssignTitleString(pszTitle, _retval);
+
+      break;
+    }
+
+    // the default view is provided by the file's WPS class;
+    // in this case, *aHandle is 0
+
+    // see if it's a known view that can be given a meaningful name
+    switch (defView) {
+      case 0xbc2b: {
+        rv = IsDescendedFrom(wpsFilePtr, "MMImage");
+        if (NS_SUCCEEDED(rv))
+          AssignNLSString(NS_LITERAL_STRING("mmImageViewerOS2").get(), _retval);
+        break;
+      }
+
+      case 0xbc0d:    // Audio editor
+      case 0xbc21:    // Video editor
+      case 0xbc17:    // Midi editor
+      case 0xbbef:    // Play
+      case 0xbbe5: {  // Player
+        rv = IsDescendedFrom(wpsFilePtr, "MMAudio");
+        if (NS_SUCCEEDED(rv)) {
+          AssignNLSString(NS_LITERAL_STRING("mmAudioPlayerOS2").get(), _retval);
+          break;
+        }
+
+        rv = IsDescendedFrom(wpsFilePtr, "MMVideo");
+        if (NS_SUCCEEDED(rv)) {
+          AssignNLSString(NS_LITERAL_STRING("mmVideoPlayerOS2").get(), _retval);
+          break;
+        }
+
+        rv = IsDescendedFrom(wpsFilePtr, "MMMIDI");
+        if (NS_SUCCEEDED(rv))
+          AssignNLSString(NS_LITERAL_STRING("mmMidiPlayerOS2").get(), _retval);
+
+        break;
+      }
+
+      case 0x7701: {
+        rv = IsDescendedFrom(wpsFilePtr, "TSArcMgr");
+        if (NS_SUCCEEDED(rv))
+          AssignNLSString(NS_LITERAL_STRING("odZipFolderOS2").get(), _retval);
+        break;
+      }
+
+      // this has to go last because TSEnhDataFile replaces
+      // WPDataFile; if present, all data files are descended from it
+      case 0xb742:
+      case 0xa742: {
+        rv = IsDescendedFrom(wpsFilePtr, "TSEnhDataFile");
+        if (NS_SUCCEEDED(rv))
+          AssignNLSString(NS_LITERAL_STRING("odTextViewOS2").get(), _retval);
+        break;
+      }
+    } // end switch
+
+    if (NS_SUCCEEDED(rv))
+      break;
+
+    // the name of this view is unknown, so create a generic name
+    // (i.e. "Viewer for Class WPSomething")
+
+    // use the file's object ptr to get the name of its class
+    rc = sRwsCall(&pHdr,
+                  RWSP_CONV, 0,
+                  RWSR_ASIS, 0, 1,
+                  RWSC_OBJ_CNAME, 0, wpsFilePtr);
+    ERRBREAK(rc, "convert object to classname")
+
+    char *pszTitle = (char*)sRwsGetResult(pHdr, 1, 0);
+    if (pszTitle == (char*)-1)
+      break;
+
+    nsAutoChar16Buffer buffer;
+    PRInt32 bufLength;
+    rv = MultiByteToWideChar(0, pszTitle, strlen(pszTitle),
+                             buffer, bufLength);
+    if (NS_FAILED(rv))
+      break;
+
+    nsAutoString classViewer;
+    AssignNLSString(NS_LITERAL_STRING("classViewerOS2").get(), classViewer);
+    int pos = -1;
+    if ((pos = classViewer.Find("%S")) > -1);
+      classViewer.Replace(pos, 2, buffer.Elements());
+    _retval.Assign(classViewer);
+    rv = NS_OK;
+  } while (0);
+
+  // free the pHdr allocated by the final call
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// retrieves an object's menu using its fully-qualified path;  aAbstract
+// indicates whether this is a Filesystem object or an Abstract or
+// Transient object;  locating non-file objects is fairly expensive
+
+NS_IMETHODIMP
+nsRwsService::MenuFromPath(const char *aPath, PRBool aAbstract)
+{
+  if (!aPath || !*aPath)
+    return NS_ERROR_INVALID_ARG;
+
+  nsresult  rv = NS_ERROR_FAILURE;
+  PRWSHDR   pHdr = 0;
+  PRUint32  type = (aAbstract ? RWSI_OFTITLE : RWSI_OPATH);
+  PRUint32  rc;
+  POINTL    ptl;
+  HWND      hTgt = 0;
+
+  // try to identify the window where the click occurred
+  if (WinQueryMsgPos(0, &ptl)) {
+    hTgt = WinQueryFocus(HWND_DESKTOP);
+    if (hTgt)
+      WinMapWindowPoints(HWND_DESKTOP, hTgt, &ptl, 1);
+  }
+
+  // if we have the window & coordinates, use them;  otherwise,
+  // let RWS position the menu at the current pointer position
+  // (specifying the window ensures the focus will return to it)
+  if (hTgt)
+      rc = sRwsCall(&pHdr,
+                    RWSP_CMD,  RWSCMD_POPUPMENU,
+                    RWSR_ASIS, 0, 3,
+                    type,      0, (ULONG)aPath,
+                    RWSI_ASIS, 0, hTgt,
+                    RWSI_PBUF, sizeof(POINTL), (ULONG)&ptl);
+  else
+      rc = sRwsCall(&pHdr,
+                    RWSP_CMD,  RWSCMD_POPUPMENU,
+                    RWSR_ASIS, 0, 3,
+                    type,      0, (ULONG)aPath,
+                    RWSI_ASIS, 0, 0,
+                    RWSI_ASIS, 0, 0);
+
+  if (rc)
+    ERRMSG(rc, "RWSCMD_POPUPMENU")
+  else
+    rv = NS_OK;
+
+  // free pHdr
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// causes the WPS to open a file using the program specified by AppPath;
+// this is identical to dropping the file on the program's WPS icon
+
+NS_IMETHODIMP
+nsRwsService::OpenWithAppHandle(const char *aFilePath, PRUint32 aAppHandle)
+{
+  if (!aFilePath || !*aFilePath || !aAppHandle)
+    return NS_ERROR_INVALID_ARG;
+
+  nsresult  rv = NS_ERROR_FAILURE;
+  PRWSHDR   pHdr = 0;
+  PRUint32  rc;
+
+  rc = sRwsCall(&pHdr,
+                RWSP_CMD,   RWSCMD_OPENUSING,
+                RWSR_ASIS,  0, 2,
+                RWSI_OPATH, 0, aFilePath,
+                RWSI_OHNDL, 0, aAppHandle);
+  if (rc)
+    ERRMSG(rc, "RWSCMD_OPENUSING")
+  else
+    rv = NS_OK;
+
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// causes the WPS to open a file using the program specified by AppPath;
+// this is identical to dropping the file on the program's WPS icon
+
+NS_IMETHODIMP
+nsRwsService::OpenWithAppPath(const char *aFilePath, const char *aAppPath)
+{
+  if (!aFilePath || !*aFilePath || !aAppPath || !*aAppPath)
+    return NS_ERROR_INVALID_ARG;
+
+  nsresult  rv = NS_ERROR_FAILURE;
+  PRWSHDR   pHdr = 0;
+  PRUint32  rc;
+
+  rc = sRwsCall(&pHdr,
+                RWSP_CMD,   RWSCMD_OPENUSING,
+                RWSR_ASIS,  0, 2,
+                RWSI_OPATH, 0, aFilePath,
+                RWSI_OPATH, 0, aAppPath);
+  if (rc)
+    ERRMSG(rc, "RWSCMD_OPENUSING")
+  else
+    rv = NS_OK;
+
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+//  nsRwsService additional methods
+//------------------------------------------------------------------------
+
+// uses RwsConvert to retrieve a result that can be handled as a ULONG;
+// type identifies the conversion, value can be anything (ULONG, char*, etc)
+
+//static
+nsresult
+nsRwsService::RwsConvert(PRUint32 type, PRUint32 value, PRUint32 *result)
+{
+  nsresult  rv = NS_ERROR_FAILURE;
+  PRWSHDR   pHdr = 0;
+
+  *result = 0;
+  PRUint32 rc = sRwsCall(&pHdr,
+                         RWSP_CONV, 0,
+                         RWSR_ASIS, 0, 1,
+                         type,      0, value);
+
+  if (rc)
+    ERRMSG(rc, "RwsConvert to ULONG")
+  else {
+    *result = sRwsGetResult(pHdr, 1, 0);
+    if (*result == (PRUint32)-1)
+      *result = 0;
+    else
+      rv = NS_OK;
+  }
+
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// uses RwsConvert to retrieve a result that can be handled as a
+// Unicode string;  type identifies the conversion, value can be
+// anything (ULONG, char*, etc)
+
+//static
+nsresult
+nsRwsService::RwsConvert(PRUint32 type, PRUint32 value, nsAString& result)
+{
+  nsresult  rv = NS_ERROR_FAILURE;
+  PRWSHDR   pHdr = 0;
+
+  result.Truncate();
+  PRUint32 rc = sRwsCall(&pHdr,
+                         RWSP_CONV, 0,
+                         RWSR_ASIS, 0, 1,
+                         type,      0, value);
+
+  if (rc)
+    ERRMSG(rc, "RwsConvert to string")
+  else {
+    char *string = (char*)sRwsGetResult(pHdr, 1, 0);
+    if (string != (char*)-1)
+      rv = AssignTitleString(string, result);
+  }
+
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+//  nsIObserver implementation
+//------------------------------------------------------------------------
+
+// when the app shuts down, advise RwsClient;  if this is the
+// last app using RwsServer, the WPS will unload the class's dll;
+// also, empty the extension cache to unlock & delete its icons
+
+NS_IMETHODIMP
+nsRwsService::Observe(nsISupports *aSubject, const char *aTopic,
+                      const PRUnichar *aSomeData)
+{
+  if (strcmp(aTopic, "quit-application") == 0) {
+    PRUint32 rc = sRwsClientTerminate();
+    if (rc)
+        ERRMSG(rc, "RwsClientTerminate");
+
+    if (mExtCache)
+      mExtCache->EmptyCache();
+  }
+
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------
+// nsRwsService static helper functions
+//------------------------------------------------------------------------
+
+// this wrapper for somIsA() makes HandlerFromPath() easier to read
+
+static nsresult IsDescendedFrom(PRUint32 wpsFilePtr, char *pszClassname)
+{
+  PRWSHDR   pHdr = 0;
+  nsresult  rv = NS_ERROR_FAILURE;
+
+  PRUint32 rc = sRwsCall(&pHdr,
+                         RWSP_MNAMI, (ULONG)"somIsA",
+                         RWSR_ASIS,  0, 2,
+                         RWSI_ASIS,  0, wpsFilePtr,
+                         RWSI_CNAME, 0, pszClassname);
+
+  if (rc)
+    ERRMSG(rc, "somIsA")
+  else
+    if (sRwsGetResult(pHdr, 0, 0) == TRUE)
+      rv = NS_OK;
+
+  sRwsFreeMem(pHdr);
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// create a temp file with the specified extension, then return its path
+
+static nsresult CreateFileForExtension(const char *aFileExt,
+                                       nsACString& aPath)
+{
+  nsresult rv = NS_ERROR_FAILURE;
+  aPath.Truncate();
+
+  nsCOMPtr<nsIFile> tempFile;
+  rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempFile));
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCAutoString pathStr(NS_LITERAL_CSTRING("nsrws."));
+  if (*aFileExt == '.')
+    aFileExt++;
+  pathStr.Append(aFileExt);
+
+  rv = tempFile->AppendNative(pathStr);
+  if (NS_FAILED(rv))
+    return rv;
+
+  tempFile->GetNativePath(pathStr);
+
+  FILE *fp = fopen(pathStr.get(), "wb+");
+  if (fp) {
+    fclose(fp);
+    aPath.Assign(pathStr);
+    rv = NS_OK;
+  }
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// delete a temp file created earlier
+
+static nsresult DeleteFileForExtension(const char *aPath)
+{
+  if (!aPath || !*aPath)
+    return NS_ERROR_INVALID_ARG;
+
+  remove(aPath);
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------
+
+// returns a localized string from unknownContentType.properties;
+// if there's a failure, returns "WPS Default"
+
+static void AssignNLSString(const PRUnichar *aKey, nsAString& result)
+{
+  nsresult      rv;
+  nsXPIDLString title;
+
+  do {
+    nsCOMPtr<nsIStringBundleService> bundleSvc =
+      do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+    if (NS_FAILED(rv))
+      break;
+
+    nsCOMPtr<nsIStringBundle> bundle;
+    rv = bundleSvc->CreateBundle(
+      "chrome://mozapps/locale/downloads/unknownContentType.properties",
+      getter_AddRefs(bundle));
+    if (NS_FAILED(rv))
+      break;
+
+    // if we can't fetch the requested string, try to get "WPS Default"
+    rv = bundle->GetStringFromName(aKey, getter_Copies(title));
+    if (NS_FAILED(rv))
+      rv = bundle->GetStringFromName(NS_LITERAL_STRING("wpsDefaultOS2").get(),
+                                     getter_Copies(title));
+  } while (0);
+
+  if (NS_SUCCEEDED(rv))
+    result.Assign(title);
+  else
+    result.Assign(NS_LITERAL_STRING("WPS Default"));
+}
+
+//------------------------------------------------------------------------
+
+// converts a native string (presumably a WPS object's title) to
+// Unicode, removes line breaks, and compresses whitespace
+
+static nsresult AssignTitleString(const char *aTitle, nsAString& result)
+{
+  nsAutoChar16Buffer buffer;
+  PRInt32 bufLength;
+
+  // convert the title to Unicode
+  if (NS_FAILED(MultiByteToWideChar(0, aTitle, strlen(aTitle),
+                                      buffer, bufLength)))
+    return NS_ERROR_FAILURE;
+
+  PRUnichar *pSrc;
+  PRUnichar *pDst;
+  PRBool     fSkip;
+
+  // remove line breaks, leading whitespace, & extra embedded whitespace
+  // (primitive, but gcc 3.2.2 doesn't support wchar)
+  for (fSkip=PR_TRUE, pSrc=pDst=buffer.Elements(); *pSrc; pSrc++) {
+    if (*pSrc == ' ' || *pSrc == '\r' || *pSrc == '\n' || *pSrc == '\t') {
+      if (!fSkip)
+        *pDst++ = ' ';
+      fSkip = PR_TRUE;
+    }
+    else {
+      if (pDst != pSrc)
+        *pDst = *pSrc;
+      pDst++;
+      fSkip = PR_FALSE;
+    }
+  }
+
+  // remove the single trailing space, if needed
+  if (fSkip && pDst > buffer.Elements())
+    pDst--;
+
+  *pDst = 0;
+  result.Assign(buffer.Elements());
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------
+//  ExtCache implementation
+//------------------------------------------------------------------------
+
+ExtCache::ExtCache() : mCount(0), mSize(0), mExtInfo(0)
+{
+  PTIB  ptib;
+  PPIB  ppib;
+
+  // the PID is needed to lock/unlock icons
+  DosGetInfoBlocks(&ptib, &ppib);
+  mPid = ppib->pib_ulpid;
+
+  PRUint32 rc = DosCreateMutexSem(0, (PHMTX)&mMutex, 0, 0);
+  if (rc)
+    ERRMSG(rc, "DosCreateMutexSem")
+}
+
+ExtCache::~ExtCache() {};
+
+//------------------------------------------------------------------------
+
+// retrieve the WPS's default icon for files with this extension
+
+nsresult ExtCache::GetIcon(const char *aExt, PRBool aNeedMini,
+                           PRUint32 *oIcon)
+{
+  PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
+  if (rc) {
+    ERRMSG(rc, "DosRequestMutexSem")
+    return NS_ERROR_FAILURE;
+  }
+
+  ExtInfo *info = FindExtension(aExt);
+
+  if (info) {
+    if (aNeedMini)
+      *oIcon = info->mini;
+    else
+      *oIcon = info->icon;
+  }
+  else
+    *oIcon = 0;
+
+  rc = DosReleaseMutexSem(mMutex);
+  if (rc)
+    ERRMSG(rc, "DosReleaseMutexSem")
+
+  return (*oIcon ? NS_OK : NS_ERROR_FAILURE);
+}
+
+//------------------------------------------------------------------------
+
+// save the WPS's default icon for files with this extension
+
+nsresult ExtCache::SetIcon(const char *aExt, PRBool aIsMini,
+                           PRUint32 aIcon)
+{
+  PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
+  if (rc) {
+    ERRMSG(rc, "DosRequestMutexSem")
+    return NS_ERROR_FAILURE;
+  }
+
+  ExtInfo *info = FindExtension(aExt, PR_TRUE);
+  if (!info)
+    return NS_ERROR_FAILURE;
+
+  // the icon has to be made non-deletable or else
+  // it will be destroyed if the WPS terminates
+  if (!WinSetPointerOwner(aIcon, mPid, FALSE)) {
+    ERRPRINTF(info->ext, "WinSetPointerOwner failed for %s icon")
+    return NS_ERROR_FAILURE;
+  }
+
+  if (aIsMini)
+    info->mini = aIcon;
+  else
+    info->icon = aIcon;
+
+  ERRPRINTF(info->ext, "ExtCache - added icon for %s")
+
+  rc = DosReleaseMutexSem(mMutex);
+  if (rc)
+    ERRMSG(rc, "DosReleaseMutexSem")
+
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------
+
+// retrieve the WPS default handler's title & object handle (if any)
+
+nsresult ExtCache::GetHandler(const char *aExt, PRUint32 *oHandle,
+                              nsAString& oTitle)
+{
+  PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
+  if (rc) {
+    ERRMSG(rc, "DosRequestMutexSem")
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = NS_ERROR_FAILURE;
+  ExtInfo *info = FindExtension(aExt);
+
+  // if there's no title, the handle isn't useful
+  if (info && info->title) {
+    oTitle.Assign(info->title);
+    *oHandle = info->handler;
+    rv = NS_OK;
+  }
+
+  rc = DosReleaseMutexSem(mMutex);
+  if (rc)
+    ERRMSG(rc, "DosReleaseMutexSem")
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// save the WPS default handler's title & object handle (if any)
+
+nsresult ExtCache::SetHandler(const char *aExt, PRUint32 aHandle,
+                              nsAString& aTitle)
+{
+  PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
+  if (rc) {
+    ERRMSG(rc, "DosRequestMutexSem")
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = NS_ERROR_FAILURE;
+  ExtInfo *info = FindExtension(aExt, PR_TRUE);
+
+  // if the title can't be saved, don't save the handle
+  if (info) {
+    info->title = ToNewUnicode(aTitle);
+    if (info->title) {
+      info->handler = aHandle;
+      rv = NS_OK;
+      ERRPRINTF(info->ext, "ExtCache - added handler for %s")
+    }
+  }
+
+  rc = DosReleaseMutexSem(mMutex);
+  if (rc)
+    ERRMSG(rc, "DosReleaseMutexSem")
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
+
+// find the entry for the requested extension;  if not found,
+// create a new entry, expanding the array as needed
+
+ExtInfo *ExtCache::FindExtension(const char *aExt, PRBool aSet)
+{
+  // eliminate any leading dot & null extensions
+  if (*aExt == '.')
+    aExt++;
+  if (*aExt == 0)
+    return 0;
+
+  // too long to cache
+  if (strlen(aExt) >= 8)
+    return 0;
+
+  // uppercase extension, then confirm it still fits
+  char extUpper[16];
+  strcpy(extUpper, aExt);
+  // XXX WinUpper() can crash with high-memory
+  //     could we just store as-is and instead use stricmp() for
+  //     compare operations?
+  if (WinUpper(0, 0, 0, extUpper) >= 8)
+    return 0;
+
+  // a minor optimization:  if we're setting a value, it probably
+  // belongs to the entry added most recently (i.e. the last one)
+  if (aSet && mCount && !strcmp(extUpper, (&mExtInfo[mCount-1])->ext))
+    return &mExtInfo[mCount-1];
+
+  ExtInfo *info;
+  PRUint32  ctr;
+
+  // look for the extension in the array, return if found
+  for (ctr = 0, info = mExtInfo; ctr < mCount; ctr++, info++)
+    if (!strcmp(extUpper, info->ext))
+      return info;
+
+  // if a new entry won't fit into the current array, realloc
+  if (mCount >= mSize) {
+    PRUint32 newSize = mSize + kGrowBy;
+    info = (ExtInfo*) NS_Realloc(mExtInfo, newSize * sizeof(ExtInfo));
+    if (!info)
+      return 0;
+
+    memset(&info[mSize], 0, kGrowBy * sizeof(ExtInfo));
+    mExtInfo = info;
+    mSize = newSize;
+  }
+
+  // point at the next entry & store the extension
+  info = &mExtInfo[mCount++];
+  strcpy(info->ext, extUpper);
+
+  return info;
+}
+
+//------------------------------------------------------------------------
+
+// clear out the cache - since this is only called at shutdown,
+// the primary concern is that the icons get unlocked & deleted
+
+void ExtCache::EmptyCache()
+{
+  if (!mExtInfo)
+    return;
+
+  PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
+  if (rc) {
+    ERRMSG(rc, "DosRequestMutexSem")
+    return;
+  }
+
+  PRUint32  saveMutex = mMutex;
+  mMutex = 0;
+
+  PRUint32 ctr;
+  ExtInfo *info;
+
+  for (ctr = 0, info = mExtInfo; ctr < mCount; ctr++, info++) {
+
+    ERRPRINTF(info->ext, "ExtCache - deleting entry for %s")
+
+    if (info->icon) {
+      if (WinSetPointerOwner(info->icon, mPid, TRUE) == FALSE ||
+          WinDestroyPointer(info->icon) == FALSE)
+        ERRPRINTF(info->ext, "unable to destroy icon for %s")
+    }
+
+    if (info->mini) {
+      if (WinSetPointerOwner(info->mini, mPid, TRUE) == FALSE ||
+          WinDestroyPointer(info->mini) == FALSE)
+        ERRPRINTF(info->ext, "unable to destroy mini for %s")
+    }
+
+    if (info->title)
+      NS_Free(info->title);
+  }
+
+  mCount = 0;
+  mSize = 0;
+  NS_Free(mExtInfo);
+  mExtInfo = 0;
+
+  rc = DosReleaseMutexSem(saveMutex);
+  if (rc)
+    ERRMSG(rc, "DosReleaseMutexSem")
+  rc = DosCloseMutexSem(saveMutex);
+  if (rc)
+    ERRMSG(rc, "DosCloseMutexSem")
+}
+
+//------------------------------------------------------------------------
+//  Module & Factory stuff
+//------------------------------------------------------------------------
+
+// this is the "getter proc" for nsRwsServiceConstructor();  it makes a
+// single attempt to load the RWS libraries and, if successful, creates
+// our singleton object;  thereafter, it returns the existing object or
+// NS_ERROR_NOT_AVAILABLE
+
+static nsresult nsRwsServiceInit(nsRwsService **aClass)
+{
+  // init already done - return what we've got or an error
+  if (sInit) {
+    *aClass = sRwsInstance;
+    if (*aClass == 0)
+      return NS_ERROR_NOT_AVAILABLE;
+
+    NS_ADDREF(*aClass);
+    return NS_OK;
+  }
+
+  sInit = TRUE;
+  *aClass = 0;
+
+  char      errBuf[16];
+  HMODULE   hmod;
+
+  // try to load RwsCliXX.dll;  first, see if the RWS WPS class is
+  // registered by f/q name - if so, look for RwsCli in the same
+  // directory;  the goal is to consistently use the same pair of
+  // dlls if the user has multiple copies
+
+  PRUint32  rc = 1;
+
+  // get the list of registered WPS classes
+  ULONG  cbClass;
+  if (!WinEnumObjectClasses(NULL, &cbClass))
+    return NS_ERROR_NOT_AVAILABLE;
+
+  char *pBuf = (char*)NS_Alloc(cbClass + CCHMAXPATH);
+  if (!pBuf)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  POBJCLASS pClass = (POBJCLASS)&pBuf[CCHMAXPATH];
+  if (!WinEnumObjectClasses(pClass, &cbClass)) {
+    NS_Free(pBuf);
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // look for RWSxx
+  while (pClass) {
+    if (!strcmp(pClass->pszClassName, RWSCLASSNAME))
+      break;
+    pClass = pClass->pNext;
+  }
+
+  // if the class was found & it was registered with a f/q name,
+  // try to load RwsCliXX from the same directory
+  if (pClass && pClass->pszModName[1] == ':') {
+    strcpy(pBuf, pClass->pszModName);
+    char *ptr = strrchr(pBuf, '\\');
+    if (ptr) {
+      strcpy(ptr+1, RWSCLIDLL);
+      rc = DosLoadModule(errBuf, sizeof(errBuf), pBuf, &hmod);
+    }
+  }
+  NS_Free(pBuf);
+
+  // if RwsCli couldn't be found, look for it on the LIBPATH
+  if (rc)
+    rc = DosLoadModule(errBuf, sizeof(errBuf), RWSCLIMOD, &hmod);
+
+  // the dll couldn't be found, so exit
+  if (rc) {
+    ERRPRINTF(RWSCLIDLL, "nsRwsServiceInit - unable to locate %s");
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // get the addresses of the procs we'll be using;
+  if (DosQueryProcAddr(hmod, ORD_RWSCALL,        0, (PFN*)&sRwsCall) ||
+      DosQueryProcAddr(hmod, ORD_RWSCALLINDIRECT,0, (PFN*)&sRwsCallIndirect) ||
+      DosQueryProcAddr(hmod, ORD_RWSFREEMEM,     0, (PFN*)&sRwsFreeMem) ||
+      DosQueryProcAddr(hmod, ORD_RWSGETRESULT,   0, (PFN*)&sRwsGetResult) ||
+      DosQueryProcAddr(hmod, ORD_RWSGETARGPTR,   0, (PFN*)&sRwsGetArgPtr) ||
+      DosQueryProcAddr(hmod, ORD_RWSCLIENTINIT,  0, (PFN*)&sRwsClientInit) ||
+      DosQueryProcAddr(hmod, ORD_RWSGETTIMEOUT,  0, (PFN*)&sRwsGetTimeout) ||
+      DosQueryProcAddr(hmod, ORD_RWSSETTIMEOUT,  0, (PFN*)&sRwsSetTimeout) ||
+      DosQueryProcAddr(hmod, ORD_RWSCLIENTTERMINATE, 0, (PFN*)&sRwsClientTerminate)) {
+    DosFreeModule(hmod);
+    ERRPRINTF("", "nsRwsServiceInit - DosQueryProcAddr failed%s")
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // init RWS and have it register the WPS class if needed
+  rc = sRwsClientInit(TRUE);
+  if (rc) {
+    ERRMSG(rc, "RwsClientInit")
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // if the user hasn't set a timeout, reset it to 2 seconds
+  // (the default is 20 seconds)
+  PRUint32  currentTO;
+  PRUint32  userTO;
+  rc = sRwsGetTimeout((PULONG)&currentTO, (PULONG)&userTO);
+  if (rc)
+    ERRMSG(rc, "RwsGetTimeout")
+  else
+    if (userTO == 0) {
+      rc = sRwsSetTimeout(2);
+      if (rc)
+        ERRMSG(rc, "RwsSetTimeout")
+    }
+
+  // create an instance of nsRwsService
+  NS_NEWXPCOM(sRwsInstance, nsRwsService);
+  if (sRwsInstance == 0)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  *aClass = sRwsInstance;
+  NS_ADDREF(*aClass);
+
+  // set the class up as a shutdown observer
+  nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
+  if (os)
+    os->AddObserver(*aClass, "quit-application", PR_FALSE);
+
+  return NS_OK;
+}
+
+//------------------------------------------------------------------------
+
+// this is a variation on NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR();
+// the only difference is that the _GetterProc returns an nsresult to
+// provide a more appropriate error code (typically NS_ERROR_NOT_AVAILABLE)
+
+NS_IMETHODIMP nsRwsServiceConstructor(nsISupports *aOuter, REFNSIID aIID,
+                                       void **aResult)
+{
+  nsresult rv;
+  nsRwsService *inst;
+  *aResult = NULL;
+
+  if (aOuter) {
+    rv = NS_ERROR_NO_AGGREGATION;
+    return rv;
+  }
+
+  rv = nsRwsServiceInit(&inst);
+  if (NS_FAILED(rv)) {
+    ERRPRINTF(rv, "==>> nsRwsServiceInit failed - rv= %x");
+    return rv;
+  }
+
+  rv = inst->QueryInterface(aIID, aResult);
+  NS_RELEASE(inst);
+
+  return rv;
+}
+
+//------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/widget/src/os2/nsRwsService.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 OS/2 Remote Workplace Server interface.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard L Walsh.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+//------------------------------------------------------------------------
+
+#ifndef nsRwsService_h__
+#define nsRwsService_h__
+
+#include "nsIRwsService.h"
+#include "rws.h"
+
+// e314efd1-f4ef-49e0-bd98-12d4e87a63a7
+#define NS_RWSSERVICE_CID \
+{ 0xe314efd1, 0xf4ef,0x49e0, { 0xbd, 0x98, 0x12, 0xd4, 0xe8, 0x7a, 0x63, 0xa7 } }
+
+#define NS_RWSSERVICE_CONTRACTID "@mozilla.org/rwsos2;1"
+
+//------------------------------------------------------------------------
+
+NS_IMETHODIMP nsRwsServiceConstructor(nsISupports *aOuter, REFNSIID aIID,
+                                      void **aResult);
+
+//------------------------------------------------------------------------
+
+class ExtCache;
+
+class nsRwsService : public nsIRwsService, public nsIObserver
+{
+public:
+  NS_DECL_NSIRWSSERVICE
+  NS_DECL_NSIOBSERVER
+  NS_DECL_ISUPPORTS
+
+  nsRwsService();
+
+private:
+  ~nsRwsService();
+
+protected:
+  static nsresult RwsConvert(PRUint32 type, PRUint32 value, PRUint32 *result);
+  static nsresult RwsConvert(PRUint32 type, PRUint32 value, nsAString& result);
+
+  ExtCache *mExtCache;
+};
+
+//------------------------------------------------------------------------
+
+#endif // nsRwsService_h__
--- a/widget/src/os2/nsWidgetFactory.cpp
+++ b/widget/src/os2/nsWidgetFactory.cpp
@@ -75,16 +75,17 @@
 
 // Drag & Drop, Clipboard
 #include "nsClipboard.h"
 #include "nsClipboardHelper.h"
 #include "nsTransferable.h"
 #include "nsHTMLFormatConverter.h"
 
 #include "nsScreenManagerOS2.h"
+#include "nsRwsService.h"
 
 // Printing
 #include "nsDeviceContextSpecOS2.h"
 #include "nsPrintOptionsOS2.h"
 #include "nsPrintSession.h"
 
 #include "nsFrameWindow.h" // OS/2 only
 
@@ -181,16 +182,20 @@ static const nsModuleComponentInfo compo
     NS_PRINTSESSION_CID,
     "@mozilla.org/gfx/printsession;1",
     nsPrintSessionConstructor },
   { "OS/2 Printer Enumerator",
     NS_PRINTER_ENUMERATOR_CID,
     //    "@mozilla.org/gfx/printer_enumerator/gtk;1",
     "@mozilla.org/gfx/printerenumerator;1",
     nsPrinterEnumeratorOS2Constructor },
+  { "Rws Service Interface",
+    NS_RWSSERVICE_CID,
+    NS_RWSSERVICE_CONTRACTID,
+    nsRwsServiceConstructor },
 };
 
 PR_STATIC_CALLBACK(void)
 nsWidgetOS2ModuleDtor(nsIModule *self)
 {
   nsWindow::ReleaseGlobals();
   nsFilePicker::ReleaseGlobals();
   nsAppShellShutdown(self);