Mozilla/mozilla/extensions/xmlterm/base/mozXMLTermSession.cpp
svn%xmlterm.org f8cbfe6e57 xmlterm changes only (not part of the default build).
Improved full screen implementation; added bold and underline styles. Centralized UI event handling. Added checks to limit number of output lines displayed. Added capability to interrupt hung output data streams. Nearing full backwards compatibility with xterm.


git-svn-id: svn://10.0.0.236/trunk@62265 18797224-902f-48f8-a5cc-f745e15eee43
2000-03-06 15:16:48 +00:00

4215 lines
120 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 "nsICaret.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 "nsFont.h"
#include "nsIFontMetrics.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",
"dblclick"
};
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(24),
mScreenCols(80),
mTopScrollRow(23),
mBotScrollRow(0),
mRestoreInputEcho(false),
mNeedsResizing(false),
mShellPrompt(""),
mPromptHTML(""),
mFragmentBuffer("")
{
}
mozXMLTermSession::~mozXMLTermSession()
{
if (mInitialized) {
Finalize();
}
}
// Initialize XMLTermSession
NS_IMETHODIMP mozXMLTermSession::Init(mozIXMLTerminal* aXMLTerminal,
nsIPresShell* aPresShell,
nsIDOMDocument* aDOMDocument)
{
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
nsresult result = NS_OK;
// Show the caret
nsCOMPtr<nsICaret> caret;
if (NS_SUCCEEDED(mPresShell->GetCaret(getter_AddRefs(caret)))) {
caret->SetCaretVisible(PR_TRUE);
caret->SetCaretReadOnly(PR_FALSE);
}
mPresShell->SetCaretEnabled(PR_TRUE);
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;
}
/** Sets XMLTerm flag to indicate XMLTerm needs to be resized
*/
NS_IMETHODIMP mozXMLTermSession::NeedsResizing(void)
{
XMLT_LOG(mozXMLTermSession::NeedsResizing,0,("\n"));
mNeedsResizing = true;
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;
// Get presentation context
nsCOMPtr<nsIPresContext> presContext;
result = mPresShell->GetPresContext( getter_AddRefs(presContext) );
if (NS_FAILED(result))
return result;
// Get the default fixed pitch font
nsFont defaultFixedFont("dummyfont", NS_FONT_STYLE_NORMAL,
NS_FONT_VARIANT_NORMAL,
NS_FONT_WEIGHT_NORMAL,
NS_FONT_DECORATION_NONE, 16);
result = presContext->GetDefaultFixedFont(defaultFixedFont);
if (NS_FAILED(result))
return result;
// Get metrics for fixed font
nsCOMPtr<nsIFontMetrics> fontMetrics;
result = presContext->GetMetricsFor(defaultFixedFont,
getter_AddRefs(fontMetrics));
if (NS_FAILED(result) || !fontMetrics)
return result;
// Get font height (includes leading?)
nscoord fontHeight, fontWidth;
result = fontMetrics->GetHeight(fontHeight);
result = fontMetrics->GetMaxAdvance(fontWidth);
// Determine docshell size in twips
nsRect shellArea;
result = presContext->GetVisibleArea(shellArea);
if (NS_FAILED(result))
return result;
// Determine twips to pixels conversion factor
float pixelScale, frameHeight, frameWidth, xdel, ydel;
presContext->GetTwipsToPixels(&pixelScale);
// Convert dimensions to pixels
frameHeight = pixelScale * shellArea.height;
frameWidth = pixelScale * shellArea.width;
xdel = pixelScale * fontWidth;
ydel = pixelScale * fontHeight + 2;
// Determine number of rows/columns
mScreenRows = (int) ((frameHeight-44) / ydel);
mScreenCols = (int) ((frameWidth-20) / xdel);
if (mScreenRows < 1) mScreenRows = 1;
if (mScreenCols < 1) mScreenCols = 1;
mTopScrollRow = mScreenRows - 1;
mBotScrollRow = 0;
XMLT_LOG(mozXMLTermSession::Resize,0,
("Resizing XMLterm, xdel=%e, ydel=%e, rows=%d, cols=%d\n",
xdel, ydel, 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
*/
NS_IMETHODIMP mozXMLTermSession::Preprocess(const nsString& aString,
PRBool& consumed)
{
consumed = 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;
}
}
}
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;
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;
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))
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
nsCOMPtr<nsICaret> caret;
if (NS_SUCCEEDED(mPresShell->GetCaret(getter_AddRefs(caret)))) {
caret->SetCaretVisible(PR_TRUE);
caret->SetCaretReadOnly(PR_FALSE);
}
mPresShell->SetCaretEnabled(PR_TRUE);
}
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);
} 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;
}
// Resize XMLTerm before command output, if request has been made
if (mNeedsResizing) {
mNeedsResizing = false;
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)) {
// Close LineTerm
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Closing LineTerm, result=%d\n", result));
lineTermAux->CloseAux();
return result;
}
if (flushOutput) {
// Flush output, splitting off incomplete line
result = FlushOutput(SPLIT_INCOMPLETE_FLUSH);
if (mEntryHasOutput)
PositionOutputCursor(lineTermAux);
result = mPresShell->ScrollSelectionIntoView(SELECTION_NORMAL,
SELECTION_FOCUS_REGION);
}
// Scroll frame (ignore result)
ScrollToBottomLeft();
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 {
nsCOMPtr<nsIDOMNode> promptTextNode;
result = mPromptSpanNode->GetFirstChild(getter_AddRefs(promptTextNode));
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDOMText> domText (do_QueryInterface(promptTextNode));
if (domText) {
PRUint32 promptLength;
result = domText->GetLength(&promptLength);
if (NS_SUCCEEDED(result)) {
XMLT_LOG(mozXMLTermSession::DisplayInput,72,
("promptLength=%d\n", promptLength));
result = selection->Collapse(promptTextNode, promptLength);
}
}
}
}
#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;
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 {
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/ondblclick 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", "dblclick", ...)
*/
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]);
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";
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] ));
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 > 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(-9999,9999);
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;
}
/** 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) {
// 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;
} 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+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, ondblclick, ...) 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");
}
}