Back to index

lightning-sunbird  0.9+nobinonly
MRJConsole.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the MRJ Carbon OJI Plugin.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corp.
00018  * Portions created by the Initial Developer are Copyright (C) 2001
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Patrick C. Beard <beard@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039        MRJConsole.cpp
00040        
00041        Implements the JVM console interface.
00042        
00043        by Patrick C. Beard.
00044  */
00045 
00046 #include <Appearance.h>
00047 #include <Gestalt.h>
00048 
00049 #include <string.h>
00050 
00051 #include "MRJConsole.h"
00052 #include "MRJPlugin.h"
00053 #include "MRJSession.h"
00054 #include "TopLevelFrame.h"
00055 
00056 #include "nsIPluginManager2.h"
00057 
00058 extern nsIPluginManager2* thePluginManager2;
00059 
00060 MRJConsole* theConsole = NULL;
00061 
00062 const InterfaceInfo MRJConsole::sInterfaces[] = {
00063        { NS_IJVMCONSOLE_IID, INTERFACE_OFFSET(MRJConsole, nsIJVMConsole) },
00064        { NS_IEVENTHANDLER_IID, INTERFACE_OFFSET(MRJConsole, nsIEventHandler) },
00065 };
00066 const UInt32 MRJConsole::kInterfaceCount = sizeof(sInterfaces) / sizeof(InterfaceInfo);
00067 
00068 MRJConsole::MRJConsole(MRJPlugin* plugin)
00069        :      SupportsMixin(this, sInterfaces, kInterfaceCount, (nsIPlugin*) plugin),
00070               mPlugin(plugin), mSession(NULL), mIsInitialized(PR_FALSE),
00071               mConsoleClass(NULL), mInitMethod(NULL), mDisposeMethod(NULL),
00072               mShowMethod(NULL), mHideMethod(NULL), mVisibleMethod(NULL), mPrintMethod(NULL), mFinishMethod(NULL),
00073               mResults(NULL), mContext(NULL), mFrame(NULL)
00074 {
00075        // Initialize();
00076 }
00077 
00078 MRJConsole::~MRJConsole()
00079 {
00080        theConsole = NULL;
00081 
00082        if (mSession != NULL) {
00083               JNIEnv* env = mSession->getCurrentEnv();
00084               
00085               if (mConsoleClass != NULL) {
00086                      if (mDisposeMethod != NULL)
00087                             env->CallStaticVoidMethod(mConsoleClass, mDisposeMethod);
00088                      env->DeleteGlobalRef(mConsoleClass);
00089                      mConsoleClass = NULL;
00090               }
00091               
00092               if (mResults != NULL) {
00093                      env->DeleteGlobalRef(mResults);
00094                      mResults = NULL;
00095               }
00096 
00097 #if !TARGET_CARBON          
00098               if (mContext != NULL) {
00099                      JMDisposeAWTContext(mContext);
00100                      mContext = NULL;
00101               }
00102 #endif
00103        }
00104 }
00105 
00106 NS_METHOD MRJConsole::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00107 {
00108        nsresult result = queryInterface(aIID, aInstancePtr);
00109        if (result == NS_NOINTERFACE)
00110               result = mPlugin->queryInterface(aIID, aInstancePtr);
00111        return result;
00112 }
00113 nsrefcnt MRJConsole::AddRef() { return addRef(); }
00114 nsrefcnt MRJConsole::Release() { return release(); }
00115 
00116 #pragma mark ***** MRJConsole *****
00117 
00118 NS_METHOD MRJConsole::Show()
00119 {
00120        Initialize();
00121 
00122        if (mShowMethod != NULL) {
00123               CallConsoleMethod(mShowMethod);
00124               return NS_OK;
00125        }
00126        
00127        return NS_ERROR_FAILURE;
00128 }
00129 
00130 NS_METHOD MRJConsole::Hide()
00131 {
00132        Initialize();
00133 
00134        if (mHideMethod != NULL) {
00135               CallConsoleMethod(mHideMethod);
00136               return NS_OK;
00137        }
00138 
00139        return NS_ERROR_FAILURE;
00140 }
00141 
00142 NS_METHOD MRJConsole::IsVisible(PRBool* isVisible)
00143 {
00144        // don't initialize here, because if we haven't been initialized, it can't be visible.
00145        *isVisible = PR_FALSE;
00146        if (mVisibleMethod != NULL) {
00147               CallConsoleMethod(mVisibleMethod, mResults);
00148               jboolean isCopy;
00149               JNIEnv* env = mSession->getCurrentEnv();
00150               jboolean* elements = env->GetBooleanArrayElements(mResults, &isCopy);
00151               *isVisible = elements[0];
00152               env->ReleaseBooleanArrayElements(mResults, elements, JNI_ABORT);
00153        }
00154        return NS_OK;
00155 }
00156 
00157 // Prints a message to the Java console. The encodingName specifies the
00158 // encoding of the message, and if NULL, specifies the default platform
00159 // encoding.
00160 
00161 NS_METHOD MRJConsole::Print(const char* msg, const char* encodingName)
00162 {
00163        Initialize();
00164 
00165        if (mPrintMethod != NULL) {
00166               JNIEnv* env = mSession->getCurrentEnv();
00167               jstring jmsg = env->NewStringUTF(msg);
00168               jvalue args[1]; args[0].l = jmsg;
00169 #if TARGET_CARBON
00170         // just use JNI?
00171 #else
00172               JMExecJNIStaticMethodInContext(mContext, env, mConsoleClass, mPrintMethod, 1, args);
00173 #endif
00174               env->DeleteLocalRef(jmsg);
00175               return NS_OK;
00176        }
00177        
00178        return NS_ERROR_FAILURE;
00179 }
00180 
00181 // Writes a message to the Java console immediately, in the current thread.
00182 
00183 void MRJConsole::write(const void *message, UInt32 messageLengthInBytes)
00184 {
00185        if (mPrintMethod != NULL) {
00186               char* buffer = new char[messageLengthInBytes + 1];
00187               if (buffer != NULL) {
00188                      memcpy(buffer, message, messageLengthInBytes);
00189                      buffer[messageLengthInBytes] = '\0';
00190 
00191                      JNIEnv* env = mSession->getCurrentEnv();
00192                      jstring jmsg = env->NewStringUTF(buffer);
00193                      if (jmsg != NULL) {
00194                             env->CallStaticVoidMethod(mConsoleClass, mPrintMethod, jmsg);
00195                             env->DeleteLocalRef(jmsg);
00196                      }
00197 
00198                      delete buffer;
00199               }
00200        }
00201 }
00202 
00203 NS_METHOD MRJConsole::HandleEvent(nsPluginEvent* pluginEvent, PRBool* eventHandled)
00204 {
00205        *eventHandled = PR_TRUE;
00206 
00207        if (pluginEvent != NULL) {
00208               EventRecord* event = pluginEvent->event;
00209 
00210               if (event->what == nullEvent) {
00211                      // Give MRJ another quantum of time.
00212                      MRJSession* session = mPlugin->getSession();
00213                      session->idle(kDefaultJMTime);
00214               } else {
00215                      TopLevelFrame* frame = mFrame;
00216                      if (frame != NULL) {
00217                             switch (event->what) {
00218                             case nsPluginEventType_GetFocusEvent:
00219                                    frame->focusEvent(true);
00220                                    break;
00221                             
00222                             case nsPluginEventType_LoseFocusEvent:
00223                                    frame->focusEvent(false);
00224                                    break;
00225 
00226                             case nsPluginEventType_AdjustCursorEvent:
00227                                    frame->idle(event->modifiers);
00228                                    break;
00229                             
00230                             case nsPluginEventType_MenuCommandEvent:
00231                                    frame->menuSelected(event->message, event->modifiers);
00232                                    break;
00233                             
00234                             default:
00235                                    *eventHandled = frame->handleEvent(event);
00236                                    break;
00237                             }
00238                      }
00239               }
00240        }
00241        
00242        return NS_OK;
00243 }
00244 
00245 OSStatus MRJConsole::CallConsoleMethod(jmethodID method)
00246 {
00247        JNIEnv* env = mSession->getCurrentEnv();
00248 #if TARGET_CARBON
00249     // just call directly?
00250     env->CallStaticVoidMethod(mConsoleClass, method);
00251     return noErr;
00252 #else
00253        OSStatus status = JMExecJNIStaticMethodInContext(mContext, env, mConsoleClass, method, 0, NULL);
00254        env->CallStaticVoidMethod(mConsoleClass, mFinishMethod);
00255        return status;
00256 #endif
00257 }
00258 
00259 OSStatus MRJConsole::CallConsoleMethod(jmethodID method, jobject arg)
00260 {
00261        JNIEnv* env = mSession->getCurrentEnv();
00262 #if TARGET_CARBON
00263     // just call directly?
00264     env->CallStaticVoidMethod(mConsoleClass, method, arg);
00265     return noErr;
00266 #else
00267        jvalue args[1];
00268        args[0].l = arg;
00269        OSStatus status = JMExecJNIStaticMethodInContext(mContext, env, mConsoleClass, method, 1, args);
00270        env->CallStaticVoidMethod(mConsoleClass, mFinishMethod);
00271        return status;
00272 #endif
00273 }
00274 
00275 #if !TARGET_CARBON
00276 
00277 static OSStatus requestFrame(JMAWTContextRef contextRef, JMFrameRef frameRef, JMFrameKind kind,
00278                                                                const Rect* initialBounds, Boolean resizeable, JMFrameCallbacks* cb)
00279 {
00280        extern JMFrameCallbacks theFrameCallbacks;
00281        // set up the viewer frame's callbacks.
00282        BlockMoveData(&theFrameCallbacks, cb, sizeof(theFrameCallbacks));
00283 
00284        MRJConsole* thisConsole = NULL;
00285        OSStatus status = ::JMGetAWTContextData(contextRef, (JMClientData*)&thisConsole);
00286        if (status == noErr && thePluginManager2 != NULL) {
00287               // Can only do this safely if we are using the new API.
00288               TopLevelFrame* frame = new TopLevelFrame(thisConsole, frameRef, kind, initialBounds, resizeable);
00289               status = ::JMSetFrameData(frameRef, frame);
00290               thisConsole->setFrame(frame);
00291        }
00292        return status;
00293 }
00294 
00295 static OSStatus releaseFrame(JMAWTContextRef contextRef, JMFrameRef frameRef)
00296 {
00297        MRJConsole* thisConsole = NULL;
00298        OSStatus status = ::JMGetAWTContextData(contextRef, (JMClientData*)&thisConsole);
00299        MRJFrame* thisFrame = NULL;
00300        status = ::JMGetFrameData(frameRef, (JMClientData*)&thisFrame);
00301        if (thisFrame != NULL) {
00302               status = ::JMSetFrameData(frameRef, NULL);
00303               thisConsole->setFrame(NULL);
00304               delete thisFrame;
00305        }
00306        return status;
00307 }
00308 
00309 static SInt16 getUniqueMenuID(JMAWTContextRef contextRef, Boolean isSubmenu)
00310 {
00311        MRJConsole* thisConsole = NULL;
00312        OSStatus status = ::JMGetAWTContextData(contextRef, (JMClientData*)&thisConsole);
00313        if (thePluginManager2 != NULL) {
00314               PRInt16 menuID;
00315               if (thePluginManager2->AllocateMenuID(thisConsole, isSubmenu, &menuID) == NS_OK)
00316                      return menuID;
00317        }
00318        return -1;
00319 }
00320 
00321 static Boolean appearanceManagerExists()
00322 {
00323        long response = 0;
00324        return (Gestalt(gestaltAppearanceAttr, &response) == noErr && (response & (1 << gestaltAppearanceExists)));
00325 }
00326 
00327 static OSStatus JMTextToStr255(JMTextRef textRef, Str255 str)
00328 {
00329        UInt32 length = 0;
00330        OSStatus status = JMGetTextBytes(textRef, kTextEncodingMacRoman, &str[1], sizeof(Str255) - 1, &length);
00331        if (status == noErr)
00332               str[0] = (unsigned char)(status == noErr ? length : 0);
00333        return status;
00334 }
00335 
00336 inline int min(int x, int y) { return (x <= y ? x : y); }
00337 
00338 static void push(StringPtr dest, StringPtr str)
00339 {
00340        int length = dest[0];
00341        int newLength = min(255, length + str[0]);
00342        if (newLength > length)
00343               ::BlockMoveData(&str[1], &dest[1] + length, newLength - length);
00344 }
00345 
00346 static void exceptionOccurred(JMAWTContextRef context, JMTextRef exceptionName, JMTextRef exceptionMsg, JMTextRef stackTrace)
00347 {
00348        // why not display this using the Appearance Manager's wizzy new alert?
00349        if (appearanceManagerExists()) {
00350               OSStatus status;
00351               Str255 preason = { '\0' }, pmessage = { '\0'}, ptrace = { '\0' };
00352               if (exceptionName != NULL) {
00353                      status = ::JMTextToStr255(exceptionName, preason);
00354                      if (exceptionMsg != NULL) {
00355                             status = ::JMTextToStr255(exceptionMsg, pmessage);
00356                             push(preason, "\p (");
00357                             push(preason, pmessage);
00358                             push(preason, "\p)");
00359                      }
00360               }
00361               
00362               if (stackTrace != NULL)
00363                      status = ::JMTextToStr255(stackTrace, ptrace);
00364               
00365               SInt16 itemHit = 0;
00366               OSErr result = ::StandardAlert(kAlertPlainAlert, preason, ptrace, NULL, &itemHit);
00367        }
00368 }
00369 
00370 #endif
00371 
00372 void MRJConsole::Initialize()
00373 {
00374        if (mIsInitialized)
00375               return;
00376        
00377        mSession = mPlugin->getSession();
00378        JNIEnv* env = mSession->getCurrentEnv();
00379        
00380        jclass consoleClass = env->FindClass("netscape/oji/MRJConsole");
00381        if (consoleClass == NULL) return;
00382        mConsoleClass = (jclass) env->NewGlobalRef(consoleClass);
00383        
00384        mInitMethod = env->GetStaticMethodID(consoleClass, "init", "()V");
00385        mDisposeMethod = env->GetStaticMethodID(consoleClass, "dispose", "()V");
00386        mShowMethod = env->GetStaticMethodID(consoleClass, "show", "()V");
00387        mHideMethod = env->GetStaticMethodID(consoleClass, "hide", "()V");
00388        mVisibleMethod = env->GetStaticMethodID(consoleClass, "visible", "([Z)V");
00389        mPrintMethod = env->GetStaticMethodID(consoleClass, "print", "(Ljava/lang/String;)V");
00390        mFinishMethod = env->GetStaticMethodID(consoleClass, "finish", "()V");
00391        env->DeleteLocalRef(consoleClass);
00392        
00393        jbooleanArray results = env->NewBooleanArray(1);
00394        mResults = (jbooleanArray) env->NewGlobalRef(results);
00395        env->DeleteLocalRef(results);
00396 
00397     OSStatus status = noErr;
00398 
00399 #if !TARGET_CARBON   
00400        // Create an AWT context to work in.
00401        JMAWTContextCallbacks callbacks = {
00402               kJMVersion,                                      /* should be set to kJMVersion */
00403               &requestFrame,                                   /* a new frame is being created. */
00404               &releaseFrame,                                   /* an existing frame is being destroyed. */
00405               &getUniqueMenuID,                         /* a new menu will be created with this id. */
00406               &exceptionOccurred,                       /* just some notification that some recent operation caused an exception.  You can't do anything really from here. */
00407        };
00408        status = ::JMNewAWTContext(&mContext, mSession->getSessionRef(), &callbacks, this);
00409 #endif
00410 
00411        // Finally, call the Java initialize method, and wait for it to complete.
00412 
00413        if (mInitMethod != NULL && status == noErr)
00414            status = CallConsoleMethod(mInitMethod);
00415 
00416        mIsInitialized = (status == noErr);
00417 
00418        if (mIsInitialized)
00419               theConsole = this;
00420 }