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