pjs/extensions/xmlterm/base/mozXMLTermSession.cpp

4302 строки
123 KiB
C++

/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "MPL"); you may not use this file
* except in compliance with the MPL. You may obtain a copy of
* the MPL at http://www.mozilla.org/MPL/
*
* Software distributed under the MPL is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the MPL for the specific language governing
* rights and limitations under the MPL.
*
* The Original Code is XMLterm.
*
* The Initial Developer of the Original Code is Ramalingam Saravanan.
* Portions created by Ramalingam Saravanan <svn@xmlterm.org> are
* Copyright (C) 1999 Ramalingam Saravanan. All Rights Reserved.
*
* Contributor(s):
*/
// mozXMLTermSession.cpp: implementation of mozXMLTermSession class
#include "nscore.h"
#include "prlog.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIAllocator.h"
#include "nsIDocumentViewer.h"
#include "nsITextContent.h"
#include "nsIDOMElement.h"
#include "nsIDOMSelection.h"
#include "nsIDOMText.h"
#include "nsIDOMAttr.h"
#include "nsIDOMNamedNodeMap.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIDOMNSRange.h"
#include "nsIViewManager.h"
#include "nsIScrollableView.h"
#include "nsIHTMLContent.h"
#include "mozXMLT.h"
#include "mozILineTermAux.h"
#include "mozIXMLTerminal.h"
#include "mozXMLTermUtils.h"
#include "mozXMLTermSession.h"
/////////////////////////////////////////////////////////////////////////
// mozXMLTermSession definition
/////////////////////////////////////////////////////////////////////////
static const char* kWhitespace=" \b\t\r\n";
const char* const mozXMLTermSession::sessionElementNames[] = {
"session",
"entry",
"input",
"output",
"prompt",
"command",
"stdin",
"stdout",
"stderr",
"mixed",
"warning"
};
// Should HTML event names should always be in lower case for DOM to work?
const char* const mozXMLTermSession::sessionEventNames[] = {
"click"
};
const char* const mozXMLTermSession::metaCommandNames[] = {
"",
"default",
"http",
"js",
"tree",
"ls"
};
const char* const mozXMLTermSession::fileTypeNames[] = {
"plainfile",
"directory",
"executable"
};
const char* const mozXMLTermSession::treeActionNames[] = {
"^",
"v",
"<",
">",
"A",
"H"
};
mozXMLTermSession::mozXMLTermSession() :
mInitialized(PR_FALSE),
mXMLTerminal(nsnull),
mPresShell(nsnull),
mDOMDocument(nsnull),
mBodyNode(nsnull),
mSessionNode(nsnull),
mCurrentDebugNode(nsnull),
mStartEntryNode(nsnull),
mCurrentEntryNode(nsnull),
mMaxHistory(20),
mStartEntryNumber(0),
mCurrentEntryNumber(0),
mEntryHasOutput(false),
mPromptSpanNode(nsnull),
mCommandSpanNode(nsnull),
mInputTextNode(nsnull),
mOutputBlockNode(nsnull),
mOutputDisplayNode(nsnull),
mOutputTextNode(nsnull),
mXMLTermStream(nsnull),
mOutputType(LINE_OUTPUT),
mOutputDisplayType(NO_NODE),
mOutputMarkupType(PLAIN_TEXT),
mMetaCommandType(NO_META_COMMAND),
mAutoDetect(FIRST_LINE),
mFirstOutputLine(false),
mEntryOutputLines(0),
mPreTextBufferLines(0),
mPreTextIncomplete(""),
mPreTextBuffered(""),
mPreTextDisplayed(""),
mScreenNode(nsnull),
mScreenRows(0),
mScreenCols(0),
mTopScrollRow(0),
mBotScrollRow(0),
mRestoreInputEcho(false),
mShellPrompt(""),
mPromptHTML(""),
mFragmentBuffer("")
{
}
mozXMLTermSession::~mozXMLTermSession()
{
if (mInitialized) {
Finalize();
}
}
// Initialize XMLTermSession
NS_IMETHODIMP mozXMLTermSession::Init(mozIXMLTerminal* aXMLTerminal,
nsIPresShell* aPresShell,
nsIDOMDocument* aDOMDocument,
PRInt32 nRows, PRInt32 nCols)
{
XMLT_LOG(mozXMLTermSession::Init,30,("\n"));
if (mInitialized)
return NS_ERROR_ALREADY_INITIALIZED;
if (!aXMLTerminal || !aPresShell || !aDOMDocument)
return NS_ERROR_NULL_POINTER;
mXMLTerminal = aXMLTerminal; // containing XMLTerminal; no addref
mPresShell = aPresShell; // presentation shell; no addref
mDOMDocument = aDOMDocument; // DOM document; no addref
mScreenRows = nRows;
mScreenCols = nCols;
mTopScrollRow = mScreenRows - 1;
mBotScrollRow = 0;
nsresult result = NS_OK;
nsCOMPtr<nsIDOMHTMLDocument> vDOMHTMLDocument
(do_QueryInterface(mDOMDocument));
if (!vDOMHTMLDocument)
return NS_ERROR_FAILURE;
// Locate document body node
nsCOMPtr<nsIDOMNodeList> nodeList;
nsAutoString bodyTag = "body";
result = vDOMHTMLDocument->GetElementsByTagName(bodyTag,
getter_AddRefs(nodeList));
if (NS_FAILED(result) || !nodeList)
return NS_ERROR_FAILURE;
PRUint32 count;
nodeList->GetLength(&count);
PR_ASSERT(count==1);
result = nodeList->Item(0, getter_AddRefs(mBodyNode));
if (NS_FAILED(result) || !mBodyNode)
return NS_ERROR_FAILURE;
// Use body node as session node by default
mSessionNode = mBodyNode;
nsCOMPtr<nsIDOMElement> sessionElement;
nsAutoString sessionID = sessionElementNames[SESSION_ELEMENT];
result = vDOMHTMLDocument->GetElementById(sessionID,
getter_AddRefs(sessionElement));
if (NS_SUCCEEDED(result) && sessionElement) {
// Specific session node
mSessionNode = do_QueryInterface(sessionElement);
}
mCurrentDebugNode = mSessionNode;
// Create preface element to display initial output
result = NewPreface();
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
#if 0
nsAutoString prefaceText ("Preface");
nsAutoString nullStyle ("");
result = AppendOutput(prefaceText, nullStyle, true);
#endif
mInitialized = PR_TRUE;
XMLT_LOG(mozXMLTermSession::Init,31,("exiting\n"));
return result;
}
// De-initialize XMLTermSession
NS_IMETHODIMP mozXMLTermSession::Finalize(void)
{
mScreenNode = nsnull;
mOutputBlockNode = nsnull;
mOutputDisplayNode = nsnull;
mOutputTextNode = nsnull;
mXMLTermStream = nsnull;
mPromptSpanNode = nsnull;
mCommandSpanNode = nsnull;
mInputTextNode = nsnull;
mStartEntryNode = nsnull;
mCurrentEntryNode = nsnull;
mBodyNode = nsnull;
mSessionNode = nsnull;
mCurrentDebugNode = nsnull;
mXMLTerminal = nsnull;
mPresShell = nsnull;
mDOMDocument = nsnull;
mInitialized = PR_FALSE;
return NS_OK;
}
/** Resizes XMLterm to match a resized window.
* @param lineTermAux LineTermAux object to be resized (may be null)
*/
NS_IMETHODIMP mozXMLTermSession::Resize(mozILineTermAux* lineTermAux)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::Resize,70,("\n"));
// Determine current screen dimensions
PRInt32 nRows, nCols, xPixels, yPixels;
result = mXMLTerminal->ScreenSize(nRows, nCols, xPixels, yPixels);
if (NS_FAILED(result))
return result;
// If dimensions haven't changed, do nothing
if ((nRows == mScreenRows) && (nCols == mScreenCols))
return NS_OK;
mScreenRows = nRows;
mScreenCols = nCols;
mTopScrollRow = mScreenRows - 1;
mBotScrollRow = 0;
XMLT_LOG(mozXMLTermSession::Resize,0,
("Resizing XMLterm, nRows=%d, nCols=%d\n", mScreenRows, mScreenCols));
if (lineTermAux) {
// Resize associated LineTerm
result = lineTermAux->ResizeAux(mScreenRows, mScreenCols);
if (NS_FAILED(result))
return result;
}
return NS_OK;
}
/** Preprocesses user input before it is transmitted to LineTerm
* @param aString (inout) input data to be preprocessed
* @param consumed (output) true if input data has been consumed
* @param checkSize (output) true if terminal size needs to be checked
*/
NS_IMETHODIMP mozXMLTermSession::Preprocess(const nsString& aString,
PRBool& consumed,
PRBool& checkSize)
{
XMLT_LOG(mozXMLTermSession::Preprocess,70,("\n"));
consumed = false;
checkSize = false;
if (mMetaCommandType == TREE_META_COMMAND) {
if (aString.Length() == 1) {
// Navigate the DOM tree from keyboard
PRUnichar uch = aString.CharAt(0);
XMLT_LOG(mozXMLTermSession::Preprocess,60,("char=0x%x\n", uch));
consumed = true;
switch (uch) {
case U_CTL_B:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_LEFT);
break;
case U_CTL_F:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_RIGHT);
break;
case U_CTL_N:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_DOWN);
break;
case U_CTL_P:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_UP);
break;
case U_A_CHAR:
case U_a_CHAR:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_PRINT_ATTS);
break;
case U_H_CHAR:
case U_h_CHAR:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_PRINT_HTML);
break;
case U_Q_CHAR:
case U_q_CHAR:
case U_CTL_C:
// End of keyboard command sequence; reset debug node to session node
mCurrentDebugNode = mSessionNode;
mMetaCommandType = NO_META_COMMAND;
break;
default:
break;
}
}
} else {
if ((mScreenNode == nsnull) &&
(aString.FindCharInSet("\r\n\017") >= 0)) {
// C-Return or Newline or Control-O found in string; not screen mode;
// resize terminal, if need be
checkSize = true;
XMLT_LOG(mozXMLTermSession::Preprocess,72,("checkSize\n"));
}
}
return NS_OK;
}
/** Reads all available data from LineTerm and displays it;
* returns when no more data is available.
* @param lineTermAux LineTermAux object to read data from
* @param processedData (output) true if any data was processed
*/
NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux,
PRBool& processedData)
{
PRInt32 opcodes, opvals, buf_row, buf_col;
PRUnichar *buf_str, *buf_style;
PRBool newline, errorFlag, streamData, screenData;
nsAutoString bufString, bufStyle;
nsAutoString abortCode = "";
XMLT_LOG(mozXMLTermSession::ReadAll,60,("\n"));
processedData = false;
if (lineTermAux == nsnull)
return NS_ERROR_FAILURE;
nsresult result = NS_OK;
PRBool flushOutput = false;
PRBool metaNextCommand = false;
// NOTE: Do not execute return statements within this loop ;
// always break out of the loop after setting result to an error value,
// allowing cleanup processing on error
for (;;) {
// NOTE: Remember to de-allocate buf_str and buf_style
// using nsAllocator::Free, if opcodes != 0
result = lineTermAux->ReadAux(&opcodes, &opvals, &buf_row, &buf_col,
&buf_str, &buf_style);
if (NS_FAILED(result)) {
abortCode = "lineTermReadAux";
break;
}
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("opcodes=0x%x,mOutputType=%d,mEntryHasOutput=%d\n",
opcodes, mOutputType, mEntryHasOutput));
if (opcodes == 0) break;
processedData = true;
screenData = (opcodes & LTERM_SCREENDATA_CODE);
streamData = (opcodes & LTERM_STREAMDATA_CODE);
newline = (opcodes & LTERM_NEWLINE_CODE);
errorFlag = (opcodes & LTERM_ERROR_CODE);
// Copy character/style strings
bufString = buf_str;
bufStyle = buf_style;
// De-allocate buf_str, buf_style using nsAllocator::Free
nsAllocator::Free(buf_str);
nsAllocator::Free(buf_style);
char* temCString = bufString.ToNewCString();
XMLT_LOG(mozXMLTermSession::ReadAll,68,("bufString=%s\n", temCString));
nsCRT::free(temCString);
if (screenData && (mOutputType != SCREEN_OUTPUT)) {
// Initiate screen mode
XMLT_LOG(mozXMLTermSession::ReadAll,62,("Initiate SCREEN mode\n"));
// Break output display
result = BreakOutput(PR_FALSE);
if (NS_FAILED(result))
break;
// Create screen element
result = NewScreen();
if (NS_FAILED(result))
break;
mOutputType = SCREEN_OUTPUT;
// Disable input echo
lineTermAux->SetEchoFlag(false);
mRestoreInputEcho = true;
}
if (!screenData && (mOutputType == SCREEN_OUTPUT)) {
// Terminate screen mode
mOutputType = LINE_OUTPUT;
XMLT_LOG(mozXMLTermSession::ReadAll,0,
("Terminating screen mode\n"));
// Delete screen element
nsCOMPtr<nsIDOMNode> resultNode;
mSessionNode->RemoveChild(mScreenNode, getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
mScreenNode = nsnull;
if (mRestoreInputEcho) {
lineTermAux->SetEchoFlag(true);
mRestoreInputEcho = false;
}
// Show the caret
// WORKAROUND for some unknown bug in the full screen implementation.
// Without this, if you delete a line using "vi" and save the file,
// the cursor suddenly disappears
mXMLTerminal->ShowCaret();
}
if (streamData) {
// Process stream data
if (mOutputType != STREAM_OUTPUT) {
mOutputType = STREAM_OUTPUT;
// Disable input echo
lineTermAux->SetEchoFlag(false);
mRestoreInputEcho = true;
// Determine effective stream URL and default markup type
nsAutoString streamURL;
OutputMarkupType streamMarkupType;
PRBool streamIsSecure = (opcodes & LTERM_COOKIESTR_CODE);
if (streamIsSecure) {
// Secure stream, i.e., prefixed with cookie; fragments allowed
streamURL = "chrome://xmlterm/content/xmltblank.html";
if (opcodes & LTERM_JSSTREAM_CODE) {
// Javascript stream
streamMarkupType = JS_FRAGMENT;
} else {
// HTML/XML stream
streamMarkupType = HTML_FRAGMENT;
}
} else {
// Insecure stream; do not display
streamURL = "http://in.sec.ure";
streamMarkupType = INSECURE_FRAGMENT;
}
if (!(opcodes & LTERM_JSSTREAM_CODE) &&
(opcodes & LTERM_DOCSTREAM_CODE)) {
// Stream contains complete document (not Javascript)
if (opcodes & LTERM_XMLSTREAM_CODE) {
streamMarkupType = XML_DOCUMENT;
} else {
streamMarkupType = HTML_DOCUMENT;
}
}
// Initialize stream output
result = InitStream(streamURL, streamMarkupType, streamIsSecure);
if (NS_FAILED(result))
break;
}
// Process stream output
bufStyle = "";
result = ProcessOutput(bufString, bufStyle, false, true);
if (NS_FAILED(result))
break;
if (newline) {
if (!mEntryHasOutput) {
// Start of command output
mEntryHasOutput = true;
}
if (errorFlag) {
mOutputMarkupType = INCOMPLETE_FRAGMENT;
}
// Break stream output display
result = BreakOutput(PR_TRUE);
if (NS_FAILED(result))
break;
mOutputType = LINE_OUTPUT;
flushOutput = true;
}
} else if (screenData) {
// Process screen data
if (opcodes & LTERM_CLEAR_CODE) {
// Clear screen
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Clear screen, opvals=%d, buf_row=%d\n",
opvals, buf_row));
nsCOMPtr<nsIDOMNode> resultNode;
result = mSessionNode->RemoveChild(mScreenNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
mScreenNode = nsnull;
// Create new screen element
result = NewScreen();
if (NS_FAILED(result))
break;
} else if (opcodes & LTERM_INSERT_CODE) {
// Insert rows
PRInt32 row;
nsCOMPtr<nsIDOMNode> rowNode, resultNode;
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Insert rows, opvals=%d, buf_row=%d\n",
opvals, buf_row));
if (opvals > 0) {
// Delete row elements below
for (row=0; row < opvals; row++) {
result = GetRow(mBotScrollRow+opvals-1, getter_AddRefs(rowNode));
if (NS_FAILED(result) || !rowNode)
break;
result = mScreenNode->RemoveChild(rowNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
}
if (NS_FAILED(result))
break;
// Insert individual row elements above
if (buf_row < opvals) {
rowNode = nsnull;
} else {
result = GetRow(buf_row, getter_AddRefs(rowNode));
if (NS_FAILED(result))
break;
}
for (row=0; row < opvals; row++)
NewRow(rowNode, getter_AddRefs(resultNode));
}
} else if (opcodes & LTERM_DELETE_CODE) {
// Delete rows
PRInt32 row;
nsCOMPtr<nsIDOMNode> rowNode, resultNode;
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Delete rows, opvals=%d, buf_row=%d\n",
opvals, buf_row));
if (opvals > 0) {
// Delete row elements below
for (row=0; row < opvals; row++) {
result = GetRow(buf_row, getter_AddRefs(rowNode));
if (NS_FAILED(result) || !rowNode)
break;
result = mScreenNode->RemoveChild(rowNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
}
if (NS_FAILED(result))
break;
// Insert individual row elements above
if (mBotScrollRow == 0) {
rowNode = nsnull;
} else {
result = GetRow(mBotScrollRow+opvals-1, getter_AddRefs(rowNode));
if (NS_FAILED(result))
break;
}
for (row=0; row < opvals; row++)
NewRow(rowNode, getter_AddRefs(resultNode));
}
} else if (opcodes & LTERM_SCROLL_CODE) {
// Set scrolling region
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Set scrolling region, opvals=%d, buf_row=%d\n",
opvals, buf_row));
mTopScrollRow = opvals;
mBotScrollRow = buf_row;
} else if (opcodes & LTERM_OUTPUT_CODE) {
// Display row
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Display buf_row=%d\n",
buf_row));
result = DisplayRow(bufString, bufStyle, buf_row);
if (NS_FAILED(result))
break;
}
// Determine cursor position and position cursor
PRInt32 cursorRow = 0;
PRInt32 cursorCol = 0;
result = lineTermAux->GetCursorRow(&cursorRow);
result = lineTermAux->GetCursorColumn(&cursorCol);
XMLT_LOG(mozXMLTermSession::ReadAll,62, ("cursorRow=%d, cursorCol=%d\n",
cursorRow, cursorCol));
result = PositionScreenCursor(cursorRow, cursorCol);
flushOutput = false;
} else {
// Process line data
PRBool promptLine, inputLine, metaCommand, completionRequested;
flushOutput = true;
inputLine = (opcodes & LTERM_INPUT_CODE);
promptLine = (opcodes & LTERM_PROMPT_CODE);
metaCommand = (opcodes & LTERM_META_CODE);
completionRequested = (opcodes & LTERM_COMPLETION_CODE);
nsAutoString promptStr ("");
PRInt32 promptLength = 0;
if (promptLine) {
// Count prompt characters
const PRUnichar *styleVals = bufStyle.GetUnicode();
const PRInt32 bufLength = bufStyle.Length();
for (promptLength=0; promptLength<bufLength; promptLength++) {
if (styleVals[promptLength] != LTERM_PROMPT_STYLE)
break;
}
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("bufLength=%d, promptLength=%d, styleVals[0]=0x%x\n",
bufLength, promptLength, styleVals[0]));
PR_ASSERT(promptLength > 0);
// Extract prompt string
bufString.Left(promptStr, promptLength);
if ( (promptLength < bufLength) &&
!inputLine &&
!promptStr.Equals(mShellPrompt) ) {
// Ignore the mismatched prompt in the output line
int j;
promptLine = 0;
for (j=0; j<promptLength; j++)
bufStyle.SetCharAt((UNICHAR) LTERM_STDOUT_STYLE, j);
} else {
// Remove prompt chars/style from buffer strings
bufString.Cut(0, promptLength);
bufStyle.Cut(0, promptLength);
// Save prompt string
mShellPrompt = promptStr;
}
}
if (!metaCommand && inputLine) {
if (metaNextCommand) {
// Echo of transmitted meta command
metaNextCommand = false;
} else {
// No meta command; enable input echo
mMetaCommandType = NO_META_COMMAND;
if (mRestoreInputEcho) {
lineTermAux->SetEchoFlag(true);
mRestoreInputEcho = false;
}
}
}
if (metaCommand && !completionRequested) {
// Identify meta command type
// Eliminate leading spaces/TABs
nsAutoString metaLine = bufString;
metaLine.Trim(kWhitespace, PR_TRUE, PR_FALSE);
int delimOffset = metaLine.FindChar((PRUnichar) ':');
PR_ASSERT(delimOffset >= 0);
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("delimOffset=%d\n", delimOffset));
if (delimOffset == 0) {
// Default protocol
mMetaCommandType = DEFAULT_META_COMMAND;
} else {
// Identify meta command type
mMetaCommandType = NO_META_COMMAND;
nsAutoString temString;
metaLine.Left(temString, delimOffset);
PRInt32 j;
for (j=NO_META_COMMAND+1; j<META_COMMAND_TYPES; j++) {
if (temString.Equals(metaCommandNames[j])) {
mMetaCommandType = (MetaCommandType) j;
break;
}
}
}
XMLT_LOG(mozXMLTermSession::ReadAll,62,("mMetaCommandType=%d\n",
mMetaCommandType));
// Extract command arguments
int argChars = metaLine.Length() - delimOffset - 1;
nsAutoString commandArgs;
metaLine.Right(commandArgs, argChars);
// Eliminate leading spaces/TABs
commandArgs.Trim(kWhitespace, PR_TRUE, PR_FALSE);
// Display meta command
if (mEntryHasOutput) {
// Break previous output display
result = BreakOutput(PR_FALSE);
// Create new entry block
result = NewEntry(promptStr);
if (NS_FAILED(result))
break;
}
// Display input and position cursor
PRInt32 cursorCol = 0;
result = lineTermAux->GetCursorColumn(&cursorCol);
// Remove prompt offset
cursorCol -= promptLength;
if (cursorCol < 0) cursorCol = 0;
XMLT_LOG(mozXMLTermSession::ReadAll,62,("cursorCol=%d\n", cursorCol));
result = DisplayInput(bufString, bufStyle, cursorCol);
if (NS_FAILED(result))
break;
if (newline && mXMLTerminal) {
// Complete meta command; XMLterm instantiated
nsAutoString metaCommandOutput = "";
switch (mMetaCommandType) {
case DEFAULT_META_COMMAND:
{
// Construct Javascript command to handle default meta comand
nsAutoString JSCommand = "MetaDefault(\"";
JSCommand.Append(commandArgs);
JSCommand.Append("\");");
// Execute JavaScript command
result = mozXMLTermUtils::ExecuteScript(mDOMDocument,
JSCommand,
metaCommandOutput);
if (NS_FAILED(result))
metaCommandOutput = "Error in displaying URL\n";
nsCAutoString cstrout = metaCommandOutput;
printf("mozXMLTermSession::ReadAll, DEFAULT_META output=%s\n",
cstrout.GetBuffer());
}
break;
case HTTP_META_COMMAND:
{
// Display URL using IFRAME
nsAutoString url = "http:";
url.Append(commandArgs);
nsAutoString width = "100%";
nsAutoString height = "100";
result = NewIFrame(mOutputBlockNode, mCurrentEntryNumber,
2, url, width, height);
if (NS_FAILED(result))
metaCommandOutput = "Error in displaying URL\n";
}
break;
case JS_META_COMMAND:
{
// Execute JavaScript command
result = mozXMLTermUtils::ExecuteScript(mDOMDocument,
commandArgs,
metaCommandOutput);
if (NS_FAILED(result))
metaCommandOutput = "Error in executing JavaScript command\n";
nsCAutoString cstrout = metaCommandOutput;
printf("mozXMLTermSession::ReadAll, JS output=%s\n",
cstrout.GetBuffer());
}
break;
case TREE_META_COMMAND:
XMLT_WARNING("\nTraverseDOMTree: use arrow keys; A for attributes; H for HTML; Q to quit\n");
break;
case LS_META_COMMAND:
{
// Disable input echo and transmit command
lineTermAux->SetEchoFlag(false);
nsAutoString lsCommand ("");
if (commandArgs.Length() > 0) {
lsCommand += "cd ";
lsCommand += commandArgs;
lsCommand += ";";
}
lsCommand += "ls -dF `pwd`/*\n";
//mXMLTerminal->SendText(lsCommand);
/* Set flag to recognize transmitted command */
metaNextCommand = true;
mRestoreInputEcho = true;
}
break;
default:
break;
}
if ((mMetaCommandType == DEFAULT_META_COMMAND) ||
(mMetaCommandType == JS_META_COMMAND)) {
// Display metacommand output
mEntryHasOutput = true;
XMLT_LOG(mozXMLTermSession::ReadAll,62,("metaCommandOutput\n"));
// Ignore the string "false", if that's the only output
if (metaCommandOutput.Equals("false"))
metaCommandOutput = "";
// Check metacommand output for markup (secure)
result = AutoDetectMarkup(metaCommandOutput, true, true);
if (NS_FAILED(result))
break;
nsAutoString nullStyle ("");
result = ProcessOutput(metaCommandOutput, nullStyle, true,
mOutputMarkupType != PLAIN_TEXT);
if (NS_FAILED(result))
break;
// Break metacommand output display
result = BreakOutput(PR_FALSE);
}
// Reset newline flag
newline = false;
}
// Clear the meta command from the string nuffer
bufString = "";
bufStyle = "";
}
if (promptLine) {
// Prompt line
if (mEntryHasOutput) {
// Break previous output display
result = BreakOutput(PR_FALSE);
// Create new entry block
result = NewEntry(promptStr);
if (NS_FAILED(result))
break;
if (mCurrentEntryNumber == mStartEntryNumber) {
// First entry; resize terminal
result = Resize(lineTermAux);
if (NS_FAILED(result))
break;
}
}
// Display input and position cursor
PRInt32 cursorCol = 0;
result = lineTermAux->GetCursorColumn(&cursorCol);
// Remove prompt offset
cursorCol -= promptLength;
if (cursorCol < 0) cursorCol = 0;
XMLT_LOG(mozXMLTermSession::ReadAll,62,("cursorCol=%d\n", cursorCol));
result = DisplayInput(bufString, bufStyle, cursorCol);
if (NS_FAILED(result))
break;
if (newline) {
// Start of command output
// (this is needed to properly handle commands with no output!)
mEntryHasOutput = true;
mFirstOutputLine = true;
}
} else {
// Not prompt line
if (!mEntryHasOutput) {
// Start of command output
mEntryHasOutput = true;
mFirstOutputLine = true;
}
if (newline) {
// Complete line; check for markup (insecure)
result = AutoDetectMarkup(bufString, mFirstOutputLine, false);
if (NS_FAILED(result))
break;
// Not first output line anymore
mFirstOutputLine = false;
}
if (mOutputMarkupType == PLAIN_TEXT) {
// Display plain text output
result = ProcessOutput(bufString, bufStyle, newline, false);
if (NS_FAILED(result))
break;
} else if (newline) {
// Process autodetected stream output (complete lines only)
bufStyle = "";
result = ProcessOutput(bufString, bufStyle, true, true);
if (NS_FAILED(result))
break;
}
}
}
}
if (NS_FAILED(result)) {
// Error processing; close LineTerm
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Aborting on error, result=0x%x\n", result));
Abort(lineTermAux, abortCode);
return result;
}
if (flushOutput) {
// Flush output, splitting off incomplete line
FlushOutput(SPLIT_INCOMPLETE_FLUSH);
if (mEntryHasOutput)
PositionOutputCursor(lineTermAux);
mPresShell->ScrollSelectionIntoView(SELECTION_NORMAL,
SELECTION_FOCUS_REGION);
}
// Show caret
mXMLTerminal->ShowCaret();
// Scroll frame (ignore result)
ScrollToBottomLeft();
return NS_OK;
}
/** Aborts session by closing LineTerm and displays an error message
* @param lineTermAux LineTermAux object to be closed
* @param abortCode abort code string to dbe displayed
*/
NS_IMETHODIMP mozXMLTermSession::Abort(mozILineTermAux* lineTermAux,
nsString& abortCode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::Abort,70,
("Aborting session; closing LineTerm\n"));
// Close LineTerm
lineTermAux->CloseAux();
// Display error message using DIV node
nsCOMPtr<nsIDOMNode> divNode, textNode;
nsAutoString tagName = "div";
nsAutoString elementName = "errmsg";
result = NewElementWithText(tagName, elementName, -1,
mSessionNode, divNode, textNode);
if (NS_SUCCEEDED(result) && divNode && textNode) {
nsAutoString errMsg = "Error in XMLterm (code ";
errMsg.Append(abortCode);
errMsg.Append("); session closed.");
SetDOMText(textNode, errMsg);
// Collapse selection and position cursor
nsCOMPtr<nsIDOMSelection> selection;
result = mPresShell->GetSelection(SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_SUCCEEDED(result) && selection) {
selection->Collapse(textNode, errMsg.Length());
if (NS_SUCCEEDED(result)) {
mPresShell->ScrollSelectionIntoView(SELECTION_NORMAL,
SELECTION_FOCUS_REGION);
}
}
}
return NS_OK;
}
/** Displays ("echoes") input text string with style and positions cursor
* @param aString string to be displayed
* @param aStyle style values for string (see lineterm.h)
* @param cursorCol cursor column
*/
NS_IMETHODIMP mozXMLTermSession::DisplayInput(const nsString& aString,
const nsString& aStyle,
PRInt32 cursorCol)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::DisplayInput,70,("cursorCol=%d\n", cursorCol));
result = SetDOMText(mInputTextNode, aString);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
char* temCString = aString.ToNewCString();
XMLT_LOG(mozXMLTermSession::DisplayInput,72,
("aString=%s\n", temCString));
nsCRT::free(temCString);
// Collapse selection and position cursor
nsCOMPtr<nsIDOMSelection> selection;
result = mPresShell->GetSelection(SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_FAILED(result) || !selection)
return NS_ERROR_FAILURE;
#ifdef NO_WORKAROUND
// Collapse selection to new cursor location
result = selection->Collapse(mInputTextNode, cursorCol);
#else
// WORKAROUND for cursor positioning at end of prompt
// Without this workaround, the cursor is positioned too close to the prompt
// (i.e., too far to the left, ignoring the prompt whitespace)
if ((cursorCol > 0) || (mPromptHTML.Length() > 0)) {
// Collapse selection to new cursor location
result = selection->Collapse(mInputTextNode, cursorCol);
} else {
// Get the last bit of text in the prompt
nsCOMPtr<nsIDOMNode> promptTextNode;
result = mPromptSpanNode->GetLastChild(getter_AddRefs(promptTextNode));
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDOMText> domText (do_QueryInterface(promptTextNode));
if (domText) {
PRUint32 textLength;
result = domText->GetLength(&textLength);
if (NS_SUCCEEDED(result)) {
XMLT_LOG(mozXMLTermSession::DisplayInput,72,
("textLength=%d\n", textLength));
result = selection->Collapse(promptTextNode, textLength);
}
}
}
}
#endif // !NO_WORKAROUND
NS_ASSERTION((NS_SUCCEEDED(result)),
"selection could not be collapsed after insert.");
return NS_OK;
}
/** Autodetects markup in current output line
* @param aString string to be displayed
* @param firstOutputLine true if this is the first output line
* @param secure true if output data is secure
* (usually true for metacommand output only)
*/
NS_IMETHODIMP mozXMLTermSession::AutoDetectMarkup(const nsString& aString,
PRBool firstOutputLine,
PRBool secure)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::AutoDetectMarkup,70,("firstOutputLine=0x%x\n",
firstOutputLine));
// If autodetect disabled or not plain text, do nothing
if ((mAutoDetect == NO_MARKUP) ||
((mAutoDetect == FIRST_LINE) && !firstOutputLine) ||
(mOutputMarkupType != PLAIN_TEXT))
return NS_OK;
OutputMarkupType newMarkupType = PLAIN_TEXT;
// Copy string and trim leading spaces/backspaces/tabs
nsAutoString str = aString;
str.Trim(kWhitespace, PR_TRUE, PR_FALSE);
if (str.First() == U_LESSTHAN) {
// Markup tag detected
str.CompressWhitespace();
str.Append(" ");
if ( (str.Find("<!DOCTYPE HTML",PR_TRUE) == 0) ||
(str.Find("<BASE ",PR_TRUE) == 0) ||
(str.Find("<HTML>",PR_TRUE) == 0) ) {
// HTML document
newMarkupType = HTML_DOCUMENT;
} else if (str.Find("<?xml ",PR_FALSE) == 0) {
// XML document
newMarkupType = XML_DOCUMENT;
} else {
// HTML fragment
if (secure) {
// Secure HTML fragment
newMarkupType = HTML_FRAGMENT;
} else {
// Insecure; treat as text fragment for security reasons
newMarkupType = TEXT_FRAGMENT;
}
}
} else if (firstOutputLine && str.Find("Content-Type",PR_TRUE) == 0) {
// Possible MIME content type header
str.StripWhitespace();
if (str.Find("Content-Type:text/html",PR_TRUE) == 0) {
// MIME content type header for HTML document
newMarkupType = HTML_DOCUMENT;
}
}
if (newMarkupType != PLAIN_TEXT) {
// Markup found; initialize (insecure) stream
nsAutoString streamURL = "http://in.sec.ure";
result = InitStream(streamURL, newMarkupType, false);
if (NS_FAILED(result))
return result;
} else {
// No markup found; assume rest of output is plain text
mOutputMarkupType = PLAIN_TEXT;
}
XMLT_LOG(mozXMLTermSession::AutoDetectMarkup,71,("mOutputMarkupType=%d\n",
mOutputMarkupType));
return NS_OK;
}
/** Initializes display of stream output with specified markup type
* @param streamURL effective URL of stream output
* @param streamMarkupType stream markup type
* @param streamIsSecure true if stream is secure
*/
NS_IMETHODIMP mozXMLTermSession::InitStream(const nsString& streamURL,
OutputMarkupType streamMarkupType,
PRBool streamIsSecure)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::InitStream,70,("streamMarkupType=%d\n",
streamMarkupType));
// Break previous output display
result = BreakOutput(PR_FALSE);
if (NS_FAILED(result))
return result;
if ((streamMarkupType == TEXT_FRAGMENT) ||
(streamMarkupType == JS_FRAGMENT) ||
(streamMarkupType == HTML_FRAGMENT) ||
(streamMarkupType == INSECURE_FRAGMENT) ||
(streamMarkupType == OVERFLOW_FRAGMENT) ||
(streamMarkupType == INCOMPLETE_FRAGMENT)) {
// Initialize fragment buffer
mFragmentBuffer = "";
} else {
// Create IFRAME to display stream document
nsAutoString src = "about:blank";
nsAutoString width = "100%";
nsAutoString height = "10";
PRInt32 frameBorder = 0;
if (!streamIsSecure)
frameBorder = 2;
result = NewIFrame(mOutputBlockNode, mCurrentEntryNumber,
frameBorder, src, width, height);
if (NS_FAILED(result))
return result;
result = NS_NewXMLTermStream(getter_AddRefs(mXMLTermStream));
if (NS_FAILED(result))
return result;
nsCOMPtr<nsIDocShell> docShell;
result = mXMLTerminal->GetDocShell(getter_AddRefs(docShell));
if (NS_FAILED(result) || !docShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMWindow> outerDOMWindow;
result = mozXMLTermUtils::ConvertDocShellToDOMWindow(docShell,
getter_AddRefs(outerDOMWindow));
if (NS_FAILED(result) || !outerDOMWindow) {
fprintf(stderr,
"mozXMLTermSession::InitStream: Failed to convert webshell\n");
return NS_ERROR_FAILURE;
}
// Initialize markup handling
nsCAutoString iframeName = "iframe";
#if 0
iframeName.Append("t");
#else
iframeName.Append(mCurrentEntryNumber,10);
#endif
nsCAutoString contentType;
switch (streamMarkupType) {
case HTML_DOCUMENT:
contentType = "text/html";
break;
case XML_DOCUMENT:
contentType = "text/xml";
break;
default:
PR_ASSERT(0);
break;
}
nsCAutoString url ( streamURL );
result = mXMLTermStream->Open(outerDOMWindow, iframeName.GetBuffer(),
url.GetBuffer(),
contentType.GetBuffer(), 800);
if (NS_FAILED(result)) {
fprintf(stderr,
"mozXMLTermSession::InitStream: Failed to open stream\n");
return result;
}
}
mOutputMarkupType = streamMarkupType;
return NS_OK;
}
/** Breaks output display by flushing and deleting incomplete lines */
NS_IMETHODIMP mozXMLTermSession::BreakOutput(PRBool positionCursorBelow)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::BreakOutput,70,
("positionCursorBelow=%x, mOutputMarkupType=%d\n",
positionCursorBelow, mOutputMarkupType));
if (!mEntryHasOutput)
return NS_OK;
switch (mOutputMarkupType) {
case INSECURE_FRAGMENT:
case OVERFLOW_FRAGMENT:
case INCOMPLETE_FRAGMENT:
case TEXT_FRAGMENT:
{
// Display text fragment using new SPAN node
nsCOMPtr<nsIDOMNode> spanNode, textNode;
nsAutoString tagName = "span";
nsAutoString elementName = "stream";
result = NewElementWithText(tagName, elementName, -1,
mOutputBlockNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode)
return NS_ERROR_FAILURE;
// Append node
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputBlockNode->AppendChild(spanNode,
getter_AddRefs(resultNode));
// Handle stream output error messages
switch (mOutputMarkupType) {
case INSECURE_FRAGMENT:
mFragmentBuffer = "XMLTerm: *Error* Insecure stream data; is LTERM_COOKIE set?";
break;
case INCOMPLETE_FRAGMENT:
mFragmentBuffer = "XMLTerm: *Error* Incomplete stream data";
break;
default:
break;
}
// Display text
result = SetDOMText(textNode, mFragmentBuffer);
if (NS_FAILED(result))
return result;
mFragmentBuffer = "";
break;
}
case JS_FRAGMENT:
{
// Execute JS fragment
nsAutoString jsOutput = "";
result = mozXMLTermUtils::ExecuteScript(mDOMDocument,
mFragmentBuffer,
jsOutput);
if (NS_FAILED(result))
jsOutput = "Error in JavaScript execution\n";
mFragmentBuffer = "";
if (jsOutput.Length() > 0) {
// Display JS output as HTML fragment
result = InsertFragment(jsOutput, mOutputBlockNode,
mCurrentEntryNumber);
if (NS_FAILED(result))
return result;
}
}
break;
case HTML_FRAGMENT:
// Display HTML fragment
result = InsertFragment(mFragmentBuffer, mOutputBlockNode,
mCurrentEntryNumber);
if (NS_FAILED(result))
return result;
mFragmentBuffer = "";
break;
case HTML_DOCUMENT:
case XML_DOCUMENT:
// Close HTML/XML document
result = mXMLTermStream->Close();
if (NS_FAILED(result)) {
fprintf(stderr, "mozXMLTermSession::BreakOutput: Failed to close stream\n");
return result;
}
mXMLTermStream = nsnull;
break;
default:
// Flush plain text output, clearing any incomplete input line
result = FlushOutput(CLEAR_INCOMPLETE_FLUSH);
if (NS_FAILED(result))
return result;
mPreTextBufferLines = 0;
mPreTextBuffered = "";
mPreTextDisplayed = "";
mOutputDisplayNode = nsnull;
mOutputDisplayType = NO_NODE;
mOutputTextNode = nsnull;
break;
}
// Revert to plain text type
mOutputMarkupType = PLAIN_TEXT;
if (positionCursorBelow) {
PositionOutputCursor(nsnull);
}
return NS_OK;
}
/** Processes output string with specified style
* @param aString string to be processed
* @param aStyle style values for string (see lineterm.h)
* (if it is a null string, STDOUT style is assumed)
* @param newline true if this is a complete line of output
* @param streamOutput true if string represents stream output
*/
NS_IMETHODIMP mozXMLTermSession::ProcessOutput(const nsString& aString,
const nsString& aStyle,
PRBool newline,
PRBool streamOutput)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::ProcessOutput,70,
("newline=%d, streamOutput=%d\n", newline, streamOutput));
if ((mMetaCommandType == LS_META_COMMAND) && newline) {
// Display hypertext directory listing
result = AppendLineLS(aString, aStyle);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
return NS_OK;
} else {
// Not LS meta command
switch (mOutputMarkupType) {
case INSECURE_FRAGMENT:
case OVERFLOW_FRAGMENT:
case INCOMPLETE_FRAGMENT:
// Do nothing
break;
case TEXT_FRAGMENT:
case JS_FRAGMENT:
case HTML_FRAGMENT:
// Append complete lines to fragment buffer
if (newline || streamOutput) {
PRInt32 strLen = mFragmentBuffer.Length()+aString.Length();
if (strLen < 100000) {
mFragmentBuffer += aString;
if (newline)
mFragmentBuffer += '\n';
} else {
mOutputMarkupType = OVERFLOW_FRAGMENT;
mFragmentBuffer = "XMLTerm: *Error* Stream data overflow (";
mFragmentBuffer.Append(strLen,10);
mFragmentBuffer.Append(" chars)");
break;
}
}
break;
case HTML_DOCUMENT:
case XML_DOCUMENT:
// Write complete lines to document stream
if (newline || streamOutput) {
nsAutoString str = aString;
if (newline)
str.Append("\n");
result = mXMLTermStream->Write(str.GetUnicode());
if (NS_FAILED(result)) {
fprintf(stderr, "mozXMLTermSession::ProcessOutput: Failed to write to stream\n");
return result;
}
}
break;
default:
// Display plain text output, complete or incomplete lines
PR_ASSERT(!streamOutput);
result = AppendOutput(aString, aStyle, newline);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
break;
}
return NS_OK;
}
}
/** Ensures the total number of output lines stays within a limit
* by deleting the oldest output line.
* @param deleteAllOld if true, delete all previous display nodes
* (excluding the current one)
*/
NS_IMETHODIMP mozXMLTermSession::LimitOutputLines(PRBool deleteAllOld)
{
nsresult result;
nsAutoString attValue;
XMLT_LOG(mozXMLTermSession::LimitOutputLines,70,
("deleteAllOld=%d, mEntryOutputLines=%d\n",
deleteAllOld, mEntryOutputLines));
nsCOMPtr<nsIDOMNode> firstChild;
result = mOutputBlockNode->GetFirstChild(getter_AddRefs(firstChild));
if (NS_FAILED(result) || !firstChild)
return NS_ERROR_FAILURE;
attValue = "";
result = mozXMLTermUtils::GetNodeAttribute(firstChild, "class", attValue);
if (NS_FAILED(result))
return result;
if (!attValue.Equals(sessionElementNames[WARNING_ELEMENT])) {
// Create warning message element
nsCOMPtr<nsIDOMNode> divNode, textNode;
nsAutoString tagName = "div";
nsAutoString elementName = sessionElementNames[WARNING_ELEMENT];
result = NewElementWithText(tagName, elementName, -1,
mOutputBlockNode, divNode, textNode,
firstChild);
if (NS_FAILED(result) || !divNode || !textNode)
return NS_ERROR_FAILURE;
firstChild = divNode;
nsAutoString warningMsg ="XMLTerm: *WARNING* Command output truncated to ";
warningMsg.Append(300,10);
warningMsg.Append(" lines");
result = SetDOMText(textNode, warningMsg);
}
PR_ASSERT(mOutputDisplayNode != firstChild);
nsCOMPtr<nsIDOMNode> nextChild;
PRInt32 decrementedLineCount = 0;
for (;;) {
result = firstChild->GetNextSibling(getter_AddRefs(nextChild));
PR_ASSERT(NS_SUCCEEDED(result) && nextChild);
// Do not modify current display node
if (nextChild.get() == mOutputDisplayNode.get())
break;
PRInt32 deleteNode = 0;
if (deleteAllOld) {
deleteNode = 1;
} else {
attValue = "";
result = mozXMLTermUtils::GetNodeAttribute(nextChild, "class", attValue);
if (NS_FAILED(result)|| (attValue.Length() == 0)) {
deleteNode = 1;
} else {
if (attValue.Equals(sessionElementNames[MIXED_ELEMENT])) {
// Delete single line containing mixed style output
deleteNode = 1;
decrementedLineCount = 1;
XMLT_LOG(mozXMLTermSession::LimitOutputLines,79,
("deleted mixed line\n"));
} else if ( (attValue.Equals(sessionElementNames[STDIN_ELEMENT])) ||
(attValue.Equals(sessionElementNames[STDOUT_ELEMENT])) ||
(attValue.Equals(sessionElementNames[STDERR_ELEMENT]))) {
// Delete first line from STDIN/STDOUT/STDERR PRE output
nsCOMPtr<nsIDOMNode> textNode;
result = nextChild->GetFirstChild(getter_AddRefs(textNode));
PR_ASSERT( NS_SUCCEEDED(result) && textNode);
nsCOMPtr<nsIDOMText> domText (do_QueryInterface(textNode));
PR_ASSERT(domText);
// Delete first line from text
nsAutoString text;
domText->GetData(text);
PRInt32 offset = text.FindChar((PRUnichar) U_LINEFEED);
if (offset < 0) {
deleteNode = 1;
} else {
text.Cut(0,offset+1);
domText->SetData(text);
}
decrementedLineCount = 1;
XMLT_LOG(mozXMLTermSession::LimitOutputLines,79,
("deleted PRE line\n"));
} else {
// Unknown type of DOM element, delete
deleteNode = 1;
}
}
}
if (deleteNode) {
// Delete next child node
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputBlockNode->RemoveChild(nextChild,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
return result;
}
if (decrementedLineCount || !deleteNode)
break;
}
if (deleteAllOld) {
mEntryOutputLines = 0;
return NS_OK;
} else if (decrementedLineCount) {
mEntryOutputLines--;
return NS_OK;
} else {
return NS_ERROR_FAILURE;
}
}
/** Appends text string to output buffer
* (appended text may need to be flushed for it to be actually displayed)
* @param aString string to be processed (may be null string, for dummy line)
* @param aStyle style values for string (see lineterm.h)
* (may be a single Unichar, for uniform style)
* (if it is a null string, STDOUT style is assumed)
* @param newline true if this is a complete line of output
*/
NS_IMETHODIMP mozXMLTermSession::AppendOutput(const nsString& aString,
const nsString& aStyle,
PRBool newline)
{
nsresult result;
const PRInt32 strLength = aString.Length();
const PRInt32 styleLength = aStyle.Length();
const PRUnichar *strStyle = aStyle.GetUnicode();
XMLT_LOG(mozXMLTermSession::AppendOutput,70,("strLength=%d\n", strLength));
// Check if line has uniform style
PRUnichar uniformStyle = LTERM_STDOUT_STYLE;
PRInt32 styleChanges = 0;
if (styleLength > 0) {
PRInt32 j;
uniformStyle = strStyle[0];
PR_ASSERT((styleLength == 1) || (styleLength == strLength));
for (j=1; j<styleLength; j++) {
if (strStyle[j] != strStyle[j-1]) {
uniformStyle = 0;
styleChanges++;
}
}
}
XMLT_LOG(mozXMLTermSession::AppendOutput,72,
("mOutputDisplayType=%d, uniformStyle=0x%x, newline=%d\n",
mOutputDisplayType, uniformStyle, newline));
char* temCString = aString.ToNewCString();
XMLT_LOG(mozXMLTermSession::AppendOutput,72,
("aString=%s\n", temCString));
nsCRT::free(temCString);
#ifdef NO_WORKAROUND
// Do not use PRE text
if (0) {
#else
if (uniformStyle != 0) {
#endif
// Uniform style data; display as preformatted block
OutputDisplayType preDisplayType;
nsAutoString elementName = "";
if (uniformStyle == LTERM_STDIN_STYLE) {
preDisplayType = PRE_STDIN_NODE;
elementName = sessionElementNames[STDIN_ELEMENT];
XMLT_LOG(mozXMLTermSession::AppendOutput,72, ("PRE_STDIN_NODE\n"));
} else if (uniformStyle == LTERM_STDERR_STYLE) {
preDisplayType = PRE_STDERR_NODE;
elementName = sessionElementNames[STDERR_ELEMENT];
XMLT_LOG(mozXMLTermSession::AppendOutput,72, ("PRE_STDERR_NODE\n"));
} else {
preDisplayType = PRE_STDOUT_NODE;
elementName = sessionElementNames[STDOUT_ELEMENT];
XMLT_LOG(mozXMLTermSession::AppendOutput,72, ("PRE_STDOUT_NODE\n"));
}
if (mOutputDisplayType != preDisplayType) {
// Flush incomplete line
result = FlushOutput(CLEAR_INCOMPLETE_FLUSH);
// Create PRE display node
nsCOMPtr<nsIDOMNode> preNode, textNode;
nsAutoString tagName = "pre";
result = NewElementWithText(tagName, elementName, -1,
mOutputBlockNode, preNode, textNode);
if (NS_FAILED(result) || !preNode || !textNode)
return NS_ERROR_FAILURE;
XMLT_LOG(mozXMLTermSession::AppendOutput,72,
("Creating new PRE node\n"));
// Append node
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputBlockNode->AppendChild(preNode,
getter_AddRefs(resultNode));
mOutputDisplayType = preDisplayType;
mOutputDisplayNode = preNode;
mOutputTextNode = textNode;
mOutputTextOffset = 0;
// Display incomplete line
result = SetDOMText(mOutputTextNode, aString);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// Initialize PRE text string buffers
mPreTextDisplayed = aString;
mPreTextBuffered = "";
mPreTextBufferLines = 0;
}
// Save incomplete line
mPreTextIncomplete = aString;
if (newline) {
// Complete line; append to buffer
if (mPreTextBufferLines > 0) {
mPreTextBuffered += '\n';
}
mPreTextBufferLines++;
mPreTextBuffered += mPreTextIncomplete;
mPreTextIncomplete = "";
if (mPreTextBufferLines > 300) {
// Delete all earlier PRE/mixed blocks and first line of current block
result = LimitOutputLines(true);
if (NS_FAILED(result))
return result;
// Delete first line from PRE text buffer
PRInt32 offset = mPreTextBuffered.FindChar((PRUnichar) U_LINEFEED);
if (offset < 0) {
mPreTextBuffered = "";
} else {
mPreTextBuffered.Cut(0,offset+1);
}
mPreTextBufferLines--;
} else if (mEntryOutputLines+mPreTextBufferLines > 300) {
// Delete oldest PRE/mixed line so as to stay within the limit
result = LimitOutputLines(false);
if (NS_FAILED(result))
return result;
}
}
XMLT_LOG(mozXMLTermSession::AppendOutput,72,
("mPreTextDisplayed.Length()=%d, mPreTextBufferLines()=%d\n",
mPreTextDisplayed.Length(), mPreTextBufferLines));
} else {
// Create uniform style DIV display node
XMLT_LOG(mozXMLTermSession::AppendOutput,72,("DIV_MIXED_NODE\n"));
// Flush buffer, clearing incomplete line
result = FlushOutput(CLEAR_INCOMPLETE_FLUSH);
if (NS_FAILED(result))
return result;
// Create new DIV node
nsAutoString elementName = sessionElementNames[MIXED_ELEMENT];
nsCOMPtr<nsIDOMNode> divNode;
nsAutoString tagName = "div";
result = NewElement(tagName, elementName, -1,
mOutputBlockNode, divNode);
if (NS_FAILED(result) || !divNode)
return NS_ERROR_FAILURE;
// Append node
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputBlockNode->AppendChild(divNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
return result;
nsCOMPtr<nsIDOMNode> spanNode, textNode;
nsAutoString subString;
PRInt32 k;
PRInt32 passwordPrompt = 0;
PRUnichar currentStyle = LTERM_STDOUT_STYLE;
if (styleLength > 0)
currentStyle = strStyle[0];
mOutputTextOffset = 0;
tagName = "span";
PR_ASSERT(strLength > 0);
for (k=1; k<strLength+1; k++) {
if ((k == strLength) || ((k < styleLength) &&
(strStyle[k] != currentStyle)) ) {
// Change of style or end of string
switch (currentStyle) {
case LTERM_STDIN_STYLE:
elementName = sessionElementNames[STDIN_ELEMENT];
break;
case LTERM_STDERR_STYLE:
elementName = sessionElementNames[STDERR_ELEMENT];
break;
default:
elementName = sessionElementNames[STDOUT_ELEMENT];
break;
}
result = NewElementWithText(tagName, elementName, -1,
divNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode)
return NS_ERROR_FAILURE;
aString.Mid(subString, mOutputTextOffset, k-mOutputTextOffset);
result = SetDOMText(textNode, subString);
if (NS_FAILED(result))
return result;
if (k < styleLength) {
// Change style
PRInt32 strLen = subString.Length();
if ((styleChanges = 1) &&
(currentStyle == LTERM_STDOUT_STYLE) &&
(strStyle[k] == LTERM_STDIN_STYLE) &&
( ((strLen-10) == subString.RFind("password: ",PR_TRUE)) ||
((strLen-9) == subString.RFind("password:",PR_TRUE))) ) {
// Password prompt detected; break loop
passwordPrompt = 1;
break;
}
currentStyle = strStyle[k];
mOutputTextOffset = k;
}
}
}
mOutputDisplayType = DIV_MIXED_NODE;
mOutputDisplayNode = divNode;
mOutputTextNode = textNode;
if (newline) {
// Increment total output line count for entry
mEntryOutputLines++;
if (mEntryOutputLines > 300) {
// Delete oldest PRE/mixed line so as to stay within the limit
result = LimitOutputLines(false);
if (NS_FAILED(result))
return result;
}
if (passwordPrompt) {
result = mOutputBlockNode->RemoveChild(mOutputDisplayNode,
getter_AddRefs(resultNode));
}
mOutputDisplayType = NO_NODE;
mOutputDisplayNode = nsnull;
mOutputTextNode = nsnull;
}
}
return NS_OK;
}
/** Adds markup to LS output (TEMPORARY)
* @param aString string to be processed
* @param aStyle style values for string (see lineterm.h)
* (if it is a null string, STDOUT style is assumed)
*/
NS_IMETHODIMP mozXMLTermSession::AppendLineLS(const nsString& aString,
const nsString& aStyle)
{
nsresult result;
const PRInt32 strLength = aString.Length();
const PRInt32 styleLength = aStyle.Length();
const PRUnichar *strStyle = aStyle.GetUnicode();
// Check if line has uniform style
PRUnichar allStyles = LTERM_STDOUT_STYLE;
PRUnichar uniformStyle = LTERM_STDOUT_STYLE;
if (styleLength > 0) {
PRInt32 j;
allStyles = strStyle[0];
uniformStyle = strStyle[0];
for (j=1; j<strLength; j++) {
allStyles |= strStyle[j];
if (strStyle[j] != strStyle[0]) {
uniformStyle = 0;
}
}
}
XMLT_LOG(mozXMLTermSession::AppendLineLS,60,
("mOutputDisplayType=%d, uniformStyle=0x%x\n",
mOutputDisplayType, uniformStyle));
if (uniformStyle != LTERM_STDOUT_STYLE) {
return AppendOutput(aString, aStyle, PR_TRUE);
}
char* temCString = aString.ToNewCString();
XMLT_LOG(mozXMLTermSession::AppendLineLS,62,("aString=%s\n", temCString));
nsCRT::free(temCString);
// Add markup to directory listing
nsAutoString markupString = "";
PRInt32 lineLength = aString.Length();
PRInt32 wordBegin = 0;
while (wordBegin < lineLength) {
// Consume any leading spaces
while ( (wordBegin < lineLength) &&
((aString[wordBegin] == U_SPACE) ||
(aString[wordBegin] == U_TAB)) ) {
markupString += aString[wordBegin];
wordBegin++;
}
if (wordBegin >= lineLength) break;
// Locate end of word (non-space character)
PRInt32 wordEnd = aString.FindCharInSet(kWhitespace, wordBegin);
if (wordEnd < 0) {
wordEnd = lineLength-1;
} else {
wordEnd--;
}
PR_ASSERT(wordEnd >= wordBegin);
// Locate pure filename, with possible type suffix
PRInt32 nameBegin;
if (wordEnd > wordBegin) {
nameBegin = aString.RFindChar(U_SLASH, PR_FALSE, wordEnd-1);
if (nameBegin >= wordBegin) {
nameBegin++;
} else {
nameBegin = wordBegin;
}
} else {
nameBegin = wordBegin;
}
nsAutoString filename;
aString.Mid(filename, nameBegin, wordEnd-nameBegin+1);
FileType fileType = PLAIN_FILE;
PRUint32 dropSuffix = 0;
if (wordEnd > wordBegin) {
// Determine file type from suffix character
switch (aString[wordEnd]) {
case U_SLASH:
fileType = DIRECTORY_FILE;
break;
case U_STAR:
fileType = EXECUTABLE_FILE;
break;
default:
break;
}
// Discard any type suffix
if (fileType != PLAIN_FILE)
dropSuffix = 1;
}
// Extract full pathname (minus any type suffix)
nsAutoString pathname;
aString.Mid(pathname, wordBegin, wordEnd-wordBegin+1-dropSuffix);
// Append to markup string
markupString += "<span class=\"";
markupString += fileTypeNames[fileType];
markupString += "\"";
int j;
for (j=0; j<SESSION_EVENT_TYPES; j++) {
markupString += " on";
markupString += sessionEventNames[j];
markupString += "=\"return HandleEvent(event, '";
markupString += sessionEventNames[j];
markupString += "','";
markupString += fileTypeNames[fileType];
markupString += "',-#,'";
markupString += pathname;
markupString += "');\"";
}
markupString += ">";
markupString += filename;
markupString += "</span>";
// Search for new word
wordBegin = wordEnd+1;
}
if (mOutputDisplayType != PRE_STDOUT_NODE) {
// Create PRE block
nsAutoString nullString("");
result = AppendOutput(nullString, nullString, PR_FALSE);
}
PR_ASSERT(mOutputDisplayNode != nsnull);
PR_ASSERT(mOutputTextNode != nsnull);
result = InsertFragment(markupString, mOutputDisplayNode,
mCurrentEntryNumber, mOutputTextNode.get());
// Insert text node containing newline only
nsCOMPtr<nsIDOMText> newText;
nsAutoString newlineStr ("\n");
result = mDOMDocument->CreateTextNode(newlineStr, getter_AddRefs(newText));
if (NS_FAILED(result) || !newText)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> newTextNode = do_QueryInterface(newText);
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputDisplayNode->InsertBefore(newTextNode, mOutputTextNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
XMLT_LOG(mozXMLTermSession::AppendLineLS,61,("exiting\n"));
#if 0
mCurrentDebugNode = mOutputDisplayNode;
mMetaCommandType = TREE_META_COMMAND;
XMLT_LOG(mozXMLTermSession::AppendLineLS,0,("tree:\n"));
#endif /* 0 */
return NS_OK;
}
/** Inserts HTML fragment string as child of parentNode, before specified
* child node, or after the last child node
* @param aString HTML fragment string to be inserted
* @param parentNode parent node for HTML fragment
* @param entryNumber entry number (default value = -1)
* (if entryNumber >= 0, all '#' characters in
* id/onclick attribute values are substituted
* with entryNumber)
* @param beforeNode child node before which to insert fragment;
* if null, insert after last child node
* (default value is null)
* @param replace if true, replace beforeNode with inserted fragment
* (default value is false)
*/
NS_IMETHODIMP mozXMLTermSession::InsertFragment(const nsString& aString,
nsIDOMNode* parentNode,
PRInt32 entryNumber,
nsIDOMNode* beforeNode,
PRBool replace)
{
nsresult result;
char* temCString = aString.ToNewCString();
XMLT_LOG(mozXMLTermSession::InsertFragment,70,("aString=%s\n", temCString));
nsCRT::free(temCString);
// Get selection
nsCOMPtr<nsIDOMSelection> selection;
result = mPresShell->GetSelection(SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_FAILED(result) || !selection)
return NS_ERROR_FAILURE;
PRUint32 insertOffset = 0;
nsCOMPtr<nsIDOMNodeList> childNodes;
result = parentNode->GetChildNodes(getter_AddRefs(childNodes));
if (NS_SUCCEEDED(result) && childNodes) {
PRUint32 nChildren = 0;
childNodes->GetLength(&nChildren);
if(!beforeNode) {
// Append child
insertOffset = nChildren;
} else {
// Determine offset of before node
int j;
PRInt32 nNodes = nChildren;
for (j=0; j<nNodes; j++) {
nsCOMPtr<nsIDOMNode> childNode;
result = childNodes->Item(j, getter_AddRefs(childNode));
if ((NS_SUCCEEDED(result)) && childNode) {
if (childNode.get() == beforeNode) {
insertOffset = j;
break;
}
}
}
}
}
// Collapse selection to insertion point
result = selection->Collapse(parentNode, insertOffset);
if (NS_FAILED(result))
return result;
// Get the first range in the selection
nsCOMPtr<nsIDOMRange> firstRange;
result = selection->GetRangeAt(0, getter_AddRefs(firstRange));
if (NS_FAILED(result) || !firstRange)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNSRange> nsrange (do_QueryInterface(firstRange));
if (!nsrange)
return NS_ERROR_FAILURE;
XMLT_LOG(mozXMLTermSession::InsertFragment,62,("Creating Fragment\n"));
nsCOMPtr<nsIDOMDocumentFragment> docfrag;
result = nsrange->CreateContextualFragment(aString, getter_AddRefs(docfrag));
if (NS_FAILED(result) || !docfrag)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> docfragNode (do_QueryInterface(docfrag));
if (!docfragNode)
return NS_ERROR_FAILURE;
// Sanitize all nodes in document fragment (deep)
result = DeepSanitizeFragment(docfragNode, nsnull, entryNumber);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// If fragment was deleted during the sanitization process, simply return
if (!docfragNode)
return NS_OK;
// Insert child nodes of document fragment before PRE text node
nsCOMPtr<nsIDOMNode> childNode;
result = docfragNode->GetFirstChild(getter_AddRefs(childNode));
if (NS_FAILED(result) || !childNode)
return NS_ERROR_FAILURE;
while (childNode) {
// Get next sibling prior to insertion
nsCOMPtr<nsIDOMNode> nextChild;
result = childNode->GetNextSibling(getter_AddRefs(nextChild));
XMLT_LOG(mozXMLTermSession::InsertFragment,72,("Inserting child node ...\n"));
// nsCOMPtr<nsIContent> childContent (do_QueryInterface(childNode));
// if (childContent) childContent->List(stderr);
// Insert child node
nsCOMPtr<nsIDOMNode> resultNode;
PRBool replaceTem = replace;
if (beforeNode) {
if (replaceTem) {
// Replace before node
result = parentNode->ReplaceChild(childNode, beforeNode,
getter_AddRefs(resultNode));
beforeNode = nsnull;
nsCOMPtr<nsIDOMNode> newBeforeNode;
result = resultNode->GetNextSibling(getter_AddRefs(newBeforeNode));
if (NS_SUCCEEDED(result) && newBeforeNode) {
beforeNode = newBeforeNode.get();
replaceTem = false;
}
} else {
// Insert before specified node
result = parentNode->InsertBefore(childNode, beforeNode,
getter_AddRefs(resultNode));
}
} else {
// Append child
result = parentNode->AppendChild(childNode, getter_AddRefs(resultNode));
}
if (NS_FAILED(result))
return result;
// Refresh attributes of inserted child node (deep)
DeepRefreshEventHandlers(resultNode);
childNode = nextChild;
}
return NS_OK;
}
/** Substitute all occurrences of the '#' character in aString with
* aNumber, if aNumber >= 0;
* @ param aString string to be modified
* @ param aNumber number to substituted
*/
void mozXMLTermSession::SubstituteCommandNumber(nsString& aString,
PRInt32 aNumber)
{
if (aNumber < 0)
return;
PRInt32 numberOffset;
nsAutoString numberString = "";
numberString.Append(aNumber,10);
for (;;) {
// Search for '#' character
numberOffset = aString.FindChar((PRUnichar) '#');
if (numberOffset < 0)
break;
// Substitute '#' with supplied number
aString.Cut(numberOffset,1);
aString.Insert(numberString, numberOffset);
}
}
/** Sanitize event handler attribute values by imposing syntax checks.
* @param aAttrValue attribute value to be sanitized
* @param aEventName name of event being handled ("click", ...)
*/
void mozXMLTermSession::SanitizeAttribute(nsString& aAttrValue,
const char* aEventName)
{
// ****************NOTE***************
// At the moment this method simply prevents the word function and the
// the character '{' both occurring in the event handler attribute.
// NEEDS TO BE IMPROVED TO ENFORCE STRICTER REQUIREMENTS
// such as: the event handler attribute should always be of the form
// "return EventHandler(str_arg1, num_arg2, str_arg3, str_arg4);"
if ((aAttrValue.FindChar((PRUnichar)'{') >= 0) &&
(aAttrValue.Find("function") >= 0)) {
// Character '{' and string "function" both found in attribute value;
// set to null string
char* temCString = aAttrValue.ToNewCString();
XMLT_WARNING("mozXMLTermSession::SanitizeAttribute: Warning - deleted attribute on%s='%s'\n", aEventName, temCString);
nsCRT::free(temCString);
aAttrValue = "";
}
return;
}
/** Deep sanitizing of event handler attributes ("on*") prior to insertion
* of HTML fragments, to enfore consistent UI behaviour in XMLTerm and
* for security. The following actions are carried out:
* 1. Any SCRIPT tags in the fragment are simply deleted
* 2. All event handler attributes, except a few selected ones, are deleted.
* 3. The retained event handler attribute values are subject to strict
* checks.
* 4. If entryNumber >= 0, all '#' characters in the ID attribute and
* retained event handler attributes are substituted with entryNumber.
*
* @param domNode DOM node for HTML fragment to be sanitized
* @param parentNode parent DOM node (needed to delete SCRIPT elements;
* set to null if root element)
* @param entryNumber entry number (default value = -1)
*/
NS_IMETHODIMP mozXMLTermSession::DeepSanitizeFragment(
nsCOMPtr<nsIDOMNode>& domNode,
nsIDOMNode* parentNode,
PRInt32 entryNumber)
{
nsresult result;
PRInt32 j;
XMLT_LOG(mozXMLTermSession::DeepSanitizeFragment,72,("entryNumber=%d\n",
entryNumber));
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(domNode);
if (domElement) {
// Check if this is a script element (IGNORE CASE)
nsAutoString tagName = "";
result = domElement->GetTagName(tagName);
if (NS_SUCCEEDED(result) && tagName.EqualsIgnoreCase("script")) {
// Remove script element and return
XMLT_WARNING("mozXMLTermSession::DeepSanitizeFragment: Warning - rejected SCRIPT element in inserted HTML fragment\n");
if (parentNode) {
nsCOMPtr<nsIDOMNode> resultNode;
result = parentNode->RemoveChild(domNode, getter_AddRefs(resultNode));
if (NS_FAILED(result))
return result;
} else {
domNode = nsnull;
}
return NS_OK;
}
nsAutoString eventAttrVals[SESSION_EVENT_TYPES];
for (j=0; j<SESSION_EVENT_TYPES; j++)
eventAttrVals[j] = "";
nsAutoString attName, attValue;
for (j=0; j<SESSION_EVENT_TYPES; j++) {
attName = "on";
attName.Append(sessionEventNames[j]);
attValue = "";
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && (attValue.Length() > 0)) {
// Save allowed event attribute value for re-insertion
eventAttrVals[j] = attValue;
}
}
nsCOMPtr<nsIDOMNamedNodeMap> namedNodeMap(nsnull);
result = domNode->GetAttributes(getter_AddRefs(namedNodeMap));
if (NS_SUCCEEDED(result) && namedNodeMap) {
// Cycle through all attributes and delete all event attributes ("on*")
PRUint32 nodeCount;
result = namedNodeMap->GetLength(&nodeCount);
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDOMNode> attrNode;
PRUint32 k;
nsAutoString attrName, attrValue, prefix;
nsAutoString nullStr = "";
for (k=0; k<nodeCount; k++) {
result = namedNodeMap->Item(k, getter_AddRefs(attrNode));
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDOMAttr> attr = do_QueryInterface(attrNode);
if (attr) {
result = attr->GetName(attrName);
if (NS_SUCCEEDED(result)) {
result = attr->GetValue(attrValue);
if (NS_SUCCEEDED(result) && (attrName.Length() >= 2)) {
attrName.Left(prefix,2);
if (prefix.EqualsIgnoreCase("on")) {
// Delete event handler attribute
XMLT_LOG(mozXMLTermSession::DeepSanitizeFragment,79,
("Deleting event handler in fragment\n"));
result = domElement->SetAttribute(attrName, nullStr);
if (NS_FAILED(result))
return result;
}
}
}
}
}
}
}
}
if (entryNumber >= 0) {
// Process ID attribute
attName = "id";
attValue = "";
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && (attValue.Length() > 0)) {
// Modify attribute value
SubstituteCommandNumber(attValue, entryNumber);
domElement->SetAttribute(attName, attValue);
}
}
for (j=0; j<SESSION_EVENT_TYPES; j++) {
// Re-introduce sanitized event attribute values
attName = "on";
attName.Append(sessionEventNames[j]);
attValue = eventAttrVals[j];
if (attValue.Length() > 0) {
SubstituteCommandNumber(attValue, entryNumber);
// Sanitize attribute value
SanitizeAttribute(attValue, sessionEventNames[j]);
// Insert attribute value
domElement->SetAttribute(attName, attValue);
}
}
}
// Iterate over all child nodes for deep refresh
nsCOMPtr<nsIDOMNode> child;
result = domNode->GetFirstChild(getter_AddRefs(child));
if (NS_FAILED(result))
return NS_OK;
while (child) {
DeepSanitizeFragment(child, domNode, entryNumber);
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
if (NS_FAILED(result))
break;
}
return NS_OK;
}
/** Deep refresh of selected event handler attributes for DOM elements
* (WORKAROUND for inserting HTML fragments properly)
* @param domNode DOM node of branch to be refreshed
*/
NS_IMETHODIMP mozXMLTermSession::DeepRefreshEventHandlers(
nsCOMPtr<nsIDOMNode>& domNode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::DeepRefreshEventHandlers,82,("\n"));
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(domNode);
if (!domElement)
return NS_OK;
int j;
nsAutoString attName, attValue;
// Refresh event attributes
for (j=0; j<SESSION_EVENT_TYPES; j++) {
attName = "on";
attName.Append(sessionEventNames[j]);
XMLT_LOG(mozXMLTermSession::DeepRefreshEventHandlers,89,
("Refreshing on%s attribute\n",sessionEventNames[j] ));
attValue = "";
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && (attValue.Length() > 0)) {
// Refresh attribute value
domElement->SetAttribute(attName, attValue);
}
}
// Iterate over all child nodes for deep refresh
nsCOMPtr<nsIDOMNode> child;
result = domNode->GetFirstChild(getter_AddRefs(child));
if (NS_FAILED(result))
return NS_OK;
while (child) {
DeepRefreshEventHandlers(child);
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
if (NS_FAILED(result))
break;
}
return NS_OK;
}
/** Forces display of data in output buffer
* @param flushAction type of flush action: display, split-off, clear, or
* close incomplete lines
*/
NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction)
{
nsresult result;
if (!mEntryHasOutput)
return NS_OK;
XMLT_LOG(mozXMLTermSession::FlushOutput,70,
("flushAction=%d, mOutputDisplayType=%d\n",
flushAction, mOutputDisplayType));
PRBool preDisplay = (mOutputDisplayType == PRE_STDOUT_NODE) ||
(mOutputDisplayType == PRE_STDERR_NODE) ||
(mOutputDisplayType == PRE_STDIN_NODE);
if (preDisplay) {
// PRE text display
OutputDisplayType preDisplayType = mOutputDisplayType;
nsAutoString preTextSplit = "";
if (flushAction != DISPLAY_INCOMPLETE_FLUSH) {
// Split/clear/close incomplete line
XMLT_LOG(mozXMLTermSession::FlushOutput,72,
("mPreTextIncomplete.Length()=%d\n",
mPreTextIncomplete.Length() ));
if (flushAction == SPLIT_INCOMPLETE_FLUSH) {
// Move incomplete text to new PRE element
preTextSplit = mPreTextIncomplete;
} else if (flushAction == CLOSE_INCOMPLETE_FLUSH) {
// Move incomplete text into buffer
mPreTextBuffered += mPreTextIncomplete;
}
// Clear incomplete PRE text
mPreTextIncomplete = "";
if ((mPreTextBufferLines == 0) && (mPreTextBuffered.Length() == 0)) {
// Remove lone text node
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputDisplayNode->RemoveChild(mOutputTextNode,
getter_AddRefs(resultNode));
// Check if PRE node has any child nodes
PRBool hasChildNodes = true;
result = mOutputDisplayNode->HasChildNodes(&hasChildNodes);
if (!hasChildNodes) {
// No child nodes left; Delete PRE node itself
nsCOMPtr<nsIDOMNode> resultNode2;
result = mOutputBlockNode->RemoveChild(mOutputDisplayNode,
getter_AddRefs(resultNode));
}
mOutputDisplayNode = nsnull;
mOutputDisplayType = NO_NODE;
mOutputTextNode = nsnull;
}
}
if (mOutputDisplayNode != nsnull) {
// Update displayed PRE text
nsAutoString outString = mPreTextBuffered;
outString += mPreTextIncomplete;
// Increment total output line count for entry
mEntryOutputLines += mPreTextBufferLines;
if (outString != mPreTextDisplayed) {
// Display updated buffer
mPreTextDisplayed = outString;
XMLT_LOG(mozXMLTermSession::FlushOutput,72,
("mOutputTextNode=%d\n", (mOutputTextNode != nsnull)));
result = SetDOMText(mOutputTextNode, mPreTextDisplayed);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
}
}
if (flushAction != DISPLAY_INCOMPLETE_FLUSH) {
// Split/clear/close incomplete line
mOutputDisplayNode = nsnull;
mOutputDisplayType = NO_NODE;
mOutputTextNode = nsnull;
if ( (flushAction == SPLIT_INCOMPLETE_FLUSH) &&
(preTextSplit.Length() > 0) ) {
// Create new PRE element with incomplete text
nsAutoString styleStr = "";
if (preDisplayType == PRE_STDIN_NODE) {
styleStr += (PRUnichar) LTERM_STDIN_STYLE;
} else if (preDisplayType == PRE_STDERR_NODE) {
styleStr += (PRUnichar) LTERM_STDERR_STYLE;
} else {
styleStr += (PRUnichar) LTERM_STDOUT_STYLE;
}
XMLT_LOG(mozXMLTermSession::FlushOutput,72,("splitting\n"));
AppendOutput(preTextSplit, styleStr, false);
FlushOutput(DISPLAY_INCOMPLETE_FLUSH);
}
}
} else if (mOutputDisplayNode != nsnull) {
// Non-PRE node
if (flushAction == CLEAR_INCOMPLETE_FLUSH) {
// Clear incomplete line info
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputBlockNode->RemoveChild(mOutputDisplayNode,
getter_AddRefs(resultNode));
mOutputDisplayNode = nsnull;
mOutputDisplayType = NO_NODE;
mOutputTextNode = nsnull;
} else if (flushAction == CLOSE_INCOMPLETE_FLUSH) {
mOutputDisplayNode = nsnull;
mOutputDisplayType = NO_NODE;
mOutputTextNode = nsnull;
}
}
XMLT_LOG(mozXMLTermSession::FlushOutput,71,("returning\n"));
return NS_OK;
}
/** Positions cursor below the last output element */
void mozXMLTermSession::PositionOutputCursor(mozILineTermAux* lineTermAux)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::PositionOutputCursor,80,("\n"));
if (!mOutputTextNode) {
// Append dummy output line
nsCOMPtr<nsIDOMNode> spanNode, textNode;
nsAutoString tagName = "span";
nsAutoString elementName = sessionElementNames[STDOUT_ELEMENT];
result = NewElementWithText(tagName, elementName, -1,
mOutputBlockNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode)
return;
mOutputDisplayType = SPAN_DUMMY_NODE;
mOutputDisplayNode = spanNode;
mOutputTextNode = textNode;
mOutputTextOffset = 0;
}
// Get selection
nsCOMPtr<nsIDOMSelection> selection;
result = mPresShell->GetSelection(SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_SUCCEEDED(result) && selection) {
// Position cursor at end of line
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(mOutputTextNode) );
nsAutoString text = "";
domText->GetData(text);
PRInt32 textOffset = text.Length();
if (lineTermAux && (mOutputDisplayType == PRE_STDIN_NODE)) {
// Get cursor column
PRInt32 cursorCol = 0;
lineTermAux->GetCursorColumn(&cursorCol);
textOffset = cursorCol - mOutputTextOffset;
if (textOffset > (PRInt32)text.Length())
textOffset = text.Length();
}
result = selection->Collapse(mOutputTextNode, textOffset);
}
}
/** Scrolls document to align bottom and left margin with screen */
NS_IMETHODIMP mozXMLTermSession::ScrollToBottomLeft(void)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::ScrollToBottomLeft,70,("\n"));
// Get DOM Window
nsCOMPtr<nsIDocShell> docShell;
result = mXMLTerminal->GetDocShell(getter_AddRefs(docShell));
if (NS_FAILED(result) || !docShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMWindow> domWindow;
result = mozXMLTermUtils::ConvertDocShellToDOMWindow(docShell,
getter_AddRefs(domWindow));
if (NS_FAILED(result) || !domWindow)
return NS_ERROR_FAILURE;
// Scroll to bottom left of screen
domWindow->ScrollBy(-99999,99999);
return NS_OK;
}
/** Gets current entry (command) number
* @param aNumber (output) current entry number
*/
NS_IMETHODIMP mozXMLTermSession::GetCurrentEntryNumber(PRInt32 *aNumber)
{
*aNumber = mCurrentEntryNumber;
return NS_OK;
}
// Get size of entry history buffer
NS_IMETHODIMP mozXMLTermSession::GetHistory(PRInt32 *aHistory)
{
*aHistory = mMaxHistory;
return NS_OK;
}
// Set size of entry history buffer
NS_IMETHODIMP mozXMLTermSession::SetHistory(PRInt32 aHistory)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::SetHistory,30,("\n"));
if (aHistory < 1)
aHistory = 1;
if (mInitialized && mStartEntryNode && (aHistory < mMaxHistory)) {
// Delete any extra entry blocks
PRInt32 delEntries = (mCurrentEntryNumber-mStartEntryNumber)
- aHistory;
PRInt32 j;
for (j=0; j<delEntries; j++) {
nsCOMPtr<nsIDOMNode> newStartNode;
result = mStartEntryNode->GetNextSibling(getter_AddRefs(newStartNode));
if (NS_FAILED(result) || !newStartNode) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMNode> resultNode;
result = mSessionNode->RemoveChild(mStartEntryNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result)) {
return NS_ERROR_FAILURE;
}
mStartEntryNode = newStartNode;
mStartEntryNumber++;
}
}
mMaxHistory = aHistory;
return NS_OK;
}
// Get HTML prompt string
NS_IMETHODIMP mozXMLTermSession::GetPrompt(PRUnichar **_aPrompt)
{
// NOTE: Need to be sure that this may be freed by nsAllocator::Free
*_aPrompt = mPromptHTML.ToNewUnicode();
return NS_OK;
}
// Set HTML prompt string
NS_IMETHODIMP mozXMLTermSession::SetPrompt(const PRUnichar* aPrompt)
{
mPromptHTML = aPrompt;
return NS_OK;
}
/** Gets flag denoting whether terminal is in full screen mode
* @param aFlag (output) screen mode flag
*/
NS_IMETHODIMP mozXMLTermSession::GetScreenMode(PRBool* aFlag)
{
if (!aFlag)
return NS_ERROR_NULL_POINTER;
*aFlag = (mScreenNode != nsnull);
return NS_OK;
}
/** Create a DIV element with attributes NAME="preface", CLASS="preface",
* and ID="preface0", containing an empty text node, and append it as a
* child of the main BODY element. Also make it the current display element.
*/
NS_IMETHODIMP mozXMLTermSession::NewPreface(void)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewPreface,40,("\n"));
// Create preface element and append as child of session element
nsCOMPtr<nsIDOMNode> divNode;
nsAutoString tagName = "div";
nsAutoString name = "preface";
result = NewElement(tagName, name, 0,
mSessionNode, divNode);
if (NS_FAILED(result) || !divNode)
return NS_ERROR_FAILURE;
mOutputBlockNode = divNode;
mOutputDisplayType = NO_NODE;
mOutputDisplayNode = nsnull;
mOutputTextNode = nsnull;
// Command output being processed
mEntryHasOutput = true;
return NS_OK;
}
/** Create and append a new DIV element with attributes NAME="entry",
* CLASS="entry", and ID="entry#" as the last child of the main BODY element,
* where "#" denotes the new entry number obtained by incrementing the
* current entry number.
* Inside the entry element, create a DIV element with attributes
* NAME="input", CLASS="input", and ID="input#" containing two elements,
* named "prompt" and "command", each containing a text node.
* Insert the supplied prompt string into the prompt element's text node.
* @param aPrompt prompt string to be inserted into prompt element
*/
NS_IMETHODIMP mozXMLTermSession::NewEntry(const nsString& aPrompt)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewEntry,50,("\n"));
if (mCurrentEntryNumber == 0) {
// First entry
mCurrentEntryNumber = 1;
mStartEntryNumber = 1;
} else {
// Not first entry
// Add event attributes to current command element
result = SetEventAttributes(sessionElementNames[COMMAND_ELEMENT],
mCurrentEntryNumber,
mCommandSpanNode);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// Increment entry number
mCurrentEntryNumber++;
if ((mCurrentEntryNumber - mStartEntryNumber) > mMaxHistory) {
// Delete oldest displayed entry element
nsCOMPtr<nsIDOMNode> newStartNode;
result = mStartEntryNode->GetNextSibling(getter_AddRefs(newStartNode));
if (NS_FAILED(result) || !newStartNode) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMNode> resultNode;
result = mSessionNode->RemoveChild(mStartEntryNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result)) {
return NS_ERROR_FAILURE;
}
mStartEntryNode = newStartNode;
mStartEntryNumber++;
}
}
XMLT_LOG(mozXMLTermSession::NewEntry,50,
("%d (start=%d)\n", mCurrentEntryNumber, mStartEntryNumber));
nsAutoString tagName, name;
// Create "entry" element
nsCOMPtr<nsIDOMNode> entryNode;
tagName = "div";
name = sessionElementNames[ENTRY_ELEMENT];
result = NewElement(tagName, name, mCurrentEntryNumber,
mSessionNode, entryNode);
if (NS_FAILED(result) || !entryNode) {
return NS_ERROR_FAILURE;
}
mCurrentEntryNode = entryNode;
if (mCurrentEntryNumber == 1) {
mStartEntryNode = mCurrentEntryNode;
}
// Create "input" element containing "prompt" and "command" elements
nsCOMPtr<nsIDOMNode> inputNode;
tagName = "div";
name = sessionElementNames[INPUT_ELEMENT];
result = NewElement(tagName, name, mCurrentEntryNumber,
mCurrentEntryNode, inputNode);
if (NS_FAILED(result) || !inputNode) {
return NS_ERROR_FAILURE;
}
nsAutoString classAttribute;
// Create prompt element
nsCOMPtr<nsIDOMNode> newPromptSpanNode;
tagName = "span";
name = sessionElementNames[PROMPT_ELEMENT];
result = NewElement(tagName, name, mCurrentEntryNumber,
inputNode, newPromptSpanNode);
if (NS_FAILED(result) || !newPromptSpanNode) {
return NS_ERROR_FAILURE;
}
mPromptSpanNode = newPromptSpanNode;
// Add event attributes to prompt element
result = SetEventAttributes(name, mCurrentEntryNumber,
mPromptSpanNode);
if (mPromptHTML.Length() == 0) {
#define DEFAULT_ICON_PROMPT
#ifdef DEFAULT_ICON_PROMPT // Experimental code; has scrolling problems
// Create text node + image node as child of prompt element
nsCOMPtr<nsIDOMNode> spanNode, textNode;
tagName = "span";
name ="noicons";
result = NewElementWithText(tagName, name, -1,
mPromptSpanNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode) {
return NS_ERROR_FAILURE;
}
// Set prompt text
result = SetDOMText(textNode, aPrompt);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// Create IMG element
tagName = "img";
nsCOMPtr<nsIDOMElement> imgElement;
result = mDOMDocument->CreateElement(tagName, getter_AddRefs(imgElement));
if (NS_FAILED(result) || !imgElement)
return NS_ERROR_FAILURE;
// Set attributes
nsAutoString attName("class");
nsAutoString attValue("icons");
imgElement->SetAttribute(attName, attValue);
attName = "src";
attValue = "chrome://xmlterm/skin/images/wheel.gif";
imgElement->SetAttribute(attName, attValue);
attName = "align";
attValue = "middle";
imgElement->SetAttribute(attName, attValue);
nsCOMPtr<nsIDOMNode> resultNode;
// Append IMG element
nsCOMPtr<nsIDOMNode> imgNode = do_QueryInterface(imgElement);
result = mPromptSpanNode->AppendChild(imgNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// Append text node containing single space
nsCOMPtr<nsIDOMText> stubText;
nsAutoString spaceStr (" ");
result = mDOMDocument->CreateTextNode(spaceStr, getter_AddRefs(stubText));
if (NS_FAILED(result) || !stubText)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> stubNode = do_QueryInterface(stubText);
result = mPromptSpanNode->AppendChild(stubNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
#else // !DEFAULT_ICON_PROMPT
// Create text node as child of prompt element
nsCOMPtr<nsIDOMNode> textNode;
result = NewTextNode(mPromptSpanNode, textNode);
if (NS_FAILED(result) || !textNode)
return NS_ERROR_FAILURE;
// Set prompt text
result = SetDOMText(textNode, aPrompt);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
#endif // !DEFAULT_ICON_PROMPT
} else {
// User-specified HTML prompt
result = InsertFragment(mPromptHTML, mPromptSpanNode,
mCurrentEntryNumber);
}
// Create command element
nsCOMPtr<nsIDOMNode> newCommandSpanNode;
tagName = "span";
name = sessionElementNames[COMMAND_ELEMENT];
result = NewElement(tagName, name, mCurrentEntryNumber,
inputNode, newCommandSpanNode);
if (NS_FAILED(result) || !newCommandSpanNode) {
return NS_ERROR_FAILURE;
}
mCommandSpanNode = newCommandSpanNode;
// Create text node as child of command element
nsCOMPtr<nsIDOMNode> textNode2;
result = NewTextNode(mCommandSpanNode, textNode2);
if (NS_FAILED(result) || !textNode2)
return NS_ERROR_FAILURE;
mInputTextNode = textNode2;
// Create output element and append as child of current entry element
nsCOMPtr<nsIDOMNode> divNode;
tagName = "div";
name = sessionElementNames[OUTPUT_ELEMENT];
result = NewElement(tagName, name, mCurrentEntryNumber,
mCurrentEntryNode, divNode);
if (NS_FAILED(result) || !divNode)
return NS_ERROR_FAILURE;
mOutputBlockNode = divNode;
mOutputDisplayType = NO_NODE;
mOutputDisplayNode = nsnull;
mOutputTextNode = nsnull;
// No command output processed yet
mEntryHasOutput = false;
mEntryOutputLines = 0;
return NS_OK;
}
/** Create a DIV element with attributes NAME="screen" and CLASS="screen",
* containing an empty text node, and append it as a
* child of the main BODY element. Also make it the current display element.
*/
NS_IMETHODIMP mozXMLTermSession::NewScreen(void)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewScreen,0,("\n"));
// Create screen element and append as child of session element
nsCOMPtr<nsIDOMNode> divNode;
nsAutoString tagName = "div";
nsAutoString name = "screen";
result = NewElement(tagName, name, 0,
mSessionNode, divNode);
if (NS_FAILED(result) || !divNode)
return NS_ERROR_FAILURE;
mScreenNode = divNode;
// Create individual row elements
nsCOMPtr<nsIDOMNode> resultNode;
PRInt32 row;
for (row=0; row < mScreenRows; row++) {
NewRow(nsnull, getter_AddRefs(resultNode));
}
// Collapse selection to bottom of screen (for scrolling)
result = PositionScreenCursor(0, 0);
if (NS_SUCCEEDED(result)) {
result = mPresShell->ScrollSelectionIntoView(SELECTION_NORMAL,
SELECTION_FOCUS_REGION);
}
return NS_OK;
}
/** Returns DOM PRE node corresponding to specified screen row
*/
NS_IMETHODIMP mozXMLTermSession::GetRow(PRInt32 aRow, nsIDOMNode** aRowNode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::GetRow,60,("aRow=%d\n", aRow));
if (!aRowNode)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNodeList> childNodes;
result = mScreenNode->GetChildNodes(getter_AddRefs(childNodes));
if (NS_FAILED(result) || !childNodes)
return NS_ERROR_FAILURE;
PRUint32 nChildren = 0;
childNodes->GetLength(&nChildren);
XMLT_LOG(mozXMLTermSession::GetRow,62,("nChildren=%d, mScreenRows=%d\n",
nChildren, mScreenRows));
PRInt32 rowIndex = mScreenRows - aRow - 1;
if ((rowIndex < 0) || (rowIndex >= (PRInt32)nChildren))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> childNode;
result = childNodes->Item(rowIndex, getter_AddRefs(childNode));
if (NS_FAILED(result) || !childNode)
return NS_ERROR_FAILURE;
*aRowNode = childNode.get();
NS_ADDREF(*aRowNode);
XMLT_LOG(mozXMLTermSession::GetRow,61,("returning\n"));
return NS_OK;
}
/** Positions cursor to specified screen row/col position
*/
NS_IMETHODIMP mozXMLTermSession::PositionScreenCursor(PRInt32 aRow,
PRInt32 aCol)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::PositionScreenCursor,60,
("row=%d, col=%d\n",aRow,aCol));
// Get row node
nsCOMPtr<nsIDOMNode> rowNode;
result = GetRow(aRow, getter_AddRefs(rowNode));
if (NS_FAILED(result) || !rowNode)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNodeList> childNodes;
result = rowNode->GetChildNodes(getter_AddRefs(childNodes));
if (NS_FAILED(result) || !childNodes)
return NS_ERROR_FAILURE;
PRUint32 nChildren = 0;
childNodes->GetLength(&nChildren);
XMLT_LOG(mozXMLTermSession::GetScreenText,60,("children=%d\n",nChildren));
PRUint16 nodeType;
PRUint32 j;
PRInt32 prevCols = 0;
PRInt32 textOffset = 0;
nsCOMPtr<nsIDOMNode> textNode = nsnull;
nsCOMPtr<nsIDOMNode> childNode;
nsAutoString text = "";
for (j=0; j<nChildren; j++) {
result = childNodes->Item(j, getter_AddRefs(childNode));
if (NS_FAILED(result) || !childNode)
return NS_ERROR_FAILURE;
result = childNode->GetNodeType(&nodeType);
if (NS_FAILED(result))
return result;
XMLT_LOG(mozXMLTermSession::GetScreenText,60,
("j=%d, nodeType=%d\n", j, nodeType));
if (nodeType != nsIDOMNode::TEXT_NODE) {
nsCOMPtr<nsIDOMNode> temNode;
result = childNode->GetFirstChild(getter_AddRefs(temNode));
if (NS_FAILED(result))
return result;
childNode = temNode;
result = childNode->GetNodeType(&nodeType);
if (NS_FAILED(result))
return result;
PR_ASSERT(nodeType == nsIDOMNode::TEXT_NODE);
}
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(childNode) );
result = domText->GetData(text);
if (NS_FAILED(result))
return result;
XMLT_LOG(mozXMLTermSession::GetScreenText,60,("prevCols=%d\n",prevCols));
if (prevCols+(PRInt32)text.Length() >= aCol) {
// Determine offset in current text element
textOffset = aCol - prevCols;
textNode = childNode;
} else if (j == nChildren-1) {
// Position at end of line
textOffset = text.Length();
textNode = childNode;
}
}
// Get selection
nsCOMPtr<nsIDOMSelection> selection;
result = mPresShell->GetSelection(SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_SUCCEEDED(result) && selection) {
// Collapse selection to cursor position
result = selection->Collapse(textNode, textOffset);
}
return NS_OK;
}
/** Create a PRE element with attributes NAME="row", CLASS="row",
* containing an empty text node, and insert it as a
* child of the SCREEN element before beforeRowNode, or at the
* end if beforeRowNode is null.
*/
NS_IMETHODIMP mozXMLTermSession::NewRow(nsIDOMNode* beforeRowNode,
nsIDOMNode** resultNode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewRow,60,("\n"));
// Create PRE display node
nsCOMPtr<nsIDOMNode> preNode, textNode;
nsAutoString tagName = "pre";
nsAutoString elementName = "row";
result = NewElementWithText(tagName, elementName, -1,
mScreenNode, preNode, textNode);
if (NS_FAILED(result) || !preNode || !textNode)
return NS_ERROR_FAILURE;
// Set PRE element attributes
nsCOMPtr<nsIDOMElement> preElement = do_QueryInterface(preNode);
nsAutoString att("cols");
nsAutoString val("");
val.Append(mScreenCols,10);
preElement->SetAttribute(att, val);
att = "rows";
val = "1";
preElement->SetAttribute(att, val);
if (beforeRowNode) {
// Insert row node
result = mScreenNode->InsertBefore(preNode, beforeRowNode, resultNode);
} else {
// Append row node
result = mScreenNode->AppendChild(preNode, resultNode);
}
return NS_OK;
}
/** Displays screen output string with specified style
* @param aString string to be processed
* @param aStyle style values for string (see lineterm.h)
* (if it is a null string, STDOUT style is assumed)
* @param aRow row in which to insert string
*/
NS_IMETHODIMP mozXMLTermSession::DisplayRow(const nsString& aString,
const nsString& aStyle,
PRInt32 aRow)
{
nsresult result;
const PRInt32 strLength = aString.Length();
const PRInt32 styleLength = aStyle.Length();
const PRUnichar *strStyle = aStyle.GetUnicode();
XMLT_LOG(mozXMLTermSession::DisplayRow,70,
("aRow=%d, strLength=%d, styleLength=%d\n",
aRow, strLength, styleLength));
// Check if line has uniform style
PRUnichar uniformStyle = LTERM_STDOUT_STYLE;
if (styleLength > 0) {
PRInt32 j;
PR_ASSERT(styleLength == strLength);
uniformStyle = strStyle[0];
for (j=1; j<strLength; j++) {
if (strStyle[j] != strStyle[0]) {
uniformStyle = 0;
}
}
}
nsCOMPtr<nsIDOMNode> rowNode;
result = GetRow(aRow, getter_AddRefs(rowNode));
if (NS_FAILED(result) || !rowNode)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNodeList> childNodes;
result = rowNode->GetChildNodes(getter_AddRefs(childNodes));
if (NS_FAILED(result) || !childNodes)
return NS_ERROR_FAILURE;
PRUint32 nChildren = 0;
childNodes->GetLength(&nChildren);
XMLT_LOG(mozXMLTermSession::DisplayRow,79,("nChildren=%d\n", nChildren));
if ((nChildren == 1) && (uniformStyle == LTERM_STDOUT_STYLE)) {
// Get child node
nsCOMPtr<nsIDOMNode> childNode;
result = rowNode->GetFirstChild(getter_AddRefs(childNode));
if (NS_FAILED(result) || !childNode)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(childNode) );
if (domText) {
// Display uniform style
result = SetDOMText(childNode, aString);
if (NS_FAILED(result))
return result;
return NS_OK;
}
}
// Delete all child nodes for the row
nsCOMPtr<nsIDOMNode> childNode;
PRInt32 j;
for (j=nChildren-1; j>=0; j--) {
result = childNodes->Item(j, getter_AddRefs(childNode));
if (NS_FAILED(result) || !childNode)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> resultNode;
result = rowNode->RemoveChild(childNode, getter_AddRefs(resultNode));
if (NS_FAILED(result))
return result;
}
nsCOMPtr<nsIDOMNode> spanNode, textNode;
nsAutoString tagName = "span";
nsAutoString elementName;
nsAutoString subString;
PRInt32 k;
PRUnichar currentStyle = LTERM_STDOUT_STYLE;
if (styleLength > 0)
currentStyle = strStyle[0];
PRInt32 offset = 0;
offset = 0;
PR_ASSERT(strLength > 0);
for (k=1; k<strLength+1; k++) {
if ((k == strLength) || ((k < styleLength) &&
(strStyle[k] != currentStyle)) ) {
// Change of style or end of string
if (currentStyle == LTERM_STDOUT_STYLE) {
// Create text node
result = NewTextNode(rowNode, textNode);
if (NS_FAILED(result) || !textNode)
return NS_ERROR_FAILURE;
} else {
// Span Node
switch (currentStyle) {
case LTERM_STDOUT_STYLE | LTERM_BOLD_STYLE:
elementName = "boldstyle";
break;
case LTERM_STDOUT_STYLE | LTERM_ULINE_STYLE:
elementName = "underlinestyle";
break;
case LTERM_STDOUT_STYLE | LTERM_BLINK_STYLE:
elementName = "blinkstyle";
break;
case LTERM_STDOUT_STYLE | LTERM_INVERSE_STYLE:
elementName = "inversestyle";
break;
default:
elementName = "boldstyle";
break;
}
result = NewElementWithText(tagName, elementName, -1,
rowNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode)
return NS_ERROR_FAILURE;
}
aString.Mid(subString, offset, k-offset);
result = SetDOMText(textNode, subString);
if (NS_FAILED(result))
return result;
if (k < styleLength) {
// Change style
currentStyle = strStyle[k];
offset = k;
}
}
}
return NS_OK;
}
/** Append a BR element as the next child of specified parent.
* @param parentNode parent node for BR element
*/
NS_IMETHODIMP mozXMLTermSession::NewBreak(nsIDOMNode* parentNode)
{
nsresult result;
nsAutoString tagName = "br";
XMLT_LOG(mozXMLTermSession::NewBreak,60,("\n"));
// Create "br" element and append as child of specified parent
nsCOMPtr<nsIDOMNode> brNode;
nsAutoString name = "";
result = NewElement(tagName, name, -1, parentNode, brNode);
if (NS_FAILED(result) || !brNode)
return NS_ERROR_FAILURE;
return NS_OK;
}
/** Create an empty block element with tag name tagName with attributes
* NAME="name", CLASS="name", and ID="name#", and appends it as a child of
* the specified parent. ("#" denotes the specified number)
* Also create an empty text node inside the new block element.
* @param tagName tag name of element
* @param name name and class of element
* (If zero-length string, then no attributes are set)
* @param number numeric suffix for element ID
* (If < 0, no ID attribute is defined)
* @param parentNode parent node for element
* @param blockNode (output) block-level DOM node for created element
* @param textNode (output) child text DOM node of element
* @param beforeNode child node before which to insert new node
* if null, insert after last child node
* (default value is null)
*/
NS_IMETHODIMP mozXMLTermSession::NewElementWithText(const nsString& tagName,
const nsString& name, PRInt32 number,
nsIDOMNode* parentNode,
nsCOMPtr<nsIDOMNode>& blockNode,
nsCOMPtr<nsIDOMNode>& textNode,
nsIDOMNode* beforeNode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewElementWithText,80,("\n"));
// Create block element
result = NewElement(tagName, name, number, parentNode, blockNode,
beforeNode);
if (NS_FAILED(result) || !blockNode)
return NS_ERROR_FAILURE;
// Create text node as child of block element
result = NewTextNode(blockNode, textNode);
if (NS_FAILED(result) || !textNode)
return NS_ERROR_FAILURE;
return NS_OK;
}
/** Creates an empty anchor (A) element with tag name tagName with attributes
* CLASS="classAttribute", and ID="classAttribute#", and appends it as a
* child of the specified parent. ("#" denotes the specified number)
* @param classAttribute class attribute of anchor element
* (If zero-length string, then no attributes are set)
* @param number numeric suffix for element ID
* (If < 0, no ID attribute is defined)
* @param parentNode parent node for element
* @param anchorNode (output) DOM node for created anchor element
*/
NS_IMETHODIMP mozXMLTermSession::NewAnchor(const nsString& classAttribute,
PRInt32 number,
nsIDOMNode* parentNode,
nsCOMPtr<nsIDOMNode>& anchorNode)
{
nsresult result;
nsAutoString tagName("a");
XMLT_LOG(mozXMLTermSession::NewAnchor,80,("\n"));
// Create anchor
nsCOMPtr<nsIDOMElement> newElement;
result = mDOMDocument->CreateElement(tagName, getter_AddRefs(newElement));
if (NS_FAILED(result) || !newElement)
return NS_ERROR_FAILURE;
// Set element attributes
nsAutoString hrefAtt("href");
nsAutoString hrefVal("#");
newElement->SetAttribute(hrefAtt, hrefVal);
if (classAttribute.Length() > 0) {
nsAutoString classStr("class");
newElement->SetAttribute(classStr, classAttribute);
if (number >= 0) {
nsAutoString idAtt("id");
nsAutoString idVal(classAttribute);
idVal.Append(number,10);
newElement->SetAttribute(idAtt, idVal);
}
}
// Append child to parent
nsCOMPtr<nsIDOMNode> newBlockNode = do_QueryInterface(newElement);
result = parentNode->AppendChild(newBlockNode, getter_AddRefs(anchorNode));
if (NS_FAILED(result) || !anchorNode)
return NS_ERROR_FAILURE;
return NS_OK;
}
/** Creates an empty block element with tag name tagName with attributes
* NAME="name", CLASS="name", and ID="name#", and appends it as a child of
* the specified parent. ("#" denotes the specified number)
* @param tagName tag name of element
* @param name name and class of element
* (If zero-length string, then no attributes are set)
* @param number numeric suffix for element ID
* (If < 0, no ID attribute is defined)
* @param parentNode parent node for element
* @param blockNode (output) block-level DOM node for created element
* @param beforeNode child node before which to insert new node
* if null, insert after last child node
* (default value is null)
*/
NS_IMETHODIMP mozXMLTermSession::NewElement(const nsString& tagName,
const nsString& name, PRInt32 number,
nsIDOMNode* parentNode,
nsCOMPtr<nsIDOMNode>& blockNode,
nsIDOMNode* beforeNode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewElement,80,("\n"));
// Create element
nsCOMPtr<nsIDOMElement> newElement;
result = mDOMDocument->CreateElement(tagName, getter_AddRefs(newElement));
if (NS_FAILED(result) || !newElement)
return NS_ERROR_FAILURE;
if (name.Length() > 0) {
// Set attributes
nsAutoString classAtt("class");
nsAutoString classVal(name);
newElement->SetAttribute(classAtt, classVal);
nsAutoString nameAtt("name");
nsAutoString nameVal(name);
newElement->SetAttribute(nameAtt, nameVal);
if (number >= 0) {
nsAutoString idAtt("id");
nsAutoString idVal(name);
idVal.Append(number,10);
newElement->SetAttribute(idAtt, idVal);
}
}
nsCOMPtr<nsIDOMNode> newBlockNode = do_QueryInterface(newElement);
if (beforeNode) {
// Insert child
result = parentNode->InsertBefore(newBlockNode, beforeNode,
getter_AddRefs(blockNode));
if (NS_FAILED(result) || !blockNode)
return NS_ERROR_FAILURE;
} else {
// Append child
result = parentNode->AppendChild(newBlockNode, getter_AddRefs(blockNode));
if (NS_FAILED(result) || !blockNode)
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/** Creates a new DOM text node, and appends it as a child of the
* specified parent.
* @param parentNode parent node for element
* @param textNode (output) created text DOM node
*/
NS_IMETHODIMP mozXMLTermSession::NewTextNode( nsIDOMNode* parentNode,
nsCOMPtr<nsIDOMNode>& textNode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewTextNode,80,("\n"));
// Create text node
nsCOMPtr<nsIDOMText> newText;
nsAutoString nullStr("");
result = mDOMDocument->CreateTextNode(nullStr, getter_AddRefs(newText));
if (NS_FAILED(result) || !newText)
return NS_ERROR_FAILURE;
// Append child to parent
nsCOMPtr<nsIDOMNode> newTextNode = do_QueryInterface(newText);
result = parentNode->AppendChild(newTextNode, getter_AddRefs(textNode));
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
return NS_OK;
}
/** Creates a new IFRAME element with attribute NAME="iframe#",
* and appends it as a child of the specified parent.
* ("#" denotes the specified number)
* @param parentNode parent node for element
* @param number numeric suffix for element ID
* (If < 0, no name attribute is defined)
* @param frameBorder IFRAME FRAMEBORDER attribute
* @param src IFRAME SRC attribute
* @param width IFRAME width attribute
* @param height IFRAME height attribute
*/
NS_IMETHODIMP mozXMLTermSession::NewIFrame(nsIDOMNode* parentNode,
PRInt32 number,
PRInt32 frameBorder,
const nsString& src,
const nsString& width,
const nsString& height)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewIFrame,80,("\n"));
#if 0
nsAutoString iframeFrag = "<iframe name='iframe";
iframeFrag.Append(number,10);
iframeFrag.Append("' frameborder=")
iframeFrag.Append(frameBorder,10);
iframeFrag.Append(" src='");
iframeFrag.Append(src)
iframeFrag.Append("'> </iframe>\n");
result = InsertFragment(iframeFrag, parentNode, number);
if (NS_FAILED(result))
return result;
return NS_OK;
#else
// Create IFRAME element
nsCOMPtr<nsIDOMElement> newElement;
nsAutoString tagName = "iframe";
result = mDOMDocument->CreateElement(tagName, getter_AddRefs(newElement));
if (NS_FAILED(result) || !newElement)
return NS_ERROR_FAILURE;
nsAutoString attName, attValue;
// Set attributes
if (number >= 0) {
attName = "name";
attValue = "iframe";
attValue.Append(number,10);
newElement->SetAttribute(attName, attValue);
}
attName = "frameborder";
attValue = "";
attValue.Append(frameBorder,10);
newElement->SetAttribute(attName, attValue);
if (src.Length() > 0) {
// Set SRC attribute
attName = "src";
newElement->SetAttribute(attName, src);
}
if (width.Length() > 0) {
// Set WIDTH attribute
attName = "width";
newElement->SetAttribute(attName, width);
}
if (height.Length() > 0) {
// Set HEIGHT attribute
attName = "height";
newElement->SetAttribute(attName, height);
}
// Append child to parent
nsCOMPtr<nsIDOMNode> iframeNode;
nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(newElement);
result = parentNode->AppendChild(newNode, getter_AddRefs(iframeNode));
if (NS_FAILED(result) || !iframeNode)
return NS_ERROR_FAILURE;
return NS_OK;
#endif
}
/** Add event attributes (onclick, ...) to DOM node
* @param name name of DOM node (supplied as argument to the event handler)
* @param number entry number (supplied as argument to the event handler)
* @param domNode DOM node to be modified
*/
NS_IMETHODIMP mozXMLTermSession::SetEventAttributes(const nsString& name,
PRInt32 number,
nsCOMPtr<nsIDOMNode>& domNode)
{
nsresult result;
nsCOMPtr <nsIDOMElement> domElement = do_QueryInterface(domNode);
if (!domElement)
return NS_ERROR_FAILURE;
int j;
for (j=0; j<SESSION_EVENT_TYPES; j++) {
nsAutoString attName("on");
attName += sessionEventNames[j];
nsAutoString attValue("return HandleEvent(event, '");
attValue += sessionEventNames[j];
attValue += "','";
attValue += name;
attValue += "','";
attValue.Append(number,10);
attValue += "','');";
result = domElement->SetAttribute(attName, attValue);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/** Sets text content of a DOM node to supplied string
* @param textNode DOM text node to be modified
* @param aString string to be inserted
*/
NS_IMETHODIMP mozXMLTermSession::SetDOMText(nsCOMPtr<nsIDOMNode>& textNode,
const nsString& aString)
{
nsresult result;
nsCOMPtr<nsIDOMText> domText (do_QueryInterface(textNode));
if (!domText)
return NS_ERROR_FAILURE;
result = domText->SetData(aString);
return result;
}
/** Checks if node is a text node
* @param aNode DOM node to be checked
* @return true if node is a text node
*/
PRBool mozXMLTermSession::IsTextNode(nsIDOMNode *aNode)
{
if (!aNode) {
NS_NOTREACHED("null node passed to IsTextNode()");
return PR_FALSE;
}
XMLT_LOG(mozXMLTermSession::IsTextNode,90,("\n"));
PRUint16 nodeType;
aNode->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::TEXT_NODE)
return PR_TRUE;
return PR_FALSE;
}
/** Checks if node is a text, span, or anchor node
* (i.e., allowed inside a PRE element)
* @param aNode DOM node to be checked
* @return true if node is a text, span or anchor node
*/
PRBool mozXMLTermSession::IsPREInlineNode(nsIDOMNode* aNode)
{
nsresult result;
PRBool isPREInlineNode = false;
nsCOMPtr<nsIDOMText> domText = do_QueryInterface(aNode);
if (domText) {
isPREInlineNode = true;
} else {
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aNode);
if (domElement) {
nsAutoString tagName = "";
result = domElement->GetTagName(tagName);
if (NS_SUCCEEDED(result)) {
isPREInlineNode = tagName.EqualsIgnoreCase("span") ||
tagName.EqualsIgnoreCase("a");
}
}
}
return isPREInlineNode;
}
/** Serializes DOM node and its content as an HTML fragment string
* @param aNode DOM node to be serialized
* @param indentString indentation prefix string
* @param htmlString (output) serialized HTML fragment
* @param deepContent if true, serialize children of node as well
* (defaults to false)
* @param insidePREnode set to true if aNode is embedded inside a PRE node
* control formatting
* (defaults to false)
*/
NS_IMETHODIMP mozXMLTermSession::ToHTMLString(nsIDOMNode* aNode,
nsString& indentString,
nsString& htmlString,
PRBool deepContent,
PRBool insidePRENode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::ToHTMLString,80,("\n"));
nsAutoString newIndentString (indentString);
newIndentString += " ";
htmlString = "";
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(aNode) );
if (domText) {
// Text node
nsCOMPtr<nsIHTMLContent> htmlContent ( do_QueryInterface(aNode) );
if (htmlContent) {
htmlContent->ToHTMLString(htmlString);
XMLT_LOG(mozXMLTermSession::ToHTMLString,82,("htmlContent\n"));
} else {
domText->GetData(htmlString);
}
} else {
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aNode);
if (domElement) {
nsAutoString tagName = "";
domElement->GetTagName(tagName);
if (!insidePRENode) {
htmlString += indentString;
}
htmlString += "<";
htmlString += tagName;
PRBool isPRENode = tagName.EqualsIgnoreCase("pre");
nsCOMPtr<nsIDOMNamedNodeMap> namedNodeMap(nsnull);
result = aNode->GetAttributes(getter_AddRefs(namedNodeMap));
if (NS_SUCCEEDED(result) && namedNodeMap) {
// Print all attributes
PRUint32 nodeCount, j;
result = namedNodeMap->GetLength(&nodeCount);
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDOMNode> attrNode;
for (j=0; j<nodeCount; j++) {
result = namedNodeMap->Item(j, getter_AddRefs(attrNode));
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDOMAttr> attr = do_QueryInterface(attrNode);
if (attr) {
nsAutoString attrName = "";
nsAutoString attrValue = "";
result = attr->GetName(attrName);
if (NS_SUCCEEDED(result)) {
htmlString += " ";
htmlString += attrName;
}
result = attr->GetValue(attrValue);
if (NS_SUCCEEDED(result) && (attrName.Length() > 0)) {
htmlString += "=\"";
htmlString += attrValue;
htmlString += "\"";
}
}
}
}
}
}
if (!deepContent) {
htmlString += ">";
} else {
// Iterate over all child nodes to generate deep content
nsCOMPtr<nsIDOMNode> child;
result = aNode->GetFirstChild(getter_AddRefs(child));
nsAutoString htmlInner ("");
while (child) {
nsAutoString innerString;
ToHTMLString(child, newIndentString, innerString, deepContent,
isPRENode);
htmlInner += innerString;
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
if (NS_FAILED(result))
break;
}
if (htmlInner.Length() > 0) {
if (insidePRENode)
htmlString += "\n>";
else
htmlString += ">\n";
htmlString += htmlInner;
if (!insidePRENode)
htmlString += indentString;
} else {
htmlString += ">";
}
htmlString += "</";
htmlString += tagName;
if (insidePRENode)
htmlString += "\n";
htmlString += ">";
if (!insidePRENode)
htmlString += "\n";
}
}
}
return NS_OK;
}
/** Implements the "tree:" meta command to traverse DOM tree
* @param fileStream file stream for displaying tree traversal output
* @param rootNode root node of DOM tree
* @param currentNode current node for traversal
* @param treeActionCode traversal action type
*/
void mozXMLTermSession::TraverseDOMTree(FILE* fileStream,
nsIDOMNode* rootNode,
nsCOMPtr<nsIDOMNode>& currentNode,
TreeActionCode treeActionCode)
{
static const PRInt32 NODE_TYPE_NAMES = 12;
static const char* const nodeTypeNames[NODE_TYPE_NAMES] = {
"ELEMENT",
"ATTRIBUTE",
"TEXT",
"CDATA_SECTION",
"ENTITY_REFERENCE",
"ENTITY_NODE",
"PROCESSING_INSTRUCTION",
"COMMENT",
"DOCUMENT",
"DOCUMENT_TYPE",
"DOCUMENT_FRAGMENT",
"NOTATION_NODE"
};
static const PRInt32 PRINT_ATTRIBUTE_NAMES = 2;
static const char* const printAttributeNames[PRINT_ATTRIBUTE_NAMES] = {
"class",
"id"
};
nsresult result = NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> moveNode(nsnull);
nsCOMPtr<nsIDOMNamedNodeMap> namedNodeMap(nsnull);
switch (treeActionCode) {
case TREE_MOVE_UP:
if (currentNode.get() != rootNode) {
result = currentNode->GetParentNode(getter_AddRefs(moveNode));
if (NS_SUCCEEDED(result) && moveNode) {
// Move up to parent node
currentNode = moveNode;
}
} else {
fprintf(fileStream, "TraverseDOMTree: already at the root node \n");
}
break;
case TREE_MOVE_DOWN:
result = currentNode->GetFirstChild(getter_AddRefs(moveNode));
if (NS_SUCCEEDED(result) && moveNode) {
// Move down to child node
currentNode = moveNode;
} else {
fprintf(fileStream, "TraverseDOMTree: already at a leaf node\n");
}
break;
case TREE_MOVE_LEFT:
if (currentNode.get() != rootNode) {
result = currentNode->GetPreviousSibling(getter_AddRefs(moveNode));
if (NS_SUCCEEDED(result) && moveNode) {
// Move to previous sibling node
currentNode = moveNode;
} else {
fprintf(fileStream, "TraverseDOMTree: already at leftmost node\n");
}
} else {
fprintf(fileStream, "TraverseDOMTree: already at the root node \n");
}
break;
case TREE_MOVE_RIGHT:
if (currentNode.get() != rootNode) {
result = currentNode->GetNextSibling(getter_AddRefs(moveNode));
if (NS_SUCCEEDED(result) && moveNode) {
// Move to next sibling node
currentNode = moveNode;
} else {
fprintf(fileStream, "TraverseDOMTree: already at rightmost node\n");
}
} else {
fprintf(fileStream, "TraverseDOMTree: already at the root node \n");
}
break;
case TREE_PRINT_ATTS:
case TREE_PRINT_HTML:
if (true) {
nsAutoString indentString ("");
nsAutoString htmlString;
ToHTMLString(currentNode, indentString, htmlString,
(PRBool) (treeActionCode == TREE_PRINT_HTML) );
fprintf(fileStream, "%s:\n", treeActionNames[treeActionCode-1]);
char* htmlCString = htmlString.ToNewCString();
fprintf(fileStream, "%s", htmlCString);
nsCRT::free(htmlCString);
fprintf(fileStream, "\n");
}
break;
default:
fprintf(fileStream, "mozXMLTermSession::TraverseDOMTree - unknown action %d\n",
treeActionCode);
}
if (NS_SUCCEEDED(result) && moveNode) {
PRUint16 nodeType = 0;
moveNode->GetNodeType(&nodeType);
fprintf(fileStream, "%s%s: ", treeActionNames[treeActionCode-1],
nodeTypeNames[nodeType-1]);
nsCOMPtr<nsIDOMElement> domElement;
domElement = do_QueryInterface(moveNode);
if (domElement) {
nsAutoString tagName = "";
result = domElement->GetTagName(tagName);
if (NS_SUCCEEDED(result)) {
char* tagCString = tagName.ToNewCString();
fprintf(fileStream, "%s", tagCString);
nsCRT::free(tagCString);
// Print selected attribute values
int j;
for (j=0; j<PRINT_ATTRIBUTE_NAMES; j++) {
nsAutoString attName (printAttributeNames[j]);
nsAutoString attValue = "";
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && (attValue.Length() > 0)) {
// Print attribute value
char* tagCString2 = attValue.ToNewCString();
fprintf(fileStream, " %s=%s", printAttributeNames[j], tagCString2);
nsCRT::free(tagCString2);
}
}
}
}
fprintf(fileStream, "\n");
}
}