[JAEGER] Merge from tracemonkey.
[mozilla-central.git] / widget / src / os2 / nsRwsService.cpp
blobc6207771324a590fad1814dc7ae790ee115904c5
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is OS/2 Remote Workplace Server interface.
17 * The Initial Developer of the Original Code is
18 * Richard L Walsh.
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 //------------------------------------------------------------------------
40 #include "nsIFile.h"
41 #include "nsIGenericFactory.h"
42 #include "nsIObserver.h"
43 #include "nsIObserverService.h"
44 #include "nsDirectoryServiceDefs.h"
45 #include "nsLiteralString.h"
46 #include "nsReadableUtils.h"
47 #include "nsIStringBundle.h"
48 #include "mozilla/Services.h"
50 #define INCL_WIN
51 #define INCL_DOS
52 #include <os2.h>
54 // nsRwsService must be included _after_ os2.h
55 #include "nsRwsService.h"
56 #include "rwserr.h"
57 #include "nsOS2Uni.h"
59 #include "prenv.h"
60 #include <stdio.h>
62 //------------------------------------------------------------------------
63 // debug macros
64 //------------------------------------------------------------------------
66 #ifdef DEBUG
67 #define RWS_DEBUG
68 #endif
70 // use this to force debug msgs in non-debug builds
71 //#ifndef RWS_DEBUG
72 // #define RWS_DEBUG
73 //#endif
75 #ifdef RWS_DEBUG
76 #define ERRMSG(x,y) { printf(y " failed - rc= %d\n", (int)x); }
78 #define ERRBREAK(x,y) if (x) { ERRMSG(x,y); break; }
80 #define ERRPRINTF(x,y) { printf(y "\n", x); }
81 #else
82 #define ERRMSG(x,y) ;
84 #define ERRBREAK(x,y) if (x) break;
86 #define ERRPRINTF(x,y) ;
87 #endif
89 //------------------------------------------------------------------------
90 // function prototypes
91 //------------------------------------------------------------------------
93 static nsresult IsDescendedFrom(PRUint32 wpsFilePtr, const char *pszClassname);
94 static nsresult CreateFileForExtension(const char *aFileExt, nsACString& aPath);
95 static nsresult DeleteFileForExtension(const char *aPath);
96 static void AssignNLSString(const PRUnichar *aKey, nsAString& _retval);
97 static nsresult AssignTitleString(const char *aTitle, nsAString& result);
99 //------------------------------------------------------------------------
100 // module-level variables - not declared as members of nsRws
101 //------------------------------------------------------------------------
103 static nsRwsService *sRwsInstance = 0; // our singleton instance
104 static PRBool sInit = FALSE; // have we tried to load RWS
106 // RWS administrative functions
107 static ULONG (* _System sRwsClientInit)(BOOL);
108 static ULONG (* _System sRwsGetTimeout)(PULONG, PULONG);
109 static ULONG (* _System sRwsSetTimeout)(ULONG);
110 static ULONG (* _System sRwsClientTerminate)(void);
112 // RWS task-oriented functions
113 static ULONG (* _System sRwsCall)(PRWSHDR*, ULONG, ULONG, ULONG, ULONG, ULONG, ...);
114 static ULONG (* _System sRwsCallIndirect)(PRWSBLD);
115 static ULONG (* _System sRwsFreeMem)(PRWSHDR);
116 static ULONG (* _System sRwsGetResult)(PRWSHDR, ULONG, PULONG);
117 static ULONG (* _System sRwsGetArgPtr)(PRWSHDR, ULONG, PRWSDSC*);
119 //------------------------------------------------------------------------
120 // ExtCache - caches the default icons & handlers for file extensions
121 //------------------------------------------------------------------------
123 typedef struct _ExtInfo
125 char ext[8];
126 PRUint32 icon;
127 PRUint32 mini;
128 PRUint32 handler;
129 PRUnichar *title;
130 } ExtInfo;
132 #define kGrowBy 8
133 #define kMutexTimeout 500
135 class ExtCache
137 public:
138 ExtCache();
139 ~ExtCache();
141 nsresult GetIcon(const char *aExt, PRBool aNeedMini, PRUint32 *oIcon);
142 nsresult SetIcon(const char *aExt, PRBool aIsMini, PRUint32 aIcon);
143 nsresult GetHandler(const char *aExt, PRUint32 *oHandle, nsAString& oTitle);
144 nsresult SetHandler(const char *aExt, PRUint32 aHandle, nsAString& aTitle);
145 void EmptyCache();
147 protected:
148 ExtInfo *FindExtension(const char *aExt, PRBool aSet = PR_FALSE);
150 PRUint32 mPid;
151 PRUint32 mMutex;
152 PRUint32 mCount;
153 PRUint32 mSize;
154 ExtInfo *mExtInfo;
157 //------------------------------------------------------------------------
158 // nsIRwsService implementation
159 //------------------------------------------------------------------------
161 NS_IMPL_ISUPPORTS2(nsRwsService, nsIRwsService, nsIObserver)
163 nsRwsService::nsRwsService()
165 mExtCache = new ExtCache();
166 if (!mExtCache)
167 ERRPRINTF("", "nsRwsService - unable to allocate mExtArray%s");
170 nsRwsService::~nsRwsService()
174 //------------------------------------------------------------------------
176 // provides the default icon associated with a given extension; if the
177 // icon isn't in the cache, it creates a temp file with that extension,
178 // retrieves its icon, deletes the temp file, then caches the icon
180 NS_IMETHODIMP
181 nsRwsService::IconFromExtension(const char *aExt, PRBool aNeedMini,
182 PRUint32 *_retval)
184 if (!aExt || !*aExt || !_retval)
185 return NS_ERROR_INVALID_ARG;
187 nsresult rv = mExtCache->GetIcon(aExt, aNeedMini, _retval);
188 if (NS_SUCCEEDED(rv))
189 return rv;
191 nsCAutoString path;
192 rv = CreateFileForExtension(aExt, path);
193 if (NS_SUCCEEDED(rv)) {
194 rv = IconFromPath(path.get(), PR_FALSE, aNeedMini, _retval);
195 DeleteFileForExtension(path.get());
196 if (NS_SUCCEEDED(rv))
197 mExtCache->SetIcon(aExt, aNeedMini, *_retval);
200 return rv;
203 //------------------------------------------------------------------------
205 // retrieves an object's icon using its fully-qualified path; aAbstract
206 // indicates whether this is a Filesystem object or an Abstract or
207 // Transient object; locating non-file objects is fairly expensive
209 NS_IMETHODIMP
210 nsRwsService::IconFromPath(const char *aPath, PRBool aAbstract,
211 PRBool aNeedMini, PRUint32 *_retval)
213 if (!aPath || !*aPath || !_retval)
214 return NS_ERROR_INVALID_ARG;
216 PRUint32 rwsType;
218 if (aAbstract)
219 rwsType = (aNeedMini ? RWSC_OFTITLE_OMINI : RWSC_OFTITLE_OICON);
220 else
221 rwsType = (aNeedMini ? RWSC_OPATH_OMINI : RWSC_OPATH_OICON);
223 return RwsConvert(rwsType, (PRUint32)aPath, _retval);
226 //------------------------------------------------------------------------
228 // retrieve's an object's icon using its persistent handle
230 NS_IMETHODIMP
231 nsRwsService::IconFromHandle(PRUint32 aHandle, PRBool aNeedMini,
232 PRUint32 *_retval)
234 if (!aHandle || !_retval)
235 return NS_ERROR_INVALID_ARG;
237 return RwsConvert( (aNeedMini ? RWSC_OHNDL_OMINI : RWSC_OHNDL_OICON),
238 aHandle, _retval);
241 //------------------------------------------------------------------------
243 // retrieve's an object's title using its persistent handle
245 NS_IMETHODIMP
246 nsRwsService::TitleFromHandle(PRUint32 aHandle, nsAString& _retval)
248 if (!aHandle)
249 return NS_ERROR_INVALID_ARG;
251 return RwsConvert(RWSC_OHNDL_OTITLE, aHandle, _retval);
254 //------------------------------------------------------------------------
256 // provides the default handler associated with a given extension; if the
257 // info isn't in the cache, it creates a temp file with that extension,
258 // retrieves the handler's title & possibly its object handle, deletes the
259 // temp file, then caches the info.
261 NS_IMETHODIMP
262 nsRwsService::HandlerFromExtension(const char *aExt, PRUint32 *aHandle,
263 nsAString& _retval)
265 if (!aExt || !*aExt || !aHandle)
266 return NS_ERROR_INVALID_ARG;
268 nsresult rv = mExtCache->GetHandler(aExt, aHandle, _retval);
269 if (NS_SUCCEEDED(rv))
270 return rv;
272 nsCAutoString path;
273 rv = CreateFileForExtension(aExt, path);
274 if (NS_SUCCEEDED(rv)) {
275 rv = HandlerFromPath(path.get(), aHandle, _retval);
276 DeleteFileForExtension(path.get());
277 if (NS_SUCCEEDED(rv))
278 mExtCache->SetHandler(aExt, *aHandle, _retval);
281 return rv;
284 //------------------------------------------------------------------------
286 // identifies the default WPS handler for a given file; if the handler
287 // is an associated program, returns its title & object handle; if the
288 // handler is an integrated class viewer or player, it constructs a title
289 // and sets the object handle to zero
291 NS_IMETHODIMP
292 nsRwsService::HandlerFromPath(const char *aPath, PRUint32 *aHandle,
293 nsAString& _retval)
295 if (!aPath || !*aPath || !aHandle)
296 return NS_ERROR_INVALID_ARG;
298 nsresult rv = NS_ERROR_FAILURE;
299 PRWSHDR pHdr = 0;
300 PRUint32 rc;
302 _retval.Truncate();
303 *aHandle = 0;
305 // this dummy do{...}while(0) loop lets us bail out early
306 // while ensuring pHdr gets freed
307 do {
308 // get the default view for this file
309 rc = sRwsCall(&pHdr,
310 RWSP_MNAM, (ULONG)"wpQueryDefaultView",
311 RWSR_ASIS, 0, 1,
312 RWSI_OPATH, 0, (ULONG)aPath);
313 ERRBREAK(rc, "wpQueryDefaultView")
315 PRUint32 defView = sRwsGetResult(pHdr, 0, 0);
316 if (defView == (PRUint32)-1)
317 break;
319 // if the default view is OPEN_SETTINGS ('2'),
320 // change it to OPEN_RUNNING ('4')
321 if (defView == 2)
322 defView = 4;
324 // to improve efficiency, retrieve & reuse the file's wpObject
325 // ptr rather than repeatedly converting the name to an object
326 PRUint32 wpsFilePtr = sRwsGetResult(pHdr, 1, 0);
328 // free pHdr before the next call
329 sRwsFreeMem(pHdr);
330 pHdr = 0;
332 // for default view values less than OPEN_USER, see if there
333 // is a program or program object associated with this file
334 if (defView < 0x6500) {
335 rc = sRwsCall(&pHdr,
336 RWSP_MNAM, (ULONG)"wpQueryAssociatedProgram",
337 RWSR_OHNDL, 0, 6,
338 RWSI_ASIS, 0, wpsFilePtr,
339 RWSI_ASIS, 0, defView,
340 RWSI_PULONG, 0, 0,
341 RWSI_PBUF, 32, 0,
342 RWSI_ASIS, 0, 32,
343 RWSI_ASIS, 0, (ULONG)-1);
345 // if there's no associated program, create dummy values
346 // (if chosen, the WPS will open the file's Properties notebook)
347 if (rc) {
348 if (rc == RWSSRV_FUNCTIONFAILED) {
349 *aHandle = 0;
350 AssignNLSString(NS_LITERAL_STRING("wpsDefaultOS2").get(), _retval);
351 rv = NS_OK;
353 else
354 ERRMSG(rc, "wpQueryAssociatedProgram")
355 break;
358 // get a ptr to the return value's data structure; then get
359 // both the object handle we requested & the raw WPS object ptr
360 PRWSDSC pRtn;
361 rc = sRwsGetArgPtr(pHdr, 0, &pRtn);
362 ERRBREAK(rc, "GetArgPtr")
363 *aHandle = *((PRUint32*)pRtn->pget);
364 PRUint32 wpsPgmPtr = pRtn->value;
366 // free pHdr before the next call
367 sRwsFreeMem(pHdr);
368 pHdr = 0;
370 // convert the object to its title (using Rws' conversion
371 // feature is more efficient than calling wpQueryTitle)
372 rc = sRwsCall(&pHdr,
373 RWSP_CONV, 0,
374 RWSR_ASIS, 0, 1,
375 RWSC_OBJ_OTITLE, 0, wpsPgmPtr);
376 ERRBREAK(rc, "convert pgm object to title")
378 // retrieve the title, massage it & assign to _retval, then exit
379 // N.B. no need to free pHdr here, it will be freed below
380 char *pszTitle = (char*)sRwsGetResult(pHdr, 1, 0);
381 if (pszTitle != (char*)-1)
382 rv = AssignTitleString(pszTitle, _retval);
384 break;
387 // the default view is provided by the file's WPS class;
388 // in this case, *aHandle is 0
390 // see if it's a known view that can be given a meaningful name
391 switch (defView) {
392 case 0xbc2b: {
393 rv = IsDescendedFrom(wpsFilePtr, "MMImage");
394 if (NS_SUCCEEDED(rv))
395 AssignNLSString(NS_LITERAL_STRING("mmImageViewerOS2").get(), _retval);
396 break;
399 case 0xbc0d: // Audio editor
400 case 0xbc21: // Video editor
401 case 0xbc17: // Midi editor
402 case 0xbbef: // Play
403 case 0xbbe5: { // Player
404 rv = IsDescendedFrom(wpsFilePtr, "MMAudio");
405 if (NS_SUCCEEDED(rv)) {
406 AssignNLSString(NS_LITERAL_STRING("mmAudioPlayerOS2").get(), _retval);
407 break;
410 rv = IsDescendedFrom(wpsFilePtr, "MMVideo");
411 if (NS_SUCCEEDED(rv)) {
412 AssignNLSString(NS_LITERAL_STRING("mmVideoPlayerOS2").get(), _retval);
413 break;
416 rv = IsDescendedFrom(wpsFilePtr, "MMMIDI");
417 if (NS_SUCCEEDED(rv))
418 AssignNLSString(NS_LITERAL_STRING("mmMidiPlayerOS2").get(), _retval);
420 break;
423 case 0x7701: {
424 rv = IsDescendedFrom(wpsFilePtr, "TSArcMgr");
425 if (NS_SUCCEEDED(rv))
426 AssignNLSString(NS_LITERAL_STRING("odZipFolderOS2").get(), _retval);
427 break;
430 // this has to go last because TSEnhDataFile replaces
431 // WPDataFile; if present, all data files are descended from it
432 case 0xb742:
433 case 0xa742: {
434 rv = IsDescendedFrom(wpsFilePtr, "TSEnhDataFile");
435 if (NS_SUCCEEDED(rv))
436 AssignNLSString(NS_LITERAL_STRING("odTextViewOS2").get(), _retval);
437 break;
439 } // end switch
441 if (NS_SUCCEEDED(rv))
442 break;
444 // the name of this view is unknown, so create a generic name
445 // (i.e. "Viewer for Class WPSomething")
447 // use the file's object ptr to get the name of its class
448 rc = sRwsCall(&pHdr,
449 RWSP_CONV, 0,
450 RWSR_ASIS, 0, 1,
451 RWSC_OBJ_CNAME, 0, wpsFilePtr);
452 ERRBREAK(rc, "convert object to classname")
454 char *pszTitle = (char*)sRwsGetResult(pHdr, 1, 0);
455 if (pszTitle == (char*)-1)
456 break;
458 nsAutoChar16Buffer buffer;
459 PRInt32 bufLength;
460 rv = MultiByteToWideChar(0, pszTitle, strlen(pszTitle),
461 buffer, bufLength);
462 if (NS_FAILED(rv))
463 break;
465 nsAutoString classViewer;
466 AssignNLSString(NS_LITERAL_STRING("classViewerOS2").get(), classViewer);
467 int pos = -1;
468 if ((pos = classViewer.Find("%S")) > -1)
469 classViewer.Replace(pos, 2, buffer.Elements());
470 _retval.Assign(classViewer);
471 rv = NS_OK;
472 } while (0);
474 // free the pHdr allocated by the final call
475 sRwsFreeMem(pHdr);
476 return rv;
479 //------------------------------------------------------------------------
481 // retrieves an object's menu using its fully-qualified path; aAbstract
482 // indicates whether this is a Filesystem object or an Abstract or
483 // Transient object; locating non-file objects is fairly expensive
485 NS_IMETHODIMP
486 nsRwsService::MenuFromPath(const char *aPath, PRBool aAbstract)
488 if (!aPath || !*aPath)
489 return NS_ERROR_INVALID_ARG;
491 nsresult rv = NS_ERROR_FAILURE;
492 PRWSHDR pHdr = 0;
493 PRUint32 type = (aAbstract ? RWSI_OFTITLE : RWSI_OPATH);
494 PRUint32 rc;
495 POINTL ptl;
496 HWND hTgt = 0;
498 // try to identify the window where the click occurred
499 if (WinQueryMsgPos(0, &ptl)) {
500 hTgt = WinQueryFocus(HWND_DESKTOP);
501 if (hTgt)
502 WinMapWindowPoints(HWND_DESKTOP, hTgt, &ptl, 1);
505 // if we have the window & coordinates, use them; otherwise,
506 // let RWS position the menu at the current pointer position
507 // (specifying the window ensures the focus will return to it)
508 if (hTgt)
509 rc = sRwsCall(&pHdr,
510 RWSP_CMD, RWSCMD_POPUPMENU,
511 RWSR_ASIS, 0, 3,
512 type, 0, (ULONG)aPath,
513 RWSI_ASIS, 0, hTgt,
514 RWSI_PBUF, sizeof(POINTL), (ULONG)&ptl);
515 else
516 rc = sRwsCall(&pHdr,
517 RWSP_CMD, RWSCMD_POPUPMENU,
518 RWSR_ASIS, 0, 3,
519 type, 0, (ULONG)aPath,
520 RWSI_ASIS, 0, 0,
521 RWSI_ASIS, 0, 0);
523 if (rc)
524 ERRMSG(rc, "RWSCMD_POPUPMENU")
525 else
526 rv = NS_OK;
528 // free pHdr
529 sRwsFreeMem(pHdr);
530 return rv;
533 //------------------------------------------------------------------------
535 // causes the WPS to open a file using the program specified by AppPath;
536 // this is identical to dropping the file on the program's WPS icon
538 NS_IMETHODIMP
539 nsRwsService::OpenWithAppHandle(const char *aFilePath, PRUint32 aAppHandle)
541 if (!aFilePath || !*aFilePath || !aAppHandle)
542 return NS_ERROR_INVALID_ARG;
544 nsresult rv = NS_ERROR_FAILURE;
545 PRWSHDR pHdr = 0;
546 PRUint32 rc;
548 rc = sRwsCall(&pHdr,
549 RWSP_CMD, RWSCMD_OPENUSING,
550 RWSR_ASIS, 0, 2,
551 RWSI_OPATH, 0, aFilePath,
552 RWSI_OHNDL, 0, aAppHandle);
553 if (rc)
554 ERRMSG(rc, "RWSCMD_OPENUSING")
555 else
556 rv = NS_OK;
558 sRwsFreeMem(pHdr);
559 return rv;
562 //------------------------------------------------------------------------
564 // causes the WPS to open a file using the program specified by AppPath;
565 // this is identical to dropping the file on the program's WPS icon
567 NS_IMETHODIMP
568 nsRwsService::OpenWithAppPath(const char *aFilePath, const char *aAppPath)
570 if (!aFilePath || !*aFilePath || !aAppPath || !*aAppPath)
571 return NS_ERROR_INVALID_ARG;
573 nsresult rv = NS_ERROR_FAILURE;
574 PRWSHDR pHdr = 0;
575 PRUint32 rc;
577 rc = sRwsCall(&pHdr,
578 RWSP_CMD, RWSCMD_OPENUSING,
579 RWSR_ASIS, 0, 2,
580 RWSI_OPATH, 0, aFilePath,
581 RWSI_OPATH, 0, aAppPath);
582 if (rc)
583 ERRMSG(rc, "RWSCMD_OPENUSING")
584 else
585 rv = NS_OK;
587 sRwsFreeMem(pHdr);
588 return rv;
591 //------------------------------------------------------------------------
592 // nsRwsService additional methods
593 //------------------------------------------------------------------------
595 // uses RwsConvert to retrieve a result that can be handled as a ULONG;
596 // type identifies the conversion, value can be anything (ULONG, char*, etc)
598 //static
599 nsresult
600 nsRwsService::RwsConvert(PRUint32 type, PRUint32 value, PRUint32 *result)
602 nsresult rv = NS_ERROR_FAILURE;
603 PRWSHDR pHdr = 0;
605 *result = 0;
606 PRUint32 rc = sRwsCall(&pHdr,
607 RWSP_CONV, 0,
608 RWSR_ASIS, 0, 1,
609 type, 0, value);
611 if (rc)
612 ERRMSG(rc, "RwsConvert to ULONG")
613 else {
614 *result = sRwsGetResult(pHdr, 1, 0);
615 if (*result == (PRUint32)-1)
616 *result = 0;
617 else
618 rv = NS_OK;
621 sRwsFreeMem(pHdr);
622 return rv;
625 //------------------------------------------------------------------------
627 // uses RwsConvert to retrieve a result that can be handled as a
628 // Unicode string; type identifies the conversion, value can be
629 // anything (ULONG, char*, etc)
631 //static
632 nsresult
633 nsRwsService::RwsConvert(PRUint32 type, PRUint32 value, nsAString& result)
635 nsresult rv = NS_ERROR_FAILURE;
636 PRWSHDR pHdr = 0;
638 result.Truncate();
639 PRUint32 rc = sRwsCall(&pHdr,
640 RWSP_CONV, 0,
641 RWSR_ASIS, 0, 1,
642 type, 0, value);
644 if (rc)
645 ERRMSG(rc, "RwsConvert to string")
646 else {
647 char *string = (char*)sRwsGetResult(pHdr, 1, 0);
648 if (string != (char*)-1)
649 rv = AssignTitleString(string, result);
652 sRwsFreeMem(pHdr);
653 return rv;
656 //------------------------------------------------------------------------
657 // nsIObserver implementation
658 //------------------------------------------------------------------------
660 // when the app shuts down, advise RwsClient; if this is the
661 // last app using RwsServer, the WPS will unload the class's dll;
662 // also, empty the extension cache to unlock & delete its icons
664 NS_IMETHODIMP
665 nsRwsService::Observe(nsISupports *aSubject, const char *aTopic,
666 const PRUnichar *aSomeData)
668 if (strcmp(aTopic, "quit-application") == 0) {
669 PRUint32 rc = sRwsClientTerminate();
670 if (rc)
671 ERRMSG(rc, "RwsClientTerminate");
673 if (mExtCache)
674 mExtCache->EmptyCache();
677 return NS_OK;
680 //------------------------------------------------------------------------
681 // nsRwsService static helper functions
682 //------------------------------------------------------------------------
684 // this wrapper for somIsA() makes HandlerFromPath() easier to read
686 static nsresult IsDescendedFrom(PRUint32 wpsFilePtr, const char *pszClassname)
688 PRWSHDR pHdr = 0;
689 nsresult rv = NS_ERROR_FAILURE;
691 PRUint32 rc = sRwsCall(&pHdr,
692 RWSP_MNAMI, (ULONG)"somIsA",
693 RWSR_ASIS, 0, 2,
694 RWSI_ASIS, 0, wpsFilePtr,
695 RWSI_CNAME, 0, pszClassname);
697 if (rc)
698 ERRMSG(rc, "somIsA")
699 else
700 if (sRwsGetResult(pHdr, 0, 0) == TRUE)
701 rv = NS_OK;
703 sRwsFreeMem(pHdr);
704 return rv;
707 //------------------------------------------------------------------------
709 // create a temp file with the specified extension, then return its path
711 static nsresult CreateFileForExtension(const char *aFileExt,
712 nsACString& aPath)
714 nsresult rv = NS_ERROR_FAILURE;
715 aPath.Truncate();
717 nsCOMPtr<nsIFile> tempFile;
718 rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempFile));
719 if (NS_FAILED(rv))
720 return rv;
722 nsCAutoString pathStr(NS_LITERAL_CSTRING("nsrws."));
723 if (*aFileExt == '.')
724 aFileExt++;
725 pathStr.Append(aFileExt);
727 rv = tempFile->AppendNative(pathStr);
728 if (NS_FAILED(rv))
729 return rv;
731 tempFile->GetNativePath(pathStr);
733 FILE *fp = fopen(pathStr.get(), "wb+");
734 if (fp) {
735 fclose(fp);
736 aPath.Assign(pathStr);
737 rv = NS_OK;
740 return rv;
743 //------------------------------------------------------------------------
745 // delete a temp file created earlier
747 static nsresult DeleteFileForExtension(const char *aPath)
749 if (!aPath || !*aPath)
750 return NS_ERROR_INVALID_ARG;
752 remove(aPath);
753 return NS_OK;
756 //------------------------------------------------------------------------
758 // returns a localized string from unknownContentType.properties;
759 // if there's a failure, returns "WPS Default"
761 static void AssignNLSString(const PRUnichar *aKey, nsAString& result)
763 nsresult rv;
764 nsXPIDLString title;
766 do {
767 nsCOMPtr<nsIStringBundleService> bundleSvc =
768 mozilla::services::GetStringBundleService();
769 if (!bundleSvc)
770 break;
772 nsCOMPtr<nsIStringBundle> bundle;
773 rv = bundleSvc->CreateBundle(
774 "chrome://mozapps/locale/downloads/unknownContentType.properties",
775 getter_AddRefs(bundle));
776 if (NS_FAILED(rv))
777 break;
779 // if we can't fetch the requested string, try to get "WPS Default"
780 rv = bundle->GetStringFromName(aKey, getter_Copies(title));
781 if (NS_FAILED(rv))
782 rv = bundle->GetStringFromName(NS_LITERAL_STRING("wpsDefaultOS2").get(),
783 getter_Copies(title));
784 } while (0);
786 if (NS_SUCCEEDED(rv))
787 result.Assign(title);
788 else
789 result.Assign(NS_LITERAL_STRING("WPS Default"));
792 //------------------------------------------------------------------------
794 // converts a native string (presumably a WPS object's title) to
795 // Unicode, removes line breaks, and compresses whitespace
797 static nsresult AssignTitleString(const char *aTitle, nsAString& result)
799 nsAutoChar16Buffer buffer;
800 PRInt32 bufLength;
802 // convert the title to Unicode
803 if (NS_FAILED(MultiByteToWideChar(0, aTitle, strlen(aTitle),
804 buffer, bufLength)))
805 return NS_ERROR_FAILURE;
807 PRUnichar *pSrc;
808 PRUnichar *pDst;
809 PRBool fSkip;
811 // remove line breaks, leading whitespace, & extra embedded whitespace
812 // (primitive, but gcc 3.2.2 doesn't support wchar)
813 for (fSkip=PR_TRUE, pSrc=pDst=buffer.Elements(); *pSrc; pSrc++) {
814 if (*pSrc == ' ' || *pSrc == '\r' || *pSrc == '\n' || *pSrc == '\t') {
815 if (!fSkip)
816 *pDst++ = ' ';
817 fSkip = PR_TRUE;
819 else {
820 if (pDst != pSrc)
821 *pDst = *pSrc;
822 pDst++;
823 fSkip = PR_FALSE;
827 // remove the single trailing space, if needed
828 if (fSkip && pDst > buffer.Elements())
829 pDst--;
831 *pDst = 0;
832 result.Assign(buffer.Elements());
833 return NS_OK;
836 //------------------------------------------------------------------------
837 // ExtCache implementation
838 //------------------------------------------------------------------------
840 ExtCache::ExtCache() : mCount(0), mSize(0), mExtInfo(0)
842 PTIB ptib;
843 PPIB ppib;
845 // the PID is needed to lock/unlock icons
846 DosGetInfoBlocks(&ptib, &ppib);
847 mPid = ppib->pib_ulpid;
849 PRUint32 rc = DosCreateMutexSem(0, (PHMTX)&mMutex, 0, 0);
850 if (rc)
851 ERRMSG(rc, "DosCreateMutexSem")
854 ExtCache::~ExtCache() {}
856 //------------------------------------------------------------------------
858 // retrieve the WPS's default icon for files with this extension
860 nsresult ExtCache::GetIcon(const char *aExt, PRBool aNeedMini,
861 PRUint32 *oIcon)
863 PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
864 if (rc) {
865 ERRMSG(rc, "DosRequestMutexSem")
866 return NS_ERROR_FAILURE;
869 ExtInfo *info = FindExtension(aExt);
871 if (info) {
872 if (aNeedMini)
873 *oIcon = info->mini;
874 else
875 *oIcon = info->icon;
877 else
878 *oIcon = 0;
880 rc = DosReleaseMutexSem(mMutex);
881 if (rc)
882 ERRMSG(rc, "DosReleaseMutexSem")
884 return (*oIcon ? NS_OK : NS_ERROR_FAILURE);
887 //------------------------------------------------------------------------
889 // save the WPS's default icon for files with this extension
891 nsresult ExtCache::SetIcon(const char *aExt, PRBool aIsMini,
892 PRUint32 aIcon)
894 PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
895 if (rc) {
896 ERRMSG(rc, "DosRequestMutexSem")
897 return NS_ERROR_FAILURE;
900 ExtInfo *info = FindExtension(aExt, PR_TRUE);
901 if (!info)
902 return NS_ERROR_FAILURE;
904 // the icon has to be made non-deletable or else
905 // it will be destroyed if the WPS terminates
906 if (!WinSetPointerOwner(aIcon, mPid, FALSE)) {
907 ERRPRINTF(info->ext, "WinSetPointerOwner failed for %s icon")
908 return NS_ERROR_FAILURE;
911 if (aIsMini)
912 info->mini = aIcon;
913 else
914 info->icon = aIcon;
916 ERRPRINTF(info->ext, "ExtCache - added icon for %s")
918 rc = DosReleaseMutexSem(mMutex);
919 if (rc)
920 ERRMSG(rc, "DosReleaseMutexSem")
922 return NS_OK;
925 //------------------------------------------------------------------------
927 // retrieve the WPS default handler's title & object handle (if any)
929 nsresult ExtCache::GetHandler(const char *aExt, PRUint32 *oHandle,
930 nsAString& oTitle)
932 PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
933 if (rc) {
934 ERRMSG(rc, "DosRequestMutexSem")
935 return NS_ERROR_FAILURE;
938 nsresult rv = NS_ERROR_FAILURE;
939 ExtInfo *info = FindExtension(aExt);
941 // if there's no title, the handle isn't useful
942 if (info && info->title) {
943 oTitle.Assign(info->title);
944 *oHandle = info->handler;
945 rv = NS_OK;
948 rc = DosReleaseMutexSem(mMutex);
949 if (rc)
950 ERRMSG(rc, "DosReleaseMutexSem")
952 return rv;
955 //------------------------------------------------------------------------
957 // save the WPS default handler's title & object handle (if any)
959 nsresult ExtCache::SetHandler(const char *aExt, PRUint32 aHandle,
960 nsAString& aTitle)
962 PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
963 if (rc) {
964 ERRMSG(rc, "DosRequestMutexSem")
965 return NS_ERROR_FAILURE;
968 nsresult rv = NS_ERROR_FAILURE;
969 ExtInfo *info = FindExtension(aExt, PR_TRUE);
971 // if the title can't be saved, don't save the handle
972 if (info) {
973 info->title = ToNewUnicode(aTitle);
974 if (info->title) {
975 info->handler = aHandle;
976 rv = NS_OK;
977 ERRPRINTF(info->ext, "ExtCache - added handler for %s")
981 rc = DosReleaseMutexSem(mMutex);
982 if (rc)
983 ERRMSG(rc, "DosReleaseMutexSem")
985 return rv;
988 //------------------------------------------------------------------------
990 // find the entry for the requested extension; if not found,
991 // create a new entry, expanding the array as needed
993 ExtInfo *ExtCache::FindExtension(const char *aExt, PRBool aSet)
995 // eliminate any leading dot & null extensions
996 if (*aExt == '.')
997 aExt++;
998 if (*aExt == 0)
999 return 0;
1001 // too long to cache
1002 if (strlen(aExt) >= 8)
1003 return 0;
1005 // uppercase extension, then confirm it still fits
1006 char extUpper[16];
1007 strcpy(extUpper, aExt);
1008 // XXX WinUpper() can crash with high-memory
1009 // could we just store as-is and instead use stricmp() for
1010 // compare operations?
1011 if (WinUpper(0, 0, 0, extUpper) >= 8)
1012 return 0;
1014 // a minor optimization: if we're setting a value, it probably
1015 // belongs to the entry added most recently (i.e. the last one)
1016 if (aSet && mCount && !strcmp(extUpper, (&mExtInfo[mCount-1])->ext))
1017 return &mExtInfo[mCount-1];
1019 ExtInfo *info;
1020 PRUint32 ctr;
1022 // look for the extension in the array, return if found
1023 for (ctr = 0, info = mExtInfo; ctr < mCount; ctr++, info++)
1024 if (!strcmp(extUpper, info->ext))
1025 return info;
1027 // if a new entry won't fit into the current array, realloc
1028 if (mCount >= mSize) {
1029 PRUint32 newSize = mSize + kGrowBy;
1030 info = (ExtInfo*) NS_Realloc(mExtInfo, newSize * sizeof(ExtInfo));
1031 if (!info)
1032 return 0;
1034 memset(&info[mSize], 0, kGrowBy * sizeof(ExtInfo));
1035 mExtInfo = info;
1036 mSize = newSize;
1039 // point at the next entry & store the extension
1040 info = &mExtInfo[mCount++];
1041 strcpy(info->ext, extUpper);
1043 return info;
1046 //------------------------------------------------------------------------
1048 // clear out the cache - since this is only called at shutdown,
1049 // the primary concern is that the icons get unlocked & deleted
1051 void ExtCache::EmptyCache()
1053 if (!mExtInfo)
1054 return;
1056 PRUint32 rc = DosRequestMutexSem(mMutex, kMutexTimeout);
1057 if (rc) {
1058 ERRMSG(rc, "DosRequestMutexSem")
1059 return;
1062 PRUint32 saveMutex = mMutex;
1063 mMutex = 0;
1065 PRUint32 ctr;
1066 ExtInfo *info;
1068 for (ctr = 0, info = mExtInfo; ctr < mCount; ctr++, info++) {
1070 ERRPRINTF(info->ext, "ExtCache - deleting entry for %s")
1072 if (info->icon) {
1073 if (WinSetPointerOwner(info->icon, mPid, TRUE) == FALSE ||
1074 WinDestroyPointer(info->icon) == FALSE)
1075 ERRPRINTF(info->ext, "unable to destroy icon for %s")
1078 if (info->mini) {
1079 if (WinSetPointerOwner(info->mini, mPid, TRUE) == FALSE ||
1080 WinDestroyPointer(info->mini) == FALSE)
1081 ERRPRINTF(info->ext, "unable to destroy mini for %s")
1084 if (info->title)
1085 NS_Free(info->title);
1088 mCount = 0;
1089 mSize = 0;
1090 NS_Free(mExtInfo);
1091 mExtInfo = 0;
1093 rc = DosReleaseMutexSem(saveMutex);
1094 if (rc)
1095 ERRMSG(rc, "DosReleaseMutexSem")
1096 rc = DosCloseMutexSem(saveMutex);
1097 if (rc)
1098 ERRMSG(rc, "DosCloseMutexSem")
1101 //------------------------------------------------------------------------
1102 // Module & Factory stuff
1103 //------------------------------------------------------------------------
1105 // this is the "getter proc" for nsRwsServiceConstructor(); it makes a
1106 // single attempt to load the RWS libraries and, if successful, creates
1107 // our singleton object; thereafter, it returns the existing object or
1108 // NS_ERROR_NOT_AVAILABLE
1110 static nsresult nsRwsServiceInit(nsRwsService **aClass)
1112 // init already done - return what we've got or an error
1113 if (sInit) {
1114 *aClass = sRwsInstance;
1115 if (*aClass == 0)
1116 return NS_ERROR_NOT_AVAILABLE;
1118 NS_ADDREF(*aClass);
1119 return NS_OK;
1122 sInit = TRUE;
1123 *aClass = 0;
1125 // don't load RWS if "MOZ_NO_RWS" is found in the environment
1126 if (PR_GetEnv("MOZ_NO_RWS"))
1127 return NS_ERROR_NOT_AVAILABLE;
1129 char errBuf[16];
1130 HMODULE hmod;
1132 // try to load RwsCliXX.dll; first, see if the RWS WPS class is
1133 // registered by f/q name - if so, look for RwsCli in the same
1134 // directory; the goal is to consistently use the same pair of
1135 // dlls if the user has multiple copies
1137 PRUint32 rc = 1;
1139 // get the list of registered WPS classes
1140 ULONG cbClass;
1141 if (!WinEnumObjectClasses(NULL, &cbClass))
1142 return NS_ERROR_NOT_AVAILABLE;
1144 char *pBuf = (char*)NS_Alloc(cbClass + CCHMAXPATH);
1145 if (!pBuf)
1146 return NS_ERROR_OUT_OF_MEMORY;
1148 POBJCLASS pClass = (POBJCLASS)&pBuf[CCHMAXPATH];
1149 if (!WinEnumObjectClasses(pClass, &cbClass)) {
1150 NS_Free(pBuf);
1151 return NS_ERROR_NOT_AVAILABLE;
1154 // look for RWSxx
1155 while (pClass) {
1156 if (!strcmp(pClass->pszClassName, RWSCLASSNAME))
1157 break;
1158 pClass = pClass->pNext;
1161 // if the class was found & it was registered with a f/q name,
1162 // try to load RwsCliXX from the same directory
1163 if (pClass && pClass->pszModName[1] == ':') {
1164 strcpy(pBuf, pClass->pszModName);
1165 char *ptr = strrchr(pBuf, '\\');
1166 if (ptr) {
1167 strcpy(ptr+1, RWSCLIDLL);
1168 rc = DosLoadModule(errBuf, sizeof(errBuf), pBuf, &hmod);
1171 NS_Free(pBuf);
1173 // if RwsCli couldn't be found, look for it on the LIBPATH
1174 if (rc)
1175 rc = DosLoadModule(errBuf, sizeof(errBuf), RWSCLIMOD, &hmod);
1177 // the dll couldn't be found, so exit
1178 if (rc) {
1179 ERRPRINTF(RWSCLIDLL, "nsRwsServiceInit - unable to locate %s");
1180 return NS_ERROR_NOT_AVAILABLE;
1183 // get the addresses of the procs we'll be using;
1184 if (DosQueryProcAddr(hmod, ORD_RWSCALL, 0, (PFN*)&sRwsCall) ||
1185 DosQueryProcAddr(hmod, ORD_RWSCALLINDIRECT,0, (PFN*)&sRwsCallIndirect) ||
1186 DosQueryProcAddr(hmod, ORD_RWSFREEMEM, 0, (PFN*)&sRwsFreeMem) ||
1187 DosQueryProcAddr(hmod, ORD_RWSGETRESULT, 0, (PFN*)&sRwsGetResult) ||
1188 DosQueryProcAddr(hmod, ORD_RWSGETARGPTR, 0, (PFN*)&sRwsGetArgPtr) ||
1189 DosQueryProcAddr(hmod, ORD_RWSCLIENTINIT, 0, (PFN*)&sRwsClientInit) ||
1190 DosQueryProcAddr(hmod, ORD_RWSGETTIMEOUT, 0, (PFN*)&sRwsGetTimeout) ||
1191 DosQueryProcAddr(hmod, ORD_RWSSETTIMEOUT, 0, (PFN*)&sRwsSetTimeout) ||
1192 DosQueryProcAddr(hmod, ORD_RWSCLIENTTERMINATE, 0, (PFN*)&sRwsClientTerminate)) {
1193 DosFreeModule(hmod);
1194 ERRPRINTF("", "nsRwsServiceInit - DosQueryProcAddr failed%s")
1195 return NS_ERROR_NOT_AVAILABLE;
1198 // init RWS and have it register the WPS class if needed
1199 rc = sRwsClientInit(TRUE);
1200 if (rc) {
1201 ERRMSG(rc, "RwsClientInit")
1202 return NS_ERROR_NOT_AVAILABLE;
1205 // if the user hasn't set a timeout, reset it to 2 seconds
1206 // (the default is 20 seconds)
1207 PRUint32 currentTO;
1208 PRUint32 userTO;
1209 rc = sRwsGetTimeout((PULONG)&currentTO, (PULONG)&userTO);
1210 if (rc)
1211 ERRMSG(rc, "RwsGetTimeout")
1212 else
1213 if (userTO == 0) {
1214 rc = sRwsSetTimeout(2);
1215 if (rc)
1216 ERRMSG(rc, "RwsSetTimeout")
1219 // create an instance of nsRwsService
1220 sRwsInstance = new nsRwsService();
1221 if (sRwsInstance == 0)
1222 return NS_ERROR_OUT_OF_MEMORY;
1224 *aClass = sRwsInstance;
1225 NS_ADDREF(*aClass);
1227 // set the class up as a shutdown observer
1228 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1229 if (os)
1230 os->AddObserver(*aClass, "quit-application", PR_FALSE);
1232 return NS_OK;
1235 //------------------------------------------------------------------------
1237 // this is a variation on NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR();
1238 // the only difference is that the _GetterProc returns an nsresult to
1239 // provide a more appropriate error code (typically NS_ERROR_NOT_AVAILABLE)
1241 NS_IMETHODIMP nsRwsServiceConstructor(nsISupports *aOuter, REFNSIID aIID,
1242 void **aResult)
1244 nsresult rv;
1245 nsRwsService *inst;
1246 *aResult = NULL;
1248 if (aOuter) {
1249 rv = NS_ERROR_NO_AGGREGATION;
1250 return rv;
1253 rv = nsRwsServiceInit(&inst);
1254 if (NS_FAILED(rv)) {
1255 ERRPRINTF(rv, "==>> nsRwsServiceInit failed - rv= %x");
1256 return rv;
1259 rv = inst->QueryInterface(aIID, aResult);
1260 NS_RELEASE(inst);
1262 return rv;
1265 //------------------------------------------------------------------------