gecko-dev/widget/os2/nsRwsService.cpp

1234 lines
34 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//------------------------------------------------------------------------
#include "nsIFile.h"
#include "mozilla/ModuleUtils.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsLiteralString.h"
#include "nsReadableUtils.h"
#include "nsIStringBundle.h"
#include "mozilla/Services.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 "prenv.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(uint32_t wpsFilePtr, const 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 bool 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];
uint32_t icon;
uint32_t mini;
uint32_t handler;
PRUnichar *title;
} ExtInfo;
#define kGrowBy 8
#define kMutexTimeout 500
class ExtCache
{
public:
ExtCache();
~ExtCache();
nsresult GetIcon(const char *aExt, bool aNeedMini, uint32_t *oIcon);
nsresult SetIcon(const char *aExt, bool aIsMini, uint32_t aIcon);
nsresult GetHandler(const char *aExt, uint32_t *oHandle, nsAString& oTitle);
nsresult SetHandler(const char *aExt, uint32_t aHandle, nsAString& aTitle);
void EmptyCache();
protected:
ExtInfo *FindExtension(const char *aExt, bool aSet = false);
uint32_t mPid;
uint32_t mMutex;
uint32_t mCount;
uint32_t 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, bool aNeedMini,
uint32_t *_retval)
{
if (!aExt || !*aExt || !_retval)
return NS_ERROR_INVALID_ARG;
nsresult rv = mExtCache->GetIcon(aExt, aNeedMini, _retval);
if (NS_SUCCEEDED(rv))
return rv;
nsAutoCString path;
rv = CreateFileForExtension(aExt, path);
if (NS_SUCCEEDED(rv)) {
rv = IconFromPath(path.get(), 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, bool aAbstract,
bool aNeedMini, uint32_t *_retval)
{
if (!aPath || !*aPath || !_retval)
return NS_ERROR_INVALID_ARG;
uint32_t rwsType;
if (aAbstract)
rwsType = (aNeedMini ? RWSC_OFTITLE_OMINI : RWSC_OFTITLE_OICON);
else
rwsType = (aNeedMini ? RWSC_OPATH_OMINI : RWSC_OPATH_OICON);
return RwsConvert(rwsType, (uint32_t)aPath, _retval);
}
//------------------------------------------------------------------------
// retrieve's an object's icon using its persistent handle
NS_IMETHODIMP
nsRwsService::IconFromHandle(uint32_t aHandle, bool aNeedMini,
uint32_t *_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(uint32_t 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, uint32_t *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;
nsAutoCString 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, uint32_t *aHandle,
nsAString& _retval)
{
if (!aPath || !*aPath || !aHandle)
return NS_ERROR_INVALID_ARG;
nsresult rv = NS_ERROR_FAILURE;
PRWSHDR pHdr = 0;
uint32_t 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")
uint32_t defView = sRwsGetResult(pHdr, 0, 0);
if (defView == (uint32_t)-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
uint32_t 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 = *((uint32_t*)pRtn->pget);
uint32_t 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;
int32_t 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, bool aAbstract)
{
if (!aPath || !*aPath)
return NS_ERROR_INVALID_ARG;
nsresult rv = NS_ERROR_FAILURE;
PRWSHDR pHdr = 0;
uint32_t type = (aAbstract ? RWSI_OFTITLE : RWSI_OPATH);
uint32_t 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, uint32_t aAppHandle)
{
if (!aFilePath || !*aFilePath || !aAppHandle)
return NS_ERROR_INVALID_ARG;
nsresult rv = NS_ERROR_FAILURE;
PRWSHDR pHdr = 0;
uint32_t 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;
uint32_t 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(uint32_t type, uint32_t value, uint32_t *result)
{
nsresult rv = NS_ERROR_FAILURE;
PRWSHDR pHdr = 0;
*result = 0;
uint32_t 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 == (uint32_t)-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(uint32_t type, uint32_t value, nsAString& result)
{
nsresult rv = NS_ERROR_FAILURE;
PRWSHDR pHdr = 0;
result.Truncate();
uint32_t 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) {
uint32_t 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(uint32_t wpsFilePtr, const char *pszClassname)
{
PRWSHDR pHdr = 0;
nsresult rv = NS_ERROR_FAILURE;
uint32_t 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;
nsAutoCString 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 =
mozilla::services::GetStringBundleService();
if (!bundleSvc)
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;
int32_t bufLength;
// convert the title to Unicode
if (NS_FAILED(MultiByteToWideChar(0, aTitle, strlen(aTitle),
buffer, bufLength)))
return NS_ERROR_FAILURE;
PRUnichar *pSrc;
PRUnichar *pDst;
bool fSkip;
// remove line breaks, leading whitespace, & extra embedded whitespace
// (primitive, but gcc 3.2.2 doesn't support wchar)
for (fSkip=true, pSrc=pDst=buffer.Elements(); *pSrc; pSrc++) {
if (*pSrc == ' ' || *pSrc == '\r' || *pSrc == '\n' || *pSrc == '\t') {
if (!fSkip)
*pDst++ = ' ';
fSkip = true;
}
else {
if (pDst != pSrc)
*pDst = *pSrc;
pDst++;
fSkip = 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;
uint32_t 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, bool aNeedMini,
uint32_t *oIcon)
{
uint32_t 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, bool aIsMini,
uint32_t aIcon)
{
uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout);
if (rc) {
ERRMSG(rc, "DosRequestMutexSem")
return NS_ERROR_FAILURE;
}
ExtInfo *info = FindExtension(aExt, 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, uint32_t *oHandle,
nsAString& oTitle)
{
uint32_t 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, uint32_t aHandle,
nsAString& aTitle)
{
uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout);
if (rc) {
ERRMSG(rc, "DosRequestMutexSem")
return NS_ERROR_FAILURE;
}
nsresult rv = NS_ERROR_FAILURE;
ExtInfo *info = FindExtension(aExt, 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, bool 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;
uint32_t 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) {
uint32_t 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;
uint32_t rc = DosRequestMutexSem(mMutex, kMutexTimeout);
if (rc) {
ERRMSG(rc, "DosRequestMutexSem")
return;
}
uint32_t saveMutex = mMutex;
mMutex = 0;
uint32_t 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;
// don't load RWS if "MOZ_NO_RWS" is found in the environment
if (PR_GetEnv("MOZ_NO_RWS"))
return NS_ERROR_NOT_AVAILABLE;
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
uint32_t 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)
uint32_t currentTO;
uint32_t 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
sRwsInstance = new 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 = mozilla::services::GetObserverService();
if (os)
os->AddObserver(*aClass, "quit-application", 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;
}
//------------------------------------------------------------------------