gecko-dev/extensions/xmlterm/base/mozXMLTermSession.cpp
2004-08-03 13:46:52 +00:00

4621 lines
136 KiB
C++

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is XMLterm.
*
* The Initial Developer of the Original Code is
* Ramalingam Saravanan.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// mozXMLTermSession.cpp: implementation of mozXMLTermSession class
#include "nscore.h"
#include "prlog.h"
#include "nscore.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsCRT.h"
#include "nsIComponentManager.h"
#include "nsMemory.h"
#include "nsIDocumentViewer.h"
#include "nsILocalFile.h"
#include "nsNetUtil.h"
#include "nsITextContent.h"
#include "nsIDOMElement.h"
#include "nsISelection.h"
#include "nsIDOMText.h"
#include "nsIDOMAttr.h"
#include "nsIDOMNamedNodeMap.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIDOMNSRange.h"
#include "nsIViewManager.h"
#include "nsIScrollableView.h"
#include "nsIHTMLContent.h"
#include "mozXMLT.h"
#include "mozIXMLTerminal.h"
#include "mozIXMLTermStream.h"
#include "mozXMLTermUtils.h"
#include "mozXMLTermSession.h"
#include "nsISelectionController.h"
#include "nsReadableUtils.h"
#include "nsIDocument.h"
/////////////////////////////////////////////////////////////////////////
// mozXMLTermSession definition
/////////////////////////////////////////////////////////////////////////
static const char* kWhitespace=" \b\t\r\n";
static const PRUnichar kNBSP = 160;
const char* const mozXMLTermSession::sessionElementNames[] = {
"session",
"entry",
"input",
"output",
"prompt",
"command",
"stdin",
"stdout",
"stderr",
"mixed",
"warning"
};
// Should HTML event names should always be in lower case for DOM to work?
const char* const mozXMLTermSession::sessionEventNames[] = {
"click"
};
const char* const mozXMLTermSession::metaCommandNames[] = {
"",
"default",
"http",
"js",
"tree",
"ls"
};
const char* const mozXMLTermSession::fileTypeNames[] = {
"plainfile",
"directory",
"executable"
};
const char* const mozXMLTermSession::treeActionNames[] = {
"^",
"v",
"<",
">",
"A",
"H"
};
mozXMLTermSession::mozXMLTermSession() :
mInitialized(PR_FALSE),
mXMLTerminal(nsnull),
mBodyNode(nsnull),
mMenusNode(nsnull),
mSessionNode(nsnull),
mCurrentDebugNode(nsnull),
mStartEntryNode(nsnull),
mCurrentEntryNode(nsnull),
mMaxHistory(20),
mStartEntryNumber(0),
mCurrentEntryNumber(0),
mEntryHasOutput(PR_FALSE),
mPromptTextNode(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(PR_FALSE),
mEntryOutputLines(0),
mPreTextBufferLines(0),
mPreTextIncomplete(),
mPreTextBuffered(),
mPreTextDisplayed(),
mScreenNode(nsnull),
mScreenRows(0),
mScreenCols(0),
mTopScrollRow(0),
mBotScrollRow(0),
mRestoreInputEcho(PR_FALSE),
mCountExportHTML(0),
mLastExportHTML(),
mShellPrompt(),
mPromptHTML(),
mFragmentBuffer()
{
}
mozXMLTermSession::~mozXMLTermSession()
{
Finalize();
}
// Initialize XMLTermSession
NS_IMETHODIMP mozXMLTermSession::Init(mozIXMLTerminal* aXMLTerminal,
nsIPresShell* aPresShell,
nsIDOMDocument* aDOMDocument,
PRInt32 nRows, PRInt32 nCols)
{
nsresult result = NS_OK;
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
mInitialized = PR_TRUE;
mScreenRows = nRows;
mScreenCols = nCols;
mTopScrollRow = mScreenRows - 1;
mBotScrollRow = 0;
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMHTMLDocument> vDOMHTMLDocument
(do_QueryInterface(domDoc));
if (!vDOMHTMLDocument)
return NS_ERROR_FAILURE;
// Locate document body node
nsCOMPtr<nsIDOMNodeList> nodeList;
nsAutoString bodyTag;
bodyTag.AssignLiteral("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;
nsCOMPtr<nsIDOMElement> menusElement;
nsAutoString menusID( NS_LITERAL_STRING("menus") );
result = vDOMHTMLDocument->GetElementById(menusID,
getter_AddRefs(menusElement));
if (NS_SUCCEEDED(result) && menusElement) {
mMenusNode = do_QueryInterface(menusElement);
}
// Use body node as session node by default
mSessionNode = mBodyNode;
nsCOMPtr<nsIDOMElement> sessionElement;
nsAutoString sessionID;
sessionID.AssignASCII(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");
result = AppendOutput(prefaceText, EmptyString(), PR_TRUE);
#endif
XMLT_LOG(mozXMLTermSession::Init,31,("exiting\n"));
return result;
}
// De-initialize XMLTermSession
NS_IMETHODIMP mozXMLTermSession::Finalize(void)
{
if (!mInitialized)
return NS_OK;
XMLT_LOG(mozXMLTermSession::Finalize,30,("\n"));
mInitialized = PR_FALSE;
mScreenNode = nsnull;
mOutputBlockNode = nsnull;
mOutputDisplayNode = nsnull;
mOutputTextNode = nsnull;
mXMLTermStream = nsnull;
mPromptTextNode = nsnull;
mCommandSpanNode = nsnull;
mInputTextNode = nsnull;
mStartEntryNode = nsnull;
mCurrentEntryNode = nsnull;
mBodyNode = nsnull;
mMenusNode = nsnull;
mSessionNode = nsnull;
mCurrentDebugNode = nsnull;
mXMLTerminal = nsnull;
XMLT_LOG(mozXMLTermSession::Finalize,32,("END\n"));
return NS_OK;
}
/** Resizes XMLterm to match a resized window.
* @param lineTermAux LineTermAux object to be resized (may be null)
*/
NS_IMETHODIMP mozXMLTermSession::Resize(mozILineTermAux* lineTermAux)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::Resize,70,("\n"));
// Determine current screen dimensions
PRInt32 nRows, nCols, xPixels, yPixels;
result = mXMLTerminal->ScreenSize(&nRows, &nCols, &xPixels, &yPixels);
if (NS_FAILED(result))
return result;
// If dimensions haven't changed, do nothing
if ((nRows == mScreenRows) && (nCols == mScreenCols))
return NS_OK;
mScreenRows = nRows;
mScreenCols = nCols;
mTopScrollRow = mScreenRows - 1;
mBotScrollRow = 0;
XMLT_LOG(mozXMLTermSession::Resize,72,
("Resizing XMLterm, nRows=%d, nCols=%d\n", mScreenRows, mScreenCols));
if (lineTermAux) {
// Resize associated LineTerm
result = lineTermAux->ResizeAux(mScreenRows, mScreenCols);
if (NS_FAILED(result))
return result;
}
return NS_OK;
}
/** Preprocesses user input before it is transmitted to LineTerm
* @param aString (inout) input data to be preprocessed
* @param consumed (output) PR_TRUE if input data has been consumed
* @param checkSize (output) PR_TRUE if terminal size needs to be checked
*/
NS_IMETHODIMP mozXMLTermSession::Preprocess(const nsString& aString,
PRBool& consumed,
PRBool& checkSize)
{
XMLT_LOG(mozXMLTermSession::Preprocess,70,("\n"));
consumed = PR_FALSE;
checkSize = PR_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 = PR_TRUE;
switch (uch) {
case U_CTL_B:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_LEFT);
break;
case U_CTL_F:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_RIGHT);
break;
case U_CTL_N:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_DOWN);
break;
case U_CTL_P:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_MOVE_UP);
break;
case U_A_CHAR:
case U_a_CHAR:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_PRINT_ATTS);
break;
case U_H_CHAR:
case U_h_CHAR:
TraverseDOMTree(stderr, mBodyNode, mCurrentDebugNode,
TREE_PRINT_HTML);
break;
case U_Q_CHAR:
case U_q_CHAR:
case U_CTL_C:
// End of keyboard command sequence; reset debug node to session node
mCurrentDebugNode = mSessionNode;
mMetaCommandType = NO_META_COMMAND;
break;
default:
break;
}
}
} else {
if ((mScreenNode == nsnull) &&
(aString.FindCharInSet("\r\n\017") >= 0)) {
// C-Return or Newline or Control-O found in string; not screen mode;
// resize terminal, if need be
checkSize = PR_TRUE;
XMLT_LOG(mozXMLTermSession::Preprocess,72,("checkSize\n"));
}
}
return NS_OK;
}
/** Reads all available data from LineTerm and displays it;
* returns when no more data is available.
* @param lineTermAux LineTermAux object to read data from
* @param processedData (output) PR_TRUE if any data was processed
*/
NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux,
PRBool& processedData)
{
PRInt32 opcodes, opvals, buf_row, buf_col;
PRUnichar *buf_str, *buf_style;
PRBool newline, errorFlag, streamData, screenData;
nsAutoString bufString, bufStyle;
nsAutoString abortCode;
abortCode.SetLength(0);
XMLT_LOG(mozXMLTermSession::ReadAll,60,("\n"));
processedData = PR_FALSE;
if (lineTermAux == nsnull)
return NS_ERROR_FAILURE;
nsresult result = NS_OK;
PRBool flushOutput = PR_FALSE;
PRBool metaNextCommand = PR_FALSE;
// NOTE: Do not execute return statements within this loop ;
// always break out of the loop after setting result to an error value,
// allowing cleanup processing on error
for (;;) {
// NOTE: Remember to de-allocate buf_str and buf_style
// using nsMemory::Free, if opcodes != 0
result = lineTermAux->ReadAux(&opcodes, &opvals, &buf_row, &buf_col,
&buf_str, &buf_style);
if (NS_FAILED(result)) {
abortCode.AssignLiteral("lineTermReadAux");
break;
}
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("opcodes=0x%x,mOutputType=%d,mEntryHasOutput=%d\n",
opcodes, mOutputType, mEntryHasOutput));
if (opcodes == 0) break;
processedData = PR_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 nsMemory::Free
nsMemory::Free(buf_str);
nsMemory::Free(buf_style);
char* temCString = ToNewCString(bufString);
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(PR_FALSE);
mRestoreInputEcho = PR_TRUE;
}
if (!screenData && (mOutputType == SCREEN_OUTPUT)) {
// Terminate screen mode
mOutputType = LINE_OUTPUT;
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Terminating screen mode\n"));
// Uncollapse non-screen stuff
nsAutoString attName(NS_LITERAL_STRING("xmlt-block-collapsed"));
nsCOMPtr<nsIDOMElement> menusElement = do_QueryInterface(mMenusNode);
if (NS_SUCCEEDED(result) && menusElement) {
menusElement->RemoveAttribute(attName);
}
nsCOMPtr<nsIDOMElement> sessionElement = do_QueryInterface(mSessionNode);
if (sessionElement) {
sessionElement->RemoveAttribute(attName);
}
// Delete screen element
nsCOMPtr<nsIDOMNode> resultNode;
mBodyNode->RemoveChild(mScreenNode, getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
mScreenNode = nsnull;
if (mRestoreInputEcho) {
lineTermAux->SetEchoFlag(PR_TRUE);
mRestoreInputEcho = PR_FALSE;
}
// Show the caret
// WORKAROUND for some unknown bug in the full screen implementation.
// Without this, if you delete a line using "vi" and save the file,
// the cursor suddenly disappears
mXMLTerminal->ShowCaret();
}
if (streamData) {
// Process stream data
if (mOutputType != STREAM_OUTPUT) {
mOutputType = STREAM_OUTPUT;
// Disable input echo
lineTermAux->SetEchoFlag(PR_FALSE);
mRestoreInputEcho = PR_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.AssignLiteral("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.AssignLiteral("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.SetLength(0);
result = ProcessOutput(bufString, bufStyle, PR_FALSE, PR_TRUE);
if (NS_FAILED(result))
break;
if (newline) {
if (!mEntryHasOutput) {
// Start of command output
mEntryHasOutput = PR_TRUE;
}
if (errorFlag) {
mOutputMarkupType = INCOMPLETE_FRAGMENT;
}
// Break stream output display
result = BreakOutput(PR_TRUE);
if (NS_FAILED(result))
break;
mOutputType = LINE_OUTPUT;
flushOutput = PR_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 = mBodyNode->RemoveChild(mScreenNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
mScreenNode = nsnull;
// Create new screen element
result = NewScreen();
if (NS_FAILED(result))
break;
} else if (opcodes & LTERM_INSERT_CODE) {
// Insert rows
PRInt32 row;
nsCOMPtr<nsIDOMNode> rowNode, resultNode;
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Insert rows, opvals=%d, buf_row=%d\n",
opvals, buf_row));
if (opvals > 0) {
// Delete row elements below
for (row=0; row < opvals; row++) {
result = GetRow(mBotScrollRow+opvals-1, getter_AddRefs(rowNode));
if (NS_FAILED(result) || !rowNode)
break;
result = mScreenNode->RemoveChild(rowNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
}
if (NS_FAILED(result))
break;
// Insert individual row elements above
if (buf_row < opvals) {
rowNode = nsnull;
} else {
result = GetRow(buf_row, getter_AddRefs(rowNode));
if (NS_FAILED(result))
break;
}
for (row=0; row < opvals; row++)
NewRow(rowNode, getter_AddRefs(resultNode));
}
} else if (opcodes & LTERM_DELETE_CODE) {
// Delete rows
PRInt32 row;
nsCOMPtr<nsIDOMNode> rowNode, resultNode;
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Delete rows, opvals=%d, buf_row=%d\n",
opvals, buf_row));
if (opvals > 0) {
// Delete row elements below
for (row=0; row < opvals; row++) {
result = GetRow(buf_row, getter_AddRefs(rowNode));
if (NS_FAILED(result) || !rowNode)
break;
result = mScreenNode->RemoveChild(rowNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
break;
}
if (NS_FAILED(result))
break;
// Insert individual row elements above
if (mBotScrollRow == 0) {
rowNode = nsnull;
} else {
result = GetRow(mBotScrollRow+opvals-1, getter_AddRefs(rowNode));
if (NS_FAILED(result))
break;
}
for (row=0; row < opvals; row++)
NewRow(rowNode, getter_AddRefs(resultNode));
}
} else if (opcodes & LTERM_SCROLL_CODE) {
// Set scrolling region
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Set scrolling region, opvals=%d, buf_row=%d\n",
opvals, buf_row));
mTopScrollRow = opvals;
mBotScrollRow = buf_row;
} else if (opcodes & LTERM_OUTPUT_CODE) {
// Display row
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Display buf_row=%d\n",
buf_row));
result = DisplayRow(bufString, bufStyle, buf_row);
if (NS_FAILED(result))
break;
}
// Determine cursor position and position cursor
PRInt32 cursorRow = 0;
PRInt32 cursorCol = 0;
result = lineTermAux->GetCursorRow(&cursorRow);
result = lineTermAux->GetCursorColumn(&cursorCol);
XMLT_LOG(mozXMLTermSession::ReadAll,62, ("cursorRow=%d, cursorCol=%d\n",
cursorRow, cursorCol));
result = PositionScreenCursor(cursorRow, cursorCol);
flushOutput = PR_FALSE;
} else {
// Process line data
PRBool promptLine, inputLine, metaCommand, completionRequested;
flushOutput = PR_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;
promptStr.SetLength(0);
if (promptLine) {
// Count prompt characters
const PRUnichar *styleVals = bufStyle.get();
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 = PR_FALSE;
} else {
// No meta command; enable input echo
mMetaCommandType = NO_META_COMMAND;
if (mRestoreInputEcho) {
lineTermAux->SetEchoFlag(PR_TRUE);
mRestoreInputEcho = PR_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.EqualsASCII(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;
metaCommandOutput.SetLength(0);
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
break;
switch (mMetaCommandType) {
case DEFAULT_META_COMMAND:
{
// Construct Javascript command to handle default meta command
nsAutoString JSCommand;
JSCommand.AssignLiteral("MetaDefault(\"");
JSCommand.Append(commandArgs);
JSCommand.Append(NS_LITERAL_STRING("\");"));
// Execute JavaScript command
result = mozXMLTermUtils::ExecuteScript(domDoc,
JSCommand,
metaCommandOutput);
nsCAutoString cstrout;
if (NS_SUCCEEDED(result))
CopyUCS2toASCII(metaCommandOutput, cstrout);
else
cstrout = "Error in displaying URL\n";
XMLT_LOG(mozXMLTermSession::ReadAll,63,
("DEFAULT_META output=%s\n", cstrout.get()));
}
break;
case HTTP_META_COMMAND:
{
// Display URL using IFRAME
nsAutoString url;
url.AssignLiteral("http:");
url.Append(commandArgs);
nsAutoString width;
width.AssignLiteral("100%");
nsAutoString height;
height.AssignLiteral("100");
result = NewIFrame(mOutputBlockNode, mCurrentEntryNumber,
2, url, width, height);
if (NS_FAILED(result))
metaCommandOutput.AssignLiteral("Error in displaying URL\n");
}
break;
case JS_META_COMMAND:
{
// Execute JavaScript command
result = mozXMLTermUtils::ExecuteScript(domDoc,
commandArgs,
metaCommandOutput);
nsCAutoString cstrout;
if (NS_SUCCEEDED(result))
CopyUCS2toASCII(metaCommandOutput, cstrout);
else
cstrout = "Error in executing JavaScript command\n";
XMLT_LOG(mozXMLTermSession::ReadAll,63,
("JS output=%s\n", cstrout.get()));
}
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(PR_FALSE);
nsAutoString lsCommand;
lsCommand.SetLength(0);
if (!commandArgs.IsEmpty()) {
lsCommand.AppendLiteral("cd ");
lsCommand.Append(commandArgs);
lsCommand.AppendLiteral(";");
}
lsCommand.AppendLiteral("ls -dF `pwd`/*\n");
//mXMLTerminal->SendText(lsCommand);
/* Set flag to recognize transmitted command */
metaNextCommand = PR_TRUE;
mRestoreInputEcho = PR_TRUE;
}
break;
default:
break;
}
if ((mMetaCommandType == DEFAULT_META_COMMAND) ||
(mMetaCommandType == JS_META_COMMAND)) {
// Display metacommand output
mEntryHasOutput = PR_TRUE;
XMLT_LOG(mozXMLTermSession::ReadAll,62,("metaCommandOutput\n"));
// Ignore the string "false", if that's the only output
if (metaCommandOutput.EqualsLiteral("false"))
metaCommandOutput.SetLength(0);
// Check metacommand output for markup (secure)
result = AutoDetectMarkup(metaCommandOutput, PR_TRUE, PR_TRUE);
if (NS_FAILED(result))
break;
nsAutoString nullStyle;
nullStyle.SetLength(0);
result = ProcessOutput(metaCommandOutput, nullStyle, PR_TRUE,
mOutputMarkupType != PLAIN_TEXT);
if (NS_FAILED(result))
break;
// Break metacommand output display
result = BreakOutput(PR_FALSE);
}
// Reset newline flag
newline = PR_FALSE;
}
// Clear the meta command from the string nuffer
bufString.SetLength(0);
bufStyle.SetLength(0);
}
if (promptLine) {
// Prompt line
if (mEntryHasOutput) {
// Break previous output display
result = BreakOutput(PR_FALSE);
// Create new entry block
result = NewEntry(promptStr);
if (NS_FAILED(result))
break;
if (mCurrentEntryNumber == mStartEntryNumber) {
// First entry; resize terminal
result = Resize(lineTermAux);
if (NS_FAILED(result))
break;
}
}
// Display input and position cursor
PRInt32 cursorCol = 0;
result = lineTermAux->GetCursorColumn(&cursorCol);
// Remove prompt offset
cursorCol -= promptLength;
if (cursorCol < 0) cursorCol = 0;
XMLT_LOG(mozXMLTermSession::ReadAll,62,("cursorCol=%d\n", cursorCol));
result = DisplayInput(bufString, bufStyle, cursorCol);
if (NS_FAILED(result))
break;
if (newline) {
// Start of command output
// (this is needed to properly handle commands with no output!)
mEntryHasOutput = PR_TRUE;
mFirstOutputLine = PR_TRUE;
}
} else {
// Not prompt line
if (!mEntryHasOutput) {
// Start of command output
mEntryHasOutput = PR_TRUE;
mFirstOutputLine = PR_TRUE;
}
if (newline) {
// Complete line; check for markup (insecure)
result = AutoDetectMarkup(bufString, mFirstOutputLine, PR_FALSE);
if (NS_FAILED(result))
break;
// Not first output line anymore
mFirstOutputLine = PR_FALSE;
}
if (mOutputMarkupType == PLAIN_TEXT) {
// Display plain text output
result = ProcessOutput(bufString, bufStyle, newline, PR_FALSE);
if (NS_FAILED(result))
break;
} else if (newline) {
// Process autodetected stream output (complete lines only)
bufStyle.SetLength(0);
result = ProcessOutput(bufString, bufStyle, PR_TRUE, PR_TRUE);
if (NS_FAILED(result))
break;
}
}
}
}
if (NS_FAILED(result)) {
// Error processing; close LineTerm
XMLT_LOG(mozXMLTermSession::ReadAll,62,
("Aborting on error, result=0x%x\n", result));
Abort(lineTermAux, abortCode);
return result;
}
if (flushOutput) {
// Flush output, splitting off incomplete line
FlushOutput(SPLIT_INCOMPLETE_FLUSH);
if (mEntryHasOutput)
PositionOutputCursor(lineTermAux);
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return NS_ERROR_FAILURE;
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_TRUE);
}
// Show caret
mXMLTerminal->ShowCaret();
// Scroll frame (ignore result)
ScrollToBottomLeft();
return NS_OK;
}
/** Exports HTML to file, with META REFRESH, if refreshSeconds is non-zero.
* Nothing is done if display has not changed since last export, unless
* forceExport is true. Returns true if export actually takes place.
* If filename is a null string, HTML is written to STDERR.
*/
NS_IMETHODIMP mozXMLTermSession::ExportHTML(const PRUnichar* aFilename,
PRInt32 permissions,
const PRUnichar* style,
PRUint32 refreshSeconds,
PRBool forceExport,
PRBool* exported)
{
nsresult result;
if (!aFilename || !exported)
return NS_ERROR_NULL_POINTER;
*exported = PR_FALSE;
if (forceExport)
mLastExportHTML.SetLength(0);
nsAutoString indentString; indentString.SetLength(0);
nsAutoString htmlString;
result = ToHTMLString(mBodyNode, indentString, htmlString,
PR_TRUE, PR_FALSE );
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
if (htmlString.Equals(mLastExportHTML))
return NS_OK;
mLastExportHTML.Assign( htmlString );
mCountExportHTML++;
nsAutoString filename( aFilename );
if (filename.IsEmpty()) {
// Write to STDERR
char* htmlCString = ToNewCString(htmlString);
fprintf(stderr, "mozXMLTermSession::ExportHTML:\n%s\n\n", htmlCString);
nsCRT::free(htmlCString);
*exported = PR_TRUE;
return NS_OK;
}
// Copy HTML to local file
nsCOMPtr<nsILocalFile> localFile = do_CreateInstance( NS_LOCAL_FILE_CONTRACTID, &result);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
XMLT_LOG(mozXMLTermSession::ExportHTML,0,
("Exporting %d\n", mCountExportHTML));
result = localFile->InitWithPath(filename);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
PRInt32 ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
nsCOMPtr<nsIOutputStream> outStream;
result = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
localFile, ioFlags, permissions);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
PRUint32 writeCount;
nsCAutoString cString( "<html>\n<head>\n" );
if (refreshSeconds > 0) {
cString.Append("<META HTTP-EQUIV='refresh' content='");
cString.AppendInt(refreshSeconds);
cString.Append("'>");
}
cString.Append("<title>xmlterm page</title>\n");
cString.Append("<link title='defaultstyle' rel='stylesheet' type='text/css' href='xmlt.css'>\n");
if (style) {
cString.Append("<style type='text/css'>\n");
AppendUTF16toUTF8(style, cString);
cString.Append("</style>\n");
}
cString.Append("<script language='JavaScript'>var exportCount=");
cString.AppendInt(mCountExportHTML);
cString.Append(";</script>\n");
cString.Append("<script language='JavaScript' src='xmlt.js'></script>\n</head>");
AppendUTF16toUTF8(htmlString, cString);
cString.Append("</html>\n");
result = outStream->Write(cString.get(), cString.Length(),
&writeCount);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
result = outStream->Flush();
result = outStream->Close();
*exported = PR_TRUE;
return NS_OK;
}
/** Aborts session by closing LineTerm and displays an error message
* @param lineTermAux LineTermAux object to be closed
* @param abortCode abort code string to dbe displayed
*/
NS_IMETHODIMP mozXMLTermSession::Abort(mozILineTermAux* lineTermAux,
nsString& abortCode)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::Abort,70,
("Aborting session; closing LineTerm\n"));
// Close LineTerm
lineTermAux->CloseAux();
// Display error message using DIV node
nsCOMPtr<nsIDOMNode> divNode, textNode;
nsAutoString tagName(NS_LITERAL_STRING("div"));
nsAutoString elementName(NS_LITERAL_STRING("errmsg"));
result = NewElementWithText(tagName, elementName, -1,
mSessionNode, divNode, textNode);
if (NS_SUCCEEDED(result) && divNode && textNode) {
nsAutoString errMsg(NS_LITERAL_STRING("Error in XMLterm (code "));
errMsg.Append(abortCode);
errMsg.Append(NS_LITERAL_STRING("); session closed."));
SetDOMText(textNode, errMsg);
// Collapse selection and position cursor
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return NS_ERROR_FAILURE;
nsCOMPtr<nsISelection> selection;
result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_SUCCEEDED(result) && selection) {
selection->Collapse(textNode, errMsg.Length());
if (NS_SUCCEEDED(result)) {
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_TRUE);
}
}
}
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));
// If string terminates in whitespace, append NBSP for cursor positioning
nsAutoString tempString( aString );
if (!aString.IsEmpty() && aString.Last() == PRUnichar(' '))
tempString += kNBSP;
// Display string
result = SetDOMText(mInputTextNode, tempString);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
char* temCString = ToNewCString(aString);
XMLT_LOG(mozXMLTermSession::DisplayInput,72,
("aString=%s\n", temCString));
nsCRT::free(temCString);
// Collapse selection and position cursor
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return NS_ERROR_FAILURE;
nsCOMPtr<nsISelection> selection;
result = selCon->GetSelection(nsISelectionController::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.IsEmpty()) {
// Collapse selection to new cursor location
result = selection->Collapse(mInputTextNode, cursorCol);
} else {
// Get the last bit of text in the prompt
nsCOMPtr<nsIDOMText> domText (do_QueryInterface(mPromptTextNode));
if (domText) {
PRUint32 textLength;
result = domText->GetLength(&textLength);
if (NS_SUCCEEDED(result)) {
XMLT_LOG(mozXMLTermSession::DisplayInput,72,
("textLength=%d\n", textLength));
result = selection->Collapse(mPromptTextNode, textLength);
}
}
}
#endif // !NO_WORKAROUND
NS_ASSERTION((NS_SUCCEEDED(result)),
"selection could not be collapsed after insert.");
return NS_OK;
}
/** Autodetects markup in current output line
* @param aString string to be displayed
* @param firstOutputLine PR_TRUE if this is the first output line
* @param secure PR_TRUE if output data is secure
* (usually PR_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.AppendLiteral(" ");
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(NS_LITERAL_STRING("http://in.sec.ure"));
result = InitStream(streamURL, newMarkupType, PR_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 PR_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.SetLength(0);
} else {
// Create IFRAME to display stream document
nsAutoString src(NS_LITERAL_STRING("about:blank"));
nsAutoString width(NS_LITERAL_STRING("100%"));
nsAutoString height(NS_LITERAL_STRING("10"));
PRInt32 frameBorder = 0;
if (!streamIsSecure)
frameBorder = 2;
result = NewIFrame(mOutputBlockNode, mCurrentEntryNumber,
frameBorder, src, width, height);
if (NS_FAILED(result))
return result;
mXMLTermStream = do_CreateInstance( MOZXMLTERMSTREAM_CONTRACTID,
&result);
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<nsIDOMWindowInternal> outerDOMWindow;
result = mozXMLTermUtils::ConvertDocShellToDOMWindow(docShell,
getter_AddRefs(outerDOMWindow));
if (NS_FAILED(result) || !outerDOMWindow) {
XMLT_ERROR("mozXMLTermSession::InitStream: Failed to convert webshell\n");
return NS_ERROR_FAILURE;
}
// Initialize markup handling
nsCAutoString iframeName("iframe");
#if 0
iframeName.Append("t");
#else
iframeName.AppendInt(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;
}
NS_ConvertUTF16toUTF8 url(streamURL);
result = mXMLTermStream->Open(outerDOMWindow, iframeName.get(),
url.get(),
contentType.get(), 800);
if (NS_FAILED(result)) {
XMLT_ERROR("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;
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
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(NS_LITERAL_STRING("span"));
nsAutoString elementName(NS_LITERAL_STRING("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.AssignLiteral("XMLTerm: *Error* Insecure stream data; is LTERM_COOKIE set?");
break;
case INCOMPLETE_FRAGMENT:
mFragmentBuffer.AssignLiteral("XMLTerm: *Error* Incomplete stream data");
break;
default:
break;
}
// Display text
result = SetDOMText(textNode, mFragmentBuffer);
if (NS_FAILED(result))
return result;
mFragmentBuffer.SetLength(0);
break;
}
case JS_FRAGMENT:
{
// Execute JS fragment
nsAutoString jsOutput;
jsOutput.SetLength(0);
result = mozXMLTermUtils::ExecuteScript(domDoc,
mFragmentBuffer,
jsOutput);
if (NS_FAILED(result))
jsOutput.AssignLiteral("Error in JavaScript execution\n");
mFragmentBuffer.SetLength(0);
if (!jsOutput.IsEmpty()) {
// 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.SetLength(0);
break;
case HTML_DOCUMENT:
case XML_DOCUMENT:
// Close HTML/XML document
result = mXMLTermStream->Close();
if (NS_FAILED(result)) {
XMLT_ERROR("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.SetLength(0);
mPreTextDisplayed.SetLength(0);
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 PR_TRUE if this is a complete line of output
* @param streamOutput PR_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 += PRUnichar('\n');
} else {
mOutputMarkupType = OVERFLOW_FRAGMENT;
mFragmentBuffer.AssignLiteral("XMLTerm: *Error* Stream data overflow (");
mFragmentBuffer.AppendInt(strLen,10);
mFragmentBuffer.Append(NS_LITERAL_STRING(" chars)"));
break;
}
}
break;
case HTML_DOCUMENT:
case XML_DOCUMENT:
// Write complete lines to document stream
if (newline || streamOutput) {
nsAutoString str(aString);
if (newline)
str.AppendLiteral("\n");
result = mXMLTermStream->Write(str.get());
if (NS_FAILED(result)) {
XMLT_ERROR("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 PR_TRUE, delete all previous display nodes
* (excluding the current one)
*/
NS_IMETHODIMP mozXMLTermSession::LimitOutputLines(PRBool deleteAllOld)
{
nsresult result;
nsAutoString attValue;
XMLT_LOG(mozXMLTermSession::LimitOutputLines,70,
("deleteAllOld=%d, mEntryOutputLines=%d\n",
deleteAllOld, mEntryOutputLines));
nsCOMPtr<nsIDOMNode> firstChild;
result = mOutputBlockNode->GetFirstChild(getter_AddRefs(firstChild));
if (NS_FAILED(result) || !firstChild)
return NS_ERROR_FAILURE;
attValue.SetLength(0);
result = mozXMLTermUtils::GetNodeAttribute(firstChild, "class", attValue);
if (NS_FAILED(result))
return result;
if (!attValue.EqualsASCII(sessionElementNames[WARNING_ELEMENT])) {
// Create warning message element
nsCOMPtr<nsIDOMNode> divNode, textNode;
nsAutoString tagName(NS_LITERAL_STRING("div"));
nsAutoString elementName; elementName.AssignASCII(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;
warningMsg.AssignLiteral("XMLTerm: *WARNING* Command output truncated to ");
warningMsg.AppendInt(300,10);
warningMsg.AppendLiteral(" lines");
result = SetDOMText(textNode, warningMsg);
}
PR_ASSERT(mOutputDisplayNode != firstChild);
nsCOMPtr<nsIDOMNode> nextChild;
PRInt32 decrementedLineCount = 0;
for (;;) {
result = firstChild->GetNextSibling(getter_AddRefs(nextChild));
PR_ASSERT(NS_SUCCEEDED(result) && nextChild);
// Do not modify current display node
if (nextChild.get() == mOutputDisplayNode.get())
break;
PRInt32 deleteNode = 0;
if (deleteAllOld) {
deleteNode = 1;
} else {
attValue.SetLength(0);
result = mozXMLTermUtils::GetNodeAttribute(nextChild, "class", attValue);
if (NS_FAILED(result) || attValue.IsEmpty()) {
deleteNode = 1;
} else {
if (attValue.EqualsASCII(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.EqualsASCII(sessionElementNames[STDIN_ELEMENT])) ||
(attValue.EqualsASCII(sessionElementNames[STDOUT_ELEMENT])) ||
(attValue.EqualsASCII(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 PR_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.get();
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 = ToNewCString(aString);
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;
elementName.SetLength(0);
if (uniformStyle == LTERM_STDIN_STYLE) {
preDisplayType = PRE_STDIN_NODE;
elementName.AssignASCII(sessionElementNames[STDIN_ELEMENT]);
XMLT_LOG(mozXMLTermSession::AppendOutput,72, ("PRE_STDIN_NODE\n"));
} else if (uniformStyle == LTERM_STDERR_STYLE) {
preDisplayType = PRE_STDERR_NODE;
elementName.AssignASCII(sessionElementNames[STDERR_ELEMENT]);
XMLT_LOG(mozXMLTermSession::AppendOutput,72, ("PRE_STDERR_NODE\n"));
} else {
preDisplayType = PRE_STDOUT_NODE;
elementName.AssignASCII(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(NS_LITERAL_STRING("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;
// If string terminates in whitespace, append NBSP for cursor positioning
nsAutoString tempString( aString );
if (newline || (aString.Last() == PRUnichar(' ')))
tempString += kNBSP;
// Display incomplete line
result = SetDOMText(mOutputTextNode, tempString);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// Initialize PRE text string buffers
mPreTextDisplayed = aString;
mPreTextBuffered.SetLength(0);
mPreTextBufferLines = 0;
}
// Save incomplete line
mPreTextIncomplete = aString;
if (newline) {
// Complete line; append to buffer
if (mPreTextBufferLines > 0) {
mPreTextBuffered += PRUnichar('\n');
}
mPreTextBufferLines++;
mPreTextBuffered += mPreTextIncomplete;
mPreTextIncomplete.SetLength(0);
if (mPreTextBufferLines > 300) {
// Delete all earlier PRE/mixed blocks and first line of current block
result = LimitOutputLines(PR_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.SetLength(0);
} 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(PR_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; elementName.AssignASCII(sessionElementNames[MIXED_ELEMENT]);
nsCOMPtr<nsIDOMNode> divNode;
nsAutoString tagName(NS_LITERAL_STRING("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.AssignLiteral("pre");
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.AssignASCII(sessionElementNames[STDIN_ELEMENT]);
break;
case LTERM_STDERR_STYLE:
elementName.AssignASCII(sessionElementNames[STDERR_ELEMENT]);
break;
default:
elementName.AssignASCII(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(PR_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.get();
// 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 = ToNewCString(aString);
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;
markupString.SetLength(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, 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.AssignLiteral("<span class=\"");
markupString.AssignASCII(fileTypeNames[fileType]);
markupString.AssignLiteral("\"");
int j;
for (j=0; j<SESSION_EVENT_TYPES; j++) {
markupString.AssignLiteral(" on");
markupString.AssignASCII(sessionEventNames[j]);
markupString.AssignLiteral("=\"return HandleEvent(event, '");
markupString.AssignASCII(sessionEventNames[j]);
markupString.AssignLiteral("','");
markupString.AssignASCII(fileTypeNames[fileType]);
markupString.AssignLiteral("',-#,'");
markupString.Assign(pathname);
markupString.Assign(NS_LITERAL_STRING("');\""));
}
markupString.AssignLiteral(">");
markupString.Assign(filename);
markupString.AssignLiteral("</span>");
// Search for new word
wordBegin = wordEnd+1;
}
if (mOutputDisplayType != PRE_STDOUT_NODE) {
// Create PRE block
nsAutoString nullString; nullString.SetLength(0);
result = AppendOutput(nullString, nullString, PR_FALSE);
}
PR_ASSERT(mOutputDisplayNode != nsnull);
PR_ASSERT(mOutputTextNode != nsnull);
result = InsertFragment(markupString, mOutputDisplayNode,
mCurrentEntryNumber, mOutputTextNode.get());
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
// Insert text node containing newline only
nsCOMPtr<nsIDOMText> newText;
nsAutoString newlineStr(NS_LITERAL_STRING("\n"));
result = domDoc->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,62,("tree:\n"));
#endif /* 0 */
return NS_OK;
}
/** Inserts HTML fragment string as child of parentNode, before specified
* child node, or after the last child node
* @param aString HTML fragment string to be inserted
* @param parentNode parent node for HTML fragment
* @param entryNumber entry number (default value = -1)
* (if entryNumber >= 0, all '#' characters in
* id/onclick attribute values are substituted
* with entryNumber)
* @param beforeNode child node before which to insert fragment;
* if null, insert after last child node
* (default value is null)
* @param replace if PR_TRUE, replace beforeNode with inserted fragment
* (default value is PR_FALSE)
*/
NS_IMETHODIMP mozXMLTermSession::InsertFragment(const nsString& aString,
nsIDOMNode* parentNode,
PRInt32 entryNumber,
nsIDOMNode* beforeNode,
PRBool replace)
{
nsresult result;
char* temCString = ToNewCString(aString);
XMLT_LOG(mozXMLTermSession::InsertFragment,70,("aString=%s\n", temCString));
nsCRT::free(temCString);
// Get selection
nsCOMPtr<nsISelection> selection;
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return NS_ERROR_FAILURE;
result = selCon->GetSelection(nsISelectionController::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);
// Deep clone child node
// Note: Not clear why this needs to be done, but like "deep refresh",
// seems to be essential for event handlers to work
nsCOMPtr<nsIDOMNode> cloneNode;
result = childNode->CloneNode(PR_TRUE, getter_AddRefs(cloneNode));
if (NS_FAILED(result) || !cloneNode)
return NS_ERROR_FAILURE;
// Insert clone of child node
nsCOMPtr<nsIDOMNode> resultNode;
PRBool replaceTem = replace;
if (beforeNode) {
if (replaceTem) {
// Replace before node
result = parentNode->ReplaceChild(cloneNode, beforeNode,
getter_AddRefs(resultNode));
beforeNode = nsnull;
nsCOMPtr<nsIDOMNode> newBeforeNode;
result = resultNode->GetNextSibling(getter_AddRefs(newBeforeNode));
if (NS_SUCCEEDED(result) && newBeforeNode) {
beforeNode = newBeforeNode.get();
replaceTem = PR_FALSE;
}
} else {
// Insert before specified node
result = parentNode->InsertBefore(cloneNode, beforeNode,
getter_AddRefs(resultNode));
}
} else {
// Append child
result = parentNode->AppendChild(cloneNode, 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.SetLength(0);
numberString.AppendInt(aNumber,10);
for (;;) {
// Search for '#' character
numberOffset = aString.FindChar((PRUnichar) '#');
if (numberOffset < 0)
break;
// Substitute '#' with supplied number
aString.Cut(numberOffset,1);
aString.Insert(numberString, numberOffset);
}
}
/** Sanitize event handler attribute values by imposing syntax checks.
* @param aAttrValue attribute value to be sanitized
* @param aEventName name of event being handled ("click", ...)
*/
void mozXMLTermSession::SanitizeAttribute(nsString& aAttrValue,
const char* aEventName)
{
// ****************NOTE***************
// At the moment this method simply prevents the word function and the
// the character '{' both occurring in the event handler attribute.
// NEEDS TO BE IMPROVED TO ENFORCE STRICTER REQUIREMENTS
// such as: the event handler attribute should always be of the form
// "return EventHandler(str_arg1, num_arg2, str_arg3, str_arg4);"
if ((aAttrValue.FindChar((PRUnichar)'{') >= 0) &&
(aAttrValue.Find("function") >= 0)) {
// Character '{' and string "function" both found in attribute value;
// set to null string
char* temCString = ToNewCString(aAttrValue);
XMLT_WARNING("mozXMLTermSession::SanitizeAttribute: Warning - deleted attribute on%s='%s'\n", aEventName, temCString);
nsCRT::free(temCString);
aAttrValue.SetLength(0);
}
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;
tagName.SetLength(0);
result = domElement->GetTagName(tagName);
if (NS_SUCCEEDED(result) && tagName.LowerCaseEqualsLiteral("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].SetLength(0);
nsAutoString attName, attValue;
for (j=0; j<SESSION_EVENT_TYPES; j++) {
attName.AssignLiteral("on");
attName.AppendASCII(sessionEventNames[j]);
attValue.SetLength(0);
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && !attValue.IsEmpty()) {
// 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; nullStr.SetLength(0);
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.LowerCaseEqualsLiteral("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.AssignLiteral("id");
attValue.SetLength(0);
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && !attValue.IsEmpty()) {
// 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.AssignLiteral("on");
attName.AppendASCII(sessionEventNames[j]);
attValue = eventAttrVals[j];
if (!attValue.IsEmpty()) {
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.AssignLiteral("on");
attName.AppendASCII(sessionEventNames[j]);
XMLT_LOG(mozXMLTermSession::DeepRefreshEventHandlers,89,
("Refreshing on%s attribute\n",sessionEventNames[j] ));
attValue.SetLength(0);
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && !attValue.IsEmpty()) {
// 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; preTextSplit.SetLength(0);
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.SetLength(0);
if ((mPreTextBufferLines == 0) && mPreTextBuffered.IsEmpty()) {
// Remove lone text node
nsCOMPtr<nsIDOMNode> resultNode;
result = mOutputDisplayNode->RemoveChild(mOutputTextNode,
getter_AddRefs(resultNode));
// Check if PRE node has any child nodes
PRBool hasChildNodes = PR_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.IsEmpty() ) {
// Create new PRE element with incomplete text
nsAutoString styleStr; styleStr.SetLength(0);
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, PR_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"));
PRBool dummyOutput = PR_FALSE;
if (!mOutputTextNode) {
// Append dummy output line
nsCOMPtr<nsIDOMNode> spanNode, textNode;
nsAutoString tagName(NS_LITERAL_STRING("span"));
nsAutoString elementName; elementName.AssignASCII(sessionElementNames[STDOUT_ELEMENT]);
result = NewElementWithText(tagName, elementName, -1,
mOutputBlockNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode)
return;
// Display NBSP for cursor positioning
nsAutoString tempString;
tempString += kNBSP;
SetDOMText(textNode, tempString);
dummyOutput = PR_TRUE;
mOutputDisplayType = SPAN_DUMMY_NODE;
mOutputDisplayNode = spanNode;
mOutputTextNode = textNode;
mOutputTextOffset = 0;
}
// Get selection
nsCOMPtr<nsISelection> selection;
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return; // NS_ERROR_FAILURE
result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
if (NS_SUCCEEDED(result) && selection) {
// Position cursor at end of line
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(mOutputTextNode) );
nsAutoString text; text.SetLength(0);
domText->GetData(text);
PRInt32 textOffset = text.Length();
if (textOffset && dummyOutput) textOffset--;
if (lineTermAux && (mOutputDisplayType == PRE_STDIN_NODE)) {
// Get cursor column
PRInt32 cursorCol = 0;
lineTermAux->GetCursorColumn(&cursorCol);
textOffset = cursorCol - mOutputTextOffset;
if (textOffset > (PRInt32)text.Length())
textOffset = text.Length();
}
result = selection->Collapse(mOutputTextNode, textOffset);
}
}
/** Scrolls document to align bottom and left margin with screen */
NS_IMETHODIMP mozXMLTermSession::ScrollToBottomLeft(void)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::ScrollToBottomLeft,70,("\n"));
nsCOMPtr<nsIPresShell> presShell;
result = mXMLTerminal->GetPresShell(getter_AddRefs(presShell));
if (NS_FAILED(result) || !presShell)
return NS_ERROR_FAILURE;
nsIDocument* doc = presShell->GetDocument();
if (doc) {
doc->FlushPendingNotifications(Flush_Layout);
}
// Get DOM Window
nsCOMPtr<nsIDocShell> docShell;
result = mXMLTerminal->GetDocShell(getter_AddRefs(docShell));
if (NS_FAILED(result) || !docShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMWindowInternal> domWindow;
result = mozXMLTermUtils::ConvertDocShellToDOMWindow(docShell,
getter_AddRefs(domWindow));
if (NS_FAILED(result) || !domWindow)
return NS_ERROR_FAILURE;
// Scroll to bottom left of screen
domWindow->ScrollBy(-99999,99999);
return NS_OK;
}
/** Gets current entry (command) number
* @param aNumber (output) current entry number
*/
NS_IMETHODIMP mozXMLTermSession::GetCurrentEntryNumber(PRInt32 *aNumber)
{
*aNumber = mCurrentEntryNumber;
return NS_OK;
}
// Get size of entry history buffer
NS_IMETHODIMP mozXMLTermSession::GetHistory(PRInt32 *aHistory)
{
*aHistory = mMaxHistory;
return NS_OK;
}
// Set size of entry history buffer
NS_IMETHODIMP mozXMLTermSession::SetHistory(PRInt32 aHistory)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::SetHistory,30,("\n"));
if (aHistory < 1)
aHistory = 1;
if (mInitialized && mStartEntryNode && (aHistory < mMaxHistory)) {
// Delete any extra entry blocks
PRInt32 delEntries = (mCurrentEntryNumber-mStartEntryNumber)
- aHistory;
PRInt32 j;
for (j=0; j<delEntries; j++) {
nsCOMPtr<nsIDOMNode> newStartNode;
result = mStartEntryNode->GetNextSibling(getter_AddRefs(newStartNode));
if (NS_FAILED(result) || !newStartNode) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMNode> resultNode;
result = mSessionNode->RemoveChild(mStartEntryNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result)) {
return NS_ERROR_FAILURE;
}
mStartEntryNode = newStartNode;
mStartEntryNumber++;
}
}
mMaxHistory = aHistory;
return NS_OK;
}
// Get HTML prompt string
NS_IMETHODIMP mozXMLTermSession::GetPrompt(PRUnichar **_aPrompt)
{
// NOTE: Need to be sure that this may be freed by nsMemory::Free
*_aPrompt = ToNewUnicode(mPromptHTML);
return NS_OK;
}
// Set HTML prompt string
NS_IMETHODIMP mozXMLTermSession::SetPrompt(const PRUnichar* aPrompt)
{
mPromptHTML = aPrompt;
return NS_OK;
}
/** Gets flag denoting whether terminal is in full screen mode
* @param aFlag (output) screen mode flag
*/
NS_IMETHODIMP mozXMLTermSession::GetScreenMode(PRBool* aFlag)
{
if (!aFlag)
return NS_ERROR_NULL_POINTER;
*aFlag = (mScreenNode != nsnull);
return NS_OK;
}
/** Create a DIV element with attributes NAME="preface", CLASS="preface",
* and ID="preface0", containing an empty text node, and append it as a
* child of the main BODY element. Also make it the current display element.
*/
NS_IMETHODIMP mozXMLTermSession::NewPreface(void)
{
nsresult result;
XMLT_LOG(mozXMLTermSession::NewPreface,40,("\n"));
// Create preface element and append as child of session element
nsCOMPtr<nsIDOMNode> divNode;
nsAutoString tagName(NS_LITERAL_STRING("div"));
nsAutoString name(NS_LITERAL_STRING("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 = PR_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
nsAutoString cmdName; cmdName.AssignASCII(sessionElementNames[COMMAND_ELEMENT]);
result = SetEventAttributes(cmdName,
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.AssignLiteral("div");
name.AssignASCII(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.AssignLiteral("div");
name.AssignASCII(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> promptSpanNode;
tagName.AssignLiteral("span");
name.AssignASCII(sessionElementNames[PROMPT_ELEMENT]);
result = NewElement(tagName, name, mCurrentEntryNumber,
inputNode, promptSpanNode);
if (NS_FAILED(result) || !promptSpanNode) {
return NS_ERROR_FAILURE;
}
// Add event attributes to prompt element
result = SetEventAttributes(name, mCurrentEntryNumber,
promptSpanNode);
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> resultNode;
if (mPromptHTML.IsEmpty()) {
#define DEFAULT_ICON_PROMPT
#ifdef DEFAULT_ICON_PROMPT // Experimental code; has scrolling problems
// Create text node + image node as child of prompt element
nsCOMPtr<nsIDOMNode> spanNode, textNode;
tagName.AssignLiteral("span");
name.AssignLiteral("noicons");
result = NewElementWithText(tagName, name, -1,
promptSpanNode, spanNode, textNode);
if (NS_FAILED(result) || !spanNode || !textNode) {
return NS_ERROR_FAILURE;
}
// Strip single trailing space, if any, from prompt string
int spaceOffset = aPrompt.Length();
if ((spaceOffset > 0) && (aPrompt.Last() == ((PRUnichar) ' ')))
spaceOffset--;
nsAutoString promptStr;
aPrompt.Left(promptStr, spaceOffset);
// Set prompt text
result = SetDOMText(textNode, promptStr);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
// Create IMG element
tagName.AssignLiteral("img");
nsCOMPtr<nsIDOMElement> imgElement;
result = domDoc->CreateElement(tagName, getter_AddRefs(imgElement));
if (NS_FAILED(result) || !imgElement)
return NS_ERROR_FAILURE;
// Set attributes
nsAutoString attName(NS_LITERAL_STRING("class"));
nsAutoString attValue(NS_LITERAL_STRING("icons"));
imgElement->SetAttribute(attName, attValue);
attName.AssignLiteral("src");
attValue.AssignLiteral("chrome://xmlterm/skin/wheel.gif");
imgElement->SetAttribute(attName, attValue);
attName.AssignLiteral("align");
attValue.AssignLiteral("middle");
imgElement->SetAttribute(attName, attValue);
// Append IMG element
nsCOMPtr<nsIDOMNode> imgNode = do_QueryInterface(imgElement);
result = promptSpanNode->AppendChild(imgNode,
getter_AddRefs(resultNode));
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
#else // !DEFAULT_ICON_PROMPT
// Create text node as child of prompt element
nsCOMPtr<nsIDOMNode> textNode;
result = NewTextNode(promptSpanNode, textNode);
if (NS_FAILED(result) || !textNode)
return NS_ERROR_FAILURE;
// Set prompt text
result = SetDOMText(textNode, aPrompt);
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
#endif // !DEFAULT_ICON_PROMPT
} else {
// User-specified HTML prompt
result = InsertFragment(mPromptHTML, promptSpanNode,
mCurrentEntryNumber);
}
// Append text node containing single NBSP
nsCOMPtr<nsIDOMText> stubText;
nsAutoString spaceStr(kNBSP);
result = domDoc->CreateTextNode(spaceStr, getter_AddRefs(stubText));
if (NS_FAILED(result) || !stubText)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> stubNode = do_QueryInterface(stubText);
result = inputNode->AppendChild(stubNode, getter_AddRefs(resultNode));
if (NS_FAILED(result))
return NS_ERROR_FAILURE;
mPromptTextNode = stubNode;
// Create command element
nsCOMPtr<nsIDOMNode> newCommandSpanNode;
tagName.AssignLiteral("span");
name.AssignASCII(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.AssignLiteral("div");
name.AssignASCII(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 = PR_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,70,("\n"));
// Create screen element and append as child of session element
nsCOMPtr<nsIDOMNode> divNode;
nsAutoString tagName(NS_LITERAL_STRING("div"));
nsAutoString name(NS_LITERAL_STRING("screen"));
result = NewElement(tagName, name, 0,
mBodyNode, divNode);
if (NS_FAILED(result) || !divNode)
return NS_ERROR_FAILURE;
mScreenNode = divNode;
// Collapse non-screen stuff
nsAutoString attName(NS_LITERAL_STRING("xmlt-block-collapsed"));
nsAutoString attValue(NS_LITERAL_STRING("true"));
nsCOMPtr<nsIDOMElement> menusElement = do_QueryInterface(mMenusNode);
if (NS_SUCCEEDED(result) && menusElement) {
menusElement->SetAttribute(attName, attValue);
}
nsCOMPtr<nsIDOMElement> sessionElement = do_QueryInterface(mSessionNode);
if (sessionElement) {
sessionElement->SetAttribute(attName, attValue);
}
// 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)) {
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return NS_ERROR_FAILURE;
result = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_TRUE);
}
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; text.SetLength(0);
for (j=0; j<nChildren; j++) {
result = childNodes->Item(j, getter_AddRefs(childNode));
if (NS_FAILED(result) || !childNode)
return NS_ERROR_FAILURE;
result = childNode->GetNodeType(&nodeType);
if (NS_FAILED(result))
return result;
XMLT_LOG(mozXMLTermSession::GetScreenText,60,
("j=%d, nodeType=%d\n", j, nodeType));
if (nodeType != nsIDOMNode::TEXT_NODE) {
nsCOMPtr<nsIDOMNode> temNode;
result = childNode->GetFirstChild(getter_AddRefs(temNode));
if (NS_FAILED(result))
return result;
childNode = temNode;
result = childNode->GetNodeType(&nodeType);
if (NS_FAILED(result))
return result;
PR_ASSERT(nodeType == nsIDOMNode::TEXT_NODE);
}
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(childNode) );
result = domText->GetData(text);
if (NS_FAILED(result))
return result;
XMLT_LOG(mozXMLTermSession::GetScreenText,60,("prevCols=%d\n",prevCols));
if (prevCols+(PRInt32)text.Length() >= aCol) {
// Determine offset in current text element
textOffset = aCol - prevCols;
textNode = childNode;
} else if (j == nChildren-1) {
// Position at end of line
textOffset = text.Length();
textNode = childNode;
}
}
// Get selection
nsCOMPtr<nsISelection> selection;
nsCOMPtr<nsISelectionController> selCon;
result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon));
if (NS_FAILED(result) || !selCon)
return NS_ERROR_FAILURE;
result = selCon->GetSelection(nsISelectionController::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(NS_LITERAL_STRING("pre"));
nsAutoString elementName(NS_LITERAL_STRING("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(NS_LITERAL_STRING("cols"));
nsAutoString val; val.SetLength(0);
val.AppendInt(mScreenCols,10);
preElement->SetAttribute(att, val);
att.AssignLiteral("rows");
val.AssignLiteral("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.get();
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(NS_LITERAL_STRING("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.AssignLiteral("boldstyle");
break;
case LTERM_STDOUT_STYLE | LTERM_ULINE_STYLE:
elementName.AssignLiteral("underlinestyle");
break;
case LTERM_STDOUT_STYLE | LTERM_BLINK_STYLE:
elementName.AssignLiteral("blinkstyle");
break;
case LTERM_STDOUT_STYLE | LTERM_INVERSE_STYLE:
elementName.AssignLiteral("inversestyle");
break;
default:
elementName.AssignLiteral("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(NS_LITERAL_STRING("br"));
XMLT_LOG(mozXMLTermSession::NewBreak,60,("\n"));
// Create "br" element and append as child of specified parent
nsCOMPtr<nsIDOMNode> brNode;
nsAutoString name; name.SetLength(0);
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(NS_LITERAL_STRING("a"));
XMLT_LOG(mozXMLTermSession::NewAnchor,80,("\n"));
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
// Create anchor
nsCOMPtr<nsIDOMElement> newElement;
result = domDoc->CreateElement(tagName, getter_AddRefs(newElement));
if (NS_FAILED(result) || !newElement)
return NS_ERROR_FAILURE;
// Set element attributes
nsAutoString hrefAtt(NS_LITERAL_STRING("href"));
nsAutoString hrefVal(NS_LITERAL_STRING("#"));
newElement->SetAttribute(hrefAtt, hrefVal);
if (!classAttribute.IsEmpty()) {
nsAutoString classStr(NS_LITERAL_STRING("class"));
newElement->SetAttribute(classStr, classAttribute);
if (number >= 0) {
nsAutoString idAtt(NS_LITERAL_STRING("id"));
nsAutoString idVal(classAttribute);
idVal.AppendInt(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"));
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
// Create element
nsCOMPtr<nsIDOMElement> newElement;
result = domDoc->CreateElement(tagName, getter_AddRefs(newElement));
if (NS_FAILED(result) || !newElement)
return NS_ERROR_FAILURE;
if (!name.IsEmpty()) {
// Set attributes
nsAutoString classAtt(NS_LITERAL_STRING("class"));
nsAutoString classVal(name);
newElement->SetAttribute(classAtt, classVal);
nsAutoString nameAtt(NS_LITERAL_STRING("name"));
nsAutoString nameVal(name);
newElement->SetAttribute(nameAtt, nameVal);
if (number >= 0) {
nsAutoString idAtt(NS_LITERAL_STRING("id"));
nsAutoString idVal(name);
idVal.AppendInt(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"));
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
// Create text node
nsCOMPtr<nsIDOMText> newText;
nsAutoString nullStr; nullStr.SetLength(0);
result = domDoc->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"));
nsCOMPtr<nsIDOMDocument> domDoc;
result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc));
if (NS_FAILED(result) || !domDoc)
return NS_ERROR_FAILURE;
#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(NS_LITERAL_STRING("iframe"));
result = domDoc->CreateElement(tagName, getter_AddRefs(newElement));
if (NS_FAILED(result) || !newElement)
return NS_ERROR_FAILURE;
nsAutoString attName, attValue;
// Set attributes
if (number >= 0) {
attName.AssignLiteral("name");
attValue.AssignLiteral("iframe");
attValue.AppendInt(number,10);
newElement->SetAttribute(attName, attValue);
}
attName.AssignLiteral("frameborder");
attValue.SetLength(0);
attValue.AppendInt(frameBorder,10);
newElement->SetAttribute(attName, attValue);
if (!src.IsEmpty()) {
// Set SRC attribute
attName.AssignLiteral("src");
newElement->SetAttribute(attName, src);
}
if (!width.IsEmpty()) {
// Set WIDTH attribute
attName.AssignLiteral("width");
newElement->SetAttribute(attName, width);
}
if (!height.IsEmpty()) {
// Set HEIGHT attribute
attName.AssignLiteral("height");
newElement->SetAttribute(attName, height);
}
// Append child to parent
nsCOMPtr<nsIDOMNode> iframeNode;
nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(newElement);
result = parentNode->AppendChild(newNode, getter_AddRefs(iframeNode));
if (NS_FAILED(result) || !iframeNode)
return NS_ERROR_FAILURE;
return NS_OK;
#endif
}
/** Add event attributes (onclick, ...) to DOM node
* @param name name of DOM node (supplied as argument to the event handler)
* @param number entry number (supplied as argument to the event handler)
* @param domNode DOM node to be modified
*/
NS_IMETHODIMP mozXMLTermSession::SetEventAttributes(const nsString& name,
PRInt32 number,
nsCOMPtr<nsIDOMNode>& domNode)
{
nsresult result;
nsCOMPtr <nsIDOMElement> domElement = do_QueryInterface(domNode);
if (!domElement)
return NS_ERROR_FAILURE;
int j;
for (j=0; j<SESSION_EVENT_TYPES; j++) {
nsAutoString attName(NS_LITERAL_STRING("on"));
attName.AppendASCII(sessionEventNames[j]);
nsAutoString attValue(NS_LITERAL_STRING("return HandleEvent(event, '"));
attValue.AppendASCII(sessionEventNames[j]);
attValue.AppendLiteral("','");
attValue.Append(name);
attValue.AppendLiteral("','");
attValue.AppendInt(number,10);
attValue.Append(NS_LITERAL_STRING("','');"));
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 PR_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 PR_TRUE if node is a text, span or anchor node
*/
PRBool mozXMLTermSession::IsPREInlineNode(nsIDOMNode* aNode)
{
nsresult result;
PRBool isPREInlineNode = PR_FALSE;
nsCOMPtr<nsIDOMText> domText = do_QueryInterface(aNode);
if (domText) {
isPREInlineNode = PR_TRUE;
} else {
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aNode);
if (domElement) {
nsAutoString tagName; tagName.SetLength(0);
result = domElement->GetTagName(tagName);
if (NS_SUCCEEDED(result)) {
isPREInlineNode = tagName.LowerCaseEqualsLiteral("span") ||
tagName.LowerCaseEqualsLiteral("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 PR_TRUE, serialize children of node as well
* (defaults to PR_FALSE)
* @param insidePREnode set to PR_TRUE if aNode is embedded inside a PRE node
* control formatting
* (defaults to PR_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.AppendLiteral(" ");
htmlString.SetLength(0);
nsCOMPtr<nsIDOMText> domText( do_QueryInterface(aNode) );
if (domText) {
// Text node
domText->GetData(htmlString);
htmlString.ReplaceChar(kNBSP, ' ');
} else {
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(aNode);
if (domElement) {
nsAutoString tagName; tagName.SetLength(0);
domElement->GetTagName(tagName);
if (!insidePRENode) {
htmlString += indentString;
}
htmlString.AppendLiteral("<");
htmlString += tagName;
PRBool isPRENode = tagName.LowerCaseEqualsLiteral("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; attrName.SetLength(0);
nsAutoString attrValue; attrValue.SetLength(0);
result = attr->GetName(attrName);
if (NS_SUCCEEDED(result)) {
htmlString.AppendLiteral(" ");
htmlString.Append(attrName);
}
result = attr->GetValue(attrValue);
if (NS_SUCCEEDED(result) && !attrName.IsEmpty()) {
htmlString.AppendLiteral("=\"");
htmlString.Append(attrValue);
htmlString.AppendLiteral("\"");
}
}
}
}
}
}
if (!deepContent) {
htmlString.AppendLiteral(">");
} 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.IsEmpty()) {
if (insidePRENode)
htmlString.AppendLiteral("\n>");
else
htmlString.AppendLiteral(">\n");
htmlString += htmlInner;
if (!insidePRENode)
htmlString += indentString;
} else {
htmlString.AppendLiteral(">");
}
htmlString.AppendLiteral("</");
htmlString += tagName;
if (insidePRENode)
htmlString.AppendLiteral("\n");
htmlString.AppendLiteral(">");
if (!insidePRENode)
htmlString.AppendLiteral("\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 (PR_TRUE) {
nsAutoString indentString; indentString.SetLength(0);
nsAutoString htmlString;
ToHTMLString(currentNode, indentString, htmlString,
(PRBool) (treeActionCode == TREE_PRINT_HTML) );
fprintf(fileStream, "%s:\n", treeActionNames[treeActionCode-1]);
char* htmlCString = ToNewCString(htmlString);
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; tagName.SetLength(0);
result = domElement->GetTagName(tagName);
if (NS_SUCCEEDED(result)) {
char* tagCString = ToNewCString(tagName);
fprintf(fileStream, "%s", tagCString);
nsCRT::free(tagCString);
// Print selected attribute values
int j;
for (j=0; j<PRINT_ATTRIBUTE_NAMES; j++) {
nsAutoString attName; attName.AssignASCII (printAttributeNames[j]);
nsAutoString attValue;
attValue.SetLength(0);
result = domElement->GetAttribute(attName, attValue);
if (NS_SUCCEEDED(result) && !attValue.IsEmpty()) {
// Print attribute value
char* tagCString2 = ToNewCString(attValue);
fprintf(fileStream, " %s=%s", printAttributeNames[j], tagCString2);
nsCRT::free(tagCString2);
}
}
}
}
fprintf(fileStream, "\n");
}
}