RetroZilla/webshell/tests/viewer/JSConsole.cpp
2015-10-20 23:03:22 -04:00

970 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//is case this is defined from the outside... MMP
#ifdef WIN32_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#endif
#include "JSConsole.h"
#include "jsconsres.h"
#include "nsIScriptContext.h"
#include <stdio.h>
#include "jsapi.h"
#include "nsReadableUtils.h"
HINSTANCE JSConsole::sAppInstance = 0;
HACCEL JSConsole::sAccelTable = 0;
CHAR JSConsole::sDefaultCaption[] = "JavaScript Console";
BOOL JSConsole::mRegistered = FALSE;
// display an error string along with the error returned from
// the GetLastError functions
#define MESSAGE_LENGTH 256
void DisplayError(LPSTR lpMessage)
{
CHAR lpMsgBuf[MESSAGE_LENGTH * 2];
CHAR lpLastError[MESSAGE_LENGTH];
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
0,
(LPTSTR)&lpLastError,
MESSAGE_LENGTH,
NULL);
strcpy(lpMsgBuf, lpMessage);
strcat(lpMsgBuf, "\nThe WWS (Worthless Windows System) reports:\n");
strcat(lpMsgBuf, lpLastError);
// Display the string.
::MessageBox(NULL, lpMsgBuf, "JSConsole Error", MB_OK | MB_ICONSTOP);
}
#if defined(_DEBUG)
#define VERIFY(value, errorCondition, message) \
if((value) == (errorCondition)) DisplayError(message);
#else // !_DEBUG
#define VERIFY(value, errorCondition, message) (value)
#endif // _DEBUG
//
// Register the window class
//
BOOL JSConsole::RegisterWidget()
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = JSConsole::WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = JSConsole::sAppInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wc.lpszMenuName = MAKEINTRESOURCE(JSCONSOLE_MENU);
wc.lpszClassName = "JavaScript Console";
return (BOOL)::RegisterClass(&wc);
}
//
// Create the main application window
//
JSConsole* JSConsole::CreateConsole()
{
if (!JSConsole::mRegistered){
JSConsole::mRegistered = RegisterWidget();
}
HWND hWnd = ::CreateWindowEx(WS_EX_ACCEPTFILES |
WS_EX_CLIENTEDGE |
WS_EX_CONTROLPARENT,
"JavaScript Console",
JSConsole::sDefaultCaption,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
450,
500,
NULL,
NULL,
JSConsole::sAppInstance,
NULL);
if (hWnd) {
::ShowWindow(hWnd, SW_SHOW);
::UpdateWindow(hWnd);
JSConsole *console = (JSConsole*)::GetWindowLong(hWnd, GWL_USERDATA);
return console;
}
return NULL;
}
//
// Window Procedure
//
LRESULT CALLBACK JSConsole::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
JSConsole *console = (JSConsole*)::GetWindowLong(hWnd, GWL_USERDATA);
// just ignore the message unless is WM_NCCREATE
if (!console) {
if (uMsg == WM_NCCREATE) {
HWND hWndEdit = ::CreateWindow("EDIT",
NULL,
WS_CHILDWINDOW | WS_VISIBLE | ES_MULTILINE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL |
WS_HSCROLL | WS_VSCROLL,
0, 0, 0, 0,
hWnd,
NULL,
JSConsole::sAppInstance,
NULL);
if (!hWndEdit) {
::DisplayError("Cannot Create Edit Window");
return FALSE;
}
::SendMessage(hWndEdit, EM_SETLIMITTEXT, (WPARAM)0, (LPARAM)0);
console = new JSConsole(hWnd, hWndEdit);
::SetWindowLong(hWnd, GWL_USERDATA, (DWORD)console);
::SetFocus(hWndEdit);
}
#if defined(STRICT)
return ::CallWindowProc((WNDPROC)::DefWindowProc, hWnd, uMsg,
wParam, lParam);
#else
return ::CallWindowProc((FARPROC)::DefWindowProc, hWnd, uMsg,
wParam, lParam);
#endif /* STRICT */
}
switch(uMsg) {
// make sure the edit window covers the whole client area
case WM_SIZE:
return console->OnSize(wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam));
// exit the application
case WM_DESTROY:
console->OnDestroy();
#if defined(STRICT)
return ::CallWindowProc((WNDPROC)::DefWindowProc, hWnd, uMsg,
wParam, lParam);
#else
return ::CallWindowProc((FARPROC)::DefWindowProc, hWnd, uMsg,
wParam, lParam);
#endif /* STRICT */
// enable/disable menu items
case WM_INITMENUPOPUP:
return console->OnInitMenu((HMENU)wParam, (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam));
case WM_COMMAND:
// menu or accelerator
if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) {
switch(LOWORD(wParam)) {
case ID_FILENEW:
console->OnFileNew();
break;
case ID_FILEOPEN:
if (console->OpenFileDialog(OPEN_DIALOG)) {
console->LoadFile();
}
break;
case ID_FILESAVE:
if (console->CanSave()) {
console->SaveFile();
break;
}
// fall through so it can "Save As..."
case ID_FILESAVEAS:
if (console->OpenFileDialog(SAVE_DIALOG)) {
console->SaveFile();
}
break;
case ID_FILEEXIT:
::DestroyWindow(hWnd);
break;
case ID_EDITUNDO:
console->OnEditUndo();
break;
case ID_EDITCUT:
console->OnEditCut();
break;
case ID_EDITCOPY:
console->OnEditCopy();
break;
case ID_EDITPASTE:
console->OnEditPaste();
break;
case ID_EDITDELETE:
console->OnEditDelete();
break;
case ID_EDITSELECTALL:
console->OnEditSelectAll();
break;
case ID_COMMANDSEVALALL:
console->OnCommandEvaluateAll();
break;
case ID_COMMANDSEVALSEL:
console->OnCommandEvaluateSelection();
break;
case ID_COMMANDSINSPECTOR:
console->OnCommandInspector();
break;
}
}
break;
case WM_DROPFILES:
{
HDROP hDropInfo = (HDROP)wParam;
if (::DragQueryFile(hDropInfo, (UINT)-1L, NULL, 0) != 1) {
::MessageBox(hWnd, "Just One File Please...", "JSConsole Error", MB_OK | MB_ICONINFORMATION);
}
else {
CHAR fileName[MAX_PATH];
::DragQueryFile(hDropInfo, 0, fileName, MAX_PATH);
console->SetFileName(fileName);
console->LoadFile();
}
break;
}
case WM_SETFOCUS:
return console->OnSetFocus((HWND)wParam);
default:
#if defined(STRICT)
return ::CallWindowProc((WNDPROC)::DefWindowProc, hWnd, uMsg,
wParam, lParam);
#else
return ::CallWindowProc((FARPROC)::DefWindowProc, hWnd, uMsg,
wParam, lParam);
#endif /* STRICT */
}
return 0;
}
//
// Constructor
// The main window and the edit control must have been created already
//
JSConsole::JSConsole(HWND aMainWindow, HWND aEditControl) :
mMainWindow(aMainWindow),
mEditWindow(aEditControl),
mContext(NULL)
{
mFileInfo.Init();
}
//
// Destructor
//
JSConsole::~JSConsole()
{
NS_IF_RELEASE(mContext);
}
//
// Load a file into the edit field
//
BOOL JSConsole::LoadFile()
{
BOOL result = FALSE;
if (mMainWindow) {
// open the file
HANDLE file = ::CreateFile(mFileInfo.mCurrentFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (file != INVALID_HANDLE_VALUE) {
// check the file size. Max is 64k
DWORD sizeHiWord;
DWORD sizeLoWord = ::GetFileSize(file, &sizeHiWord);
if (sizeLoWord < 0x10000 && sizeHiWord == 0) {
// alloc a buffer big enough to contain the file (account for '\0'
CHAR *buffer = new CHAR[sizeLoWord + 1];
if (buffer) {
// read the file in memory
if (::ReadFile(file,
buffer,
sizeLoWord,
&sizeHiWord,
NULL)) {
NS_ASSERTION(sizeLoWord == sizeHiWord, "ReadFile inconsistency");
buffer[sizeLoWord] = '\0'; // terminate the buffer
// write the file to the edit field
::SendMessage(mEditWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)buffer);
// update the caption
CHAR caption[80];
::wsprintf(caption,
"%s - %s",
mFileInfo.mCurrentFileName + mFileInfo.mFileOffset,
sDefaultCaption);
::SendMessage(mMainWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)caption);
result = TRUE;
}
else {
::DisplayError("Error Reading the File");
}
// free the allocated buffer
delete[] buffer;
}
else {
::MessageBox(mMainWindow,
"Cannot Allocate Enough Memory to Copy the File in Memory",
"JSConsole Error",
MB_OK | MB_ICONSTOP);
}
}
else {
::MessageBox(mMainWindow,
"File too big. Max is 64k",
"JSConsole Error",
MB_OK | MB_ICONSTOP);
}
// close the file handle
::CloseHandle(file);
}
#ifdef _DEBUG
else {
CHAR message[MAX_PATH + 20];
wsprintf(message, "Cannot Open File: %s", mFileInfo.mCurrentFileName);
::DisplayError(message);
}
#endif
}
return result;
}
//
// Save the current text into a file
//
BOOL JSConsole::SaveFile()
{
BOOL result = FALSE;
if (mMainWindow && mFileInfo.mCurrentFileName[0] != '\0') {
// create the new file
HANDLE file = ::CreateFile(mFileInfo.mCurrentFileName,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (file != INVALID_HANDLE_VALUE) {
DWORD size;
// get the text size
size = ::SendMessage(mEditWindow, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
// alloc a buffer big enough to contain the file
CHAR *buffer = new CHAR[++size];
if (buffer) {
DWORD byteRead;
// read the text area content
::SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)size, (LPARAM)buffer);
// write the buffer to disk
if (::WriteFile(file,
buffer,
size,
&byteRead,
NULL)) {
NS_ASSERTION(byteRead == size, "WriteFile inconsistency");
// update the caption
CHAR caption[80];
::wsprintf(caption,
"%s - %s",
mFileInfo.mCurrentFileName + mFileInfo.mFileOffset,
sDefaultCaption);
::SendMessage(mMainWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)caption);
result = TRUE;
}
else {
::DisplayError("Error Writing the File");
}
// free the allocated buffer
delete[] buffer;
}
else {
::MessageBox(mMainWindow,
"Cannot Allocate Enough Memory to Copy the Edit Text in Memory",
"JSConsole Error",
MB_OK | MB_ICONSTOP);
}
// close the file handle
::CloseHandle(file);
}
#ifdef _DEBUG
else {
CHAR message[MAX_PATH + 20];
wsprintf(message, "Cannot Open File: %s", mFileInfo.mCurrentFileName);
::DisplayError(message);
}
#endif
}
return result;
}
//
// Open a FileOpen or FileSave dialog
//
BOOL JSConsole::OpenFileDialog(UINT aWhichDialog)
{
BOOL result = FALSE;
OPENFILENAME ofn;
if (mMainWindow) {
// *.js is the standard File Name on the Save/Open Dialog
if (mFileInfo.mCurrentFileName[0] == '\0')
::strcpy(mFileInfo.mCurrentFileName, "*.js");
// fill the OPENFILENAME sruct
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = mMainWindow;
ofn.hInstance = JSConsole::sAppInstance;
ofn.lpstrFilter = "JavaScript Files (*.js)\0*.js\0Text Files (*.txt)\0*.txt\0All Files\0*.*\0\0";
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 1; // the first one in lpstrFilter
ofn.lpstrFile = mFileInfo.mCurrentFileName; // contains the file path name on return
ofn.nMaxFile = sizeof(mFileInfo.mCurrentFileName);
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL; // use default
ofn.lpstrTitle = NULL; // use default
ofn.Flags = OFN_CREATEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = "js"; // default extension is .js
ofn.lCustData = NULL;
ofn.lpfnHook = NULL;
ofn.lpTemplateName = NULL;
// call the open file dialog or the save file dialog according to aIsOpenDialog
if (aWhichDialog == OPEN_DIALOG) {
result = ::GetOpenFileName(&ofn);
}
else if (aWhichDialog == SAVE_DIALOG) {
result = ::GetSaveFileName(&ofn);
}
if (!result) {
mFileInfo.mCurrentFileName[0] = '\0';
::CommDlgExtendedError();
}
else {
mFileInfo.mFileOffset = ofn.nFileOffset;
mFileInfo.mFileExtension = ofn.nFileExtension;
}
}
return result;
}
//
// set the mFileInfo structure with the proper value given a generic path
//
void JSConsole::SetFileName(LPSTR aFileName)
{
strcpy(mFileInfo.mCurrentFileName, aFileName);
for (int i = strlen(aFileName); i >= 0; i--) {
if (mFileInfo.mCurrentFileName[i] == '.') {
mFileInfo.mFileExtension = i;
}
if (mFileInfo.mCurrentFileName[i] == '\\') {
mFileInfo.mFileOffset = i + 1;
break;
}
}
}
//
// Move the edit window to cover the all client area
//
LRESULT JSConsole::OnSize(DWORD aResizeFlags, UINT aWidth, UINT aHeight)
{
::MoveWindow(mEditWindow, 0, 0, aWidth, aHeight, TRUE);
RECT textArea;
textArea.left = 3;
textArea.top = 1;
textArea.right = aWidth - 20;
textArea.bottom = aHeight - 17;
::SendMessage(mEditWindow, EM_SETRECTNP, (WPARAM)0, (LPARAM)&textArea);
return 0L;
}
//
// Initialize properly menu items
//
LRESULT JSConsole::OnInitMenu(HMENU aMenu, UINT aPos, BOOL aIsSystem)
{
if (!aIsSystem) {
if (aPos == EDITMENUPOS) {
InitEditMenu(aMenu);
}
else if (aPos == COMMANDSMENUPOS) {
InitCommandMenu(aMenu);
}
}
return 0L;
}
//
// Pass the focus to the edit window
//
LRESULT JSConsole::OnSetFocus(HWND aWnd)
{
::SetFocus(mEditWindow);
return 0L;
}
//
// Destroy message
//
void JSConsole::OnDestroy()
{
if (mDestroyNotification)
(*mDestroyNotification)();
}
//
// File/New. Reset caption, text area and file info
//
void JSConsole::OnFileNew()
{
SendMessage(mEditWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)0);
SendMessage(mMainWindow, WM_SETTEXT, (WPARAM)0, (LPARAM)JSConsole::sDefaultCaption);
mFileInfo.Init();
}
//
// Edit/Undo. Undo the last operation on the edit field
//
void JSConsole::OnEditUndo()
{
SendMessage(mEditWindow, WM_UNDO, (WPARAM)0, (LPARAM)0);
}
//
// Edit/Cut. Cut the current selection
//
void JSConsole::OnEditCut()
{
SendMessage(mEditWindow, WM_CUT, (WPARAM)0, (LPARAM)0);
}
//
// Edit/Copy. Copy the current selection
//
void JSConsole::OnEditCopy()
{
SendMessage(mEditWindow, WM_COPY, (WPARAM)0, (LPARAM)0);
}
//
// Edit/Paste. Paste from the clipboard
//
void JSConsole::OnEditPaste()
{
SendMessage(mEditWindow, WM_PASTE, (WPARAM)0, (LPARAM)0);
}
//
// Edit/Delete. Delete the current selection
//
void JSConsole::OnEditDelete()
{
SendMessage(mEditWindow, WM_CLEAR, (WPARAM)0, (LPARAM)0);
}
//
// Edit/Select All. Select the whole text in the text area
//
void JSConsole::OnEditSelectAll()
{
//SendMessage(mEditWindow, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
}
//
// Command/Evaluate All. Take the text area content and evaluate in the js context
//
void JSConsole::OnCommandEvaluateAll()
{
EvaluateText(0, (UINT)-1);
}
//
// Command/Evaluate Selection. Take the current text area selection and evaluate in the js context
//
void JSConsole::OnCommandEvaluateSelection()
{
//
// get the selection and evaluate it
//
DWORD startSel, endSel;
// get selection range
::SendMessage(mEditWindow, EM_GETSEL, (WPARAM)&startSel, (LPARAM)&endSel);
EvaluateText(startSel, endSel);
}
//
// Command/Inspector. Run the js inspector on the global object
//
void JSConsole::OnCommandInspector()
{
::MessageBox(mMainWindow, "Inspector not yet available", "JSConsole Error", MB_OK | MB_ICONINFORMATION);
}
//
// Help
//
void JSConsole::OnHelp()
{
}
//
// private method. Deal with the "Edit" menu
//
void JSConsole::InitEditMenu(HMENU aMenu)
{
CHAR someText[2] = {'\0', '\0'}; // some buffer
// set flags to "disable"
UINT undoFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
UINT cutFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
UINT copyFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
UINT pasteFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
UINT deleteFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
UINT selectAllFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
// check if the edit area has any text
SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)2, (LPARAM)someText);
if (someText[0] != '\0') {
// enable the "Select All"
selectAllFlags = MF_BYPOSITION | MF_ENABLED;
// enable "Copy/Cut/Paste" if there is any selection
UINT startPos, endPos;
SendMessage(mEditWindow, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
if (startPos != endPos) {
cutFlags = MF_BYPOSITION | MF_ENABLED;
copyFlags = MF_BYPOSITION | MF_ENABLED;
deleteFlags = MF_BYPOSITION | MF_ENABLED;
}
}
// undo is available if the edit control says so
if (SendMessage(mEditWindow, EM_CANUNDO, (WPARAM)0, (LPARAM)0)) {
undoFlags = MF_BYPOSITION | MF_ENABLED;
}
// check whether or not the clipboard contains text data
if (IsClipboardFormatAvailable(CF_TEXT)) {
pasteFlags = MF_BYPOSITION | MF_ENABLED;
}
// do enable/disable
VERIFY(EnableMenuItem(aMenu,
ID_EDITUNDO - ID_EDITMENU - 1,
undoFlags),
-1L,
"Disable/Enable \"Undo\" Failed");
VERIFY(EnableMenuItem(aMenu,
ID_EDITCUT - ID_EDITMENU - 1,
cutFlags),
-1L,
"Disable/Enable \"Cut\" Failed");
VERIFY(EnableMenuItem(aMenu,
ID_EDITCOPY - ID_EDITMENU - 1,
copyFlags),
-1L,
"Disable/Enable \"Copy\" Failed");
VERIFY(EnableMenuItem(aMenu,
ID_EDITPASTE - ID_EDITMENU - 1,
pasteFlags),
-1L,
"Disable/Enable \"Paste\" Failed");
VERIFY(EnableMenuItem(aMenu,
ID_EDITDELETE - ID_EDITMENU - 1,
deleteFlags),
-1L,
"Disable/Enable \"Delete\" Failed");
VERIFY(EnableMenuItem(aMenu,
ID_EDITSELECTALL - ID_EDITMENU - 1,
selectAllFlags),
-1L,
"Disable/Enable \"Select All\" Failed");
}
//
// private method. Deal with the "Command" menu
//
void JSConsole::InitCommandMenu(HMENU aMenu)
{
CHAR someText[2] = {'\0', '\0'}; // some buffer
// set flags to "disable"
UINT evaluateAllFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
UINT evaluateSelectionFlags = MF_BYPOSITION | MF_DISABLED | MF_GRAYED;
// check if the edit area has any text
SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)2, (LPARAM)someText);
if (someText[0] != 0) {
// if there is some text enable "Evaluate All"
evaluateAllFlags = MF_BYPOSITION | MF_ENABLED;
// enable "Evaluate Selection" if there is any selection
UINT startPos, endPos;
SendMessage(mEditWindow, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
if (startPos != endPos) {
evaluateSelectionFlags = MF_BYPOSITION | MF_ENABLED;
}
}
// disable/enable commands
VERIFY(EnableMenuItem(aMenu,
ID_COMMANDSEVALALL - ID_COMMANDSMENU - 1,
evaluateAllFlags),
-1L,
"Disable/Enable \"Evaluate All\" Failed");
VERIFY(EnableMenuItem(aMenu,
ID_COMMANDSEVALSEL - ID_COMMANDSMENU - 1,
evaluateSelectionFlags),
-1L,
"Disable/Enable \"Evaluate Selection\" Failed");
}
//
// normailize a buffer of char coming from a text area.
// Basically get rid of the 0x0D char
//
LPSTR NormalizeBuffer(LPSTR aBuffer)
{
// trim all the 0x0D at the beginning (should be 1 at most, but hey...)
while (*aBuffer == 0x0D) {
aBuffer++;
}
LPSTR readPointer = aBuffer;
LPSTR writePointer = aBuffer;
do {
// compact the buffer if needed
*writePointer = *readPointer;
// skip the 0x0D
if (*readPointer != 0x0D) {
writePointer++;
}
} while (*readPointer++ != '\0');
return aBuffer;
}
LPSTR PrepareForTextArea(LPSTR aBuffer, PRInt32 aSize)
{
PRInt32 count = 0;
LPSTR newBuffer = aBuffer;
LPSTR readPointer = aBuffer;
// count the '\n'
while (*readPointer != '\0' && (*readPointer++ != '\n' || ++count));
if (0 != count) {
readPointer = aBuffer;
newBuffer = new CHAR[aSize + count + 1];
LPSTR writePointer = newBuffer;
while (*readPointer != '\0') {
if (*readPointer == '\n') {
*writePointer++ = 0x0D;
}
*writePointer++ = *readPointer++;
}
*writePointer = '\0';
}
return newBuffer;
}
//
// Evaluate the text enclosed between startSel and endSel
//
void JSConsole::EvaluateText(UINT aStartSel, UINT aEndSel)
{
if (mContext) {
// get the text size
UINT size = ::SendMessage(mEditWindow, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
// alloc a buffer big enough to contain the file
CHAR *buffer = new CHAR[++size];
if (buffer) {
// get the whole text
::SendMessage(mEditWindow, WM_GETTEXT, (WPARAM)size, (LPARAM)buffer);
// get the portion of the text to be evaluated
if (aEndSel != (UINT)-1) {
size = aEndSel - aStartSel;
strncpy(buffer, buffer + aStartSel, size);
buffer[size] = '\0';
}
else {
aEndSel = size;
}
// change the 0x0D0x0A couple in 0x0A ('\n')
// no new buffer allocation, the pointer may be in the middle
// of the old buffer though, so keep buffer to call delete
LPSTR cleanBuffer = ::NormalizeBuffer(buffer);
// evaluate the string
nsAutoString returnValue;
PRBool isUndefined;
if (NS_SUCCEEDED(mContext->EvaluateString(NS_ConvertASCIItoUCS2(cleanBuffer),
nsnull,
nsnull,
nsnull,
0,
nsnull,
&returnValue,
&isUndefined))) {
// output the result in the edit area
CHAR result[128];
LPSTR res = result;
int bDelete = 0;
JSContext *cx = (JSContext *)mContext->GetNativeContext();
char *str = ToNewCString(returnValue);
// make a string with 0xA changed to 0xD0xA
res = PrepareForTextArea(str, returnValue.Length());
if (res != str) {
bDelete = 1; // if the buffer was new'ed
}
// set the position at the end of the selection
::SendMessage(mEditWindow, EM_SETSEL, (WPARAM)aEndSel, (LPARAM)aEndSel);
// write the result
::SendMessage(mEditWindow, EM_REPLACESEL, (WPARAM)TRUE, (LPARAM)res);
// highlight the result
::SendMessage(mEditWindow, EM_SETSEL, (WPARAM)aEndSel - 1, (LPARAM)(aEndSel + strlen(res)));
// deal with the "big string" case
if (bDelete > 0) {
delete[] res;
}
delete[] str;
// clean up a bit
JS_GC((JSContext *)mContext->GetNativeContext());
}
else {
::MessageBox(mMainWindow,
"Error evaluating the Script",
"JSConsole Error",
MB_OK | MB_ICONERROR);
}
delete[] buffer;
}
else {
::MessageBox(mMainWindow,
"Not Enough Memory to Allocate a Buffer to Evaluate the Script",
"JSConsole Error",
MB_OK | MB_ICONSTOP);
}
}
else {
::MessageBox(mMainWindow,
"Java Script Context not initialized",
"JSConsole Error",
MB_OK | MB_ICONSTOP);
}
}