/* ***** 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 "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 domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; nsCOMPtr vDOMHTMLDocument (do_QueryInterface(domDoc)); if (!vDOMHTMLDocument) return NS_ERROR_FAILURE; // Locate document body node nsCOMPtr 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 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 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 menusElement = do_QueryInterface(mMenusNode); if (NS_SUCCEEDED(result) && menusElement) { menusElement->RemoveAttribute(attName); } nsCOMPtr sessionElement = do_QueryInterface(mSessionNode); if (sessionElement) { sessionElement->RemoveAttribute(attName); } // Delete screen element nsCOMPtr 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 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 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 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 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; jSetEchoFlag(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; jGetCursorColumn(&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 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 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 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 outStream; result = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), localFile, ioFlags, permissions); if (NS_FAILED(result)) return NS_ERROR_FAILURE; PRUint32 writeCount; nsCAutoString cString( "\n\n" ); if (refreshSeconds > 0) { cString.Append(""); } cString.Append("xmlterm page\n"); cString.Append("\n"); if (style) { cString.Append("\n"); } cString.Append("\n"); cString.Append("\n"); AppendUTF16toUTF8(htmlString, cString); cString.Append("\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 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 selCon; result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon)); if (NS_FAILED(result) || !selCon) return NS_ERROR_FAILURE; nsCOMPtr 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 selCon; result = mXMLTerminal->GetSelectionController(getter_AddRefs(selCon)); if (NS_FAILED(result) || !selCon) return NS_ERROR_FAILURE; nsCOMPtr 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 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("",PR_TRUE) == 0) ) { // HTML document newMarkupType = HTML_DOCUMENT; } else if (str.Find(" docShell; result = mXMLTerminal->GetDocShell(getter_AddRefs(docShell)); if (NS_FAILED(result) || !docShell) return NS_ERROR_FAILURE; nsCOMPtr 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 = "application/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 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 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 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 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 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 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 textNode; result = nextChild->GetFirstChild(getter_AddRefs(textNode)); PR_ASSERT( NS_SUCCEEDED(result) && textNode); nsCOMPtr 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 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 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 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 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 resultNode; result = mOutputBlockNode->AppendChild(divNode, getter_AddRefs(resultNode)); if (NS_FAILED(result)) return result; nsCOMPtr 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 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= 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(""); markupString.Assign(filename); markupString.AssignLiteral(""); // 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 domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; // Insert text node containing newline only nsCOMPtr newText; nsAutoString newlineStr(NS_LITERAL_STRING("\n")); result = domDoc->CreateTextNode(newlineStr, getter_AddRefs(newText)); if (NS_FAILED(result) || !newText) return NS_ERROR_FAILURE; nsCOMPtr newTextNode = do_QueryInterface(newText); nsCOMPtr 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 selection; nsCOMPtr 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 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 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 firstRange; result = selection->GetRangeAt(0, getter_AddRefs(firstRange)); if (NS_FAILED(result) || !firstRange) return NS_ERROR_FAILURE; nsCOMPtr nsrange (do_QueryInterface(firstRange)); if (!nsrange) return NS_ERROR_FAILURE; XMLT_LOG(mozXMLTermSession::InsertFragment,62,("Creating Fragment\n")); nsCOMPtr docfrag; result = nsrange->CreateContextualFragment(aString, getter_AddRefs(docfrag)); if (NS_FAILED(result) || !docfrag) return NS_ERROR_FAILURE; nsCOMPtr 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 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 nextChild; result = childNode->GetNextSibling(getter_AddRefs(nextChild)); XMLT_LOG(mozXMLTermSession::InsertFragment,72,("Inserting child node ...\n")); // nsCOMPtr 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 cloneNode; result = childNode->CloneNode(PR_TRUE, getter_AddRefs(cloneNode)); if (NS_FAILED(result) || !cloneNode) return NS_ERROR_FAILURE; // Insert clone of child node nsCOMPtr resultNode; PRBool replaceTem = replace; if (beforeNode) { if (replaceTem) { // Replace before node result = parentNode->ReplaceChild(cloneNode, beforeNode, getter_AddRefs(resultNode)); beforeNode = nsnull; nsCOMPtr 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& domNode, nsIDOMNode* parentNode, PRInt32 entryNumber) { nsresult result; PRInt32 j; XMLT_LOG(mozXMLTermSession::DeepSanitizeFragment,72,("entryNumber=%d\n", entryNumber)); nsCOMPtr 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 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; jGetAttribute(attName, attValue); if (NS_SUCCEEDED(result) && !attValue.IsEmpty()) { // Save allowed event attribute value for re-insertion eventAttrVals[j] = attValue; } } nsCOMPtr 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 attrNode; PRUint32 k; nsAutoString attrName, attrValue, prefix; nsAutoString nullStr; nullStr.SetLength(0); for (k=0; kItem(k, getter_AddRefs(attrNode)); if (NS_SUCCEEDED(result)) { nsCOMPtr 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; jSetAttribute(attName, attValue); } } } // Iterate over all child nodes for deep refresh nsCOMPtr child; result = domNode->GetFirstChild(getter_AddRefs(child)); if (NS_FAILED(result)) return NS_OK; while (child) { DeepSanitizeFragment(child, domNode, entryNumber); nsCOMPtr 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& domNode) { nsresult result; XMLT_LOG(mozXMLTermSession::DeepRefreshEventHandlers,82,("\n")); nsCOMPtr domElement = do_QueryInterface(domNode); if (!domElement) return NS_OK; int j; nsAutoString attName, attValue; // Refresh event attributes for (j=0; jGetAttribute(attName, attValue); if (NS_SUCCEEDED(result) && !attValue.IsEmpty()) { // Refresh attribute value domElement->SetAttribute(attName, attValue); } } // Iterate over all child nodes for deep refresh nsCOMPtr child; result = domNode->GetFirstChild(getter_AddRefs(child)); if (NS_FAILED(result)) return NS_OK; while (child) { DeepRefreshEventHandlers(child); nsCOMPtr 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 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 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 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 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 selection; nsCOMPtr 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 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 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 docShell; result = mXMLTerminal->GetDocShell(getter_AddRefs(docShell)); if (NS_FAILED(result) || !docShell) return NS_ERROR_FAILURE; nsCOMPtr 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 newStartNode; result = mStartEntryNode->GetNextSibling(getter_AddRefs(newStartNode)); if (NS_FAILED(result) || !newStartNode) { return NS_ERROR_FAILURE; } nsCOMPtr 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 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 newStartNode; result = mStartEntryNode->GetNextSibling(getter_AddRefs(newStartNode)); if (NS_FAILED(result) || !newStartNode) { return NS_ERROR_FAILURE; } nsCOMPtr 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 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 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 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 domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; nsCOMPtr 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 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 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 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 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 stubText; nsAutoString spaceStr(kNBSP); result = domDoc->CreateTextNode(spaceStr, getter_AddRefs(stubText)); if (NS_FAILED(result) || !stubText) return NS_ERROR_FAILURE; nsCOMPtr 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 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 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 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 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 menusElement = do_QueryInterface(mMenusNode); if (NS_SUCCEEDED(result) && menusElement) { menusElement->SetAttribute(attName, attValue); } nsCOMPtr sessionElement = do_QueryInterface(mSessionNode); if (sessionElement) { sessionElement->SetAttribute(attName, attValue); } // Create individual row elements nsCOMPtr 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 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 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 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 rowNode; result = GetRow(aRow, getter_AddRefs(rowNode)); if (NS_FAILED(result) || !rowNode) return NS_ERROR_FAILURE; nsCOMPtr 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 textNode = nsnull; nsCOMPtr childNode; nsAutoString text; text.SetLength(0); for (j=0; jItem(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 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 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 selection; nsCOMPtr 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 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 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 rowNode; result = GetRow(aRow, getter_AddRefs(rowNode)); if (NS_FAILED(result) || !rowNode) return NS_ERROR_FAILURE; nsCOMPtr 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 childNode; result = rowNode->GetFirstChild(getter_AddRefs(childNode)); if (NS_FAILED(result) || !childNode) return NS_ERROR_FAILURE; nsCOMPtr 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 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 resultNode; result = rowNode->RemoveChild(childNode, getter_AddRefs(resultNode)); if (NS_FAILED(result)) return result; } nsCOMPtr 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 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& blockNode, nsCOMPtr& 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& anchorNode) { nsresult result; nsAutoString tagName(NS_LITERAL_STRING("a")); XMLT_LOG(mozXMLTermSession::NewAnchor,80,("\n")); nsCOMPtr domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; // Create anchor nsCOMPtr 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 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& blockNode, nsIDOMNode* beforeNode) { nsresult result; XMLT_LOG(mozXMLTermSession::NewElement,80,("\n")); nsCOMPtr domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; // Create element nsCOMPtr 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 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& textNode) { nsresult result; XMLT_LOG(mozXMLTermSession::NewTextNode,80,("\n")); nsCOMPtr domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; // Create text node nsCOMPtr 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 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 domDoc; result = mXMLTerminal->GetDOMDocument(getter_AddRefs(domDoc)); if (NS_FAILED(result) || !domDoc) return NS_ERROR_FAILURE; #if 0 nsAutoString iframeFrag("\n"); result = InsertFragment(iframeFrag, parentNode, number); if (NS_FAILED(result)) return result; return NS_OK; #else // Create IFRAME element nsCOMPtr 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 iframeNode; nsCOMPtr 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& domNode) { nsresult result; nsCOMPtr domElement = do_QueryInterface(domNode); if (!domElement) return NS_ERROR_FAILURE; int j; for (j=0; jSetAttribute(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& textNode, const nsString& aString) { nsresult result; nsCOMPtr 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 domText = do_QueryInterface(aNode); if (domText) { isPREInlineNode = PR_TRUE; } else { nsCOMPtr 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 domText( do_QueryInterface(aNode) ); if (domText) { // Text node domText->GetData(htmlString); htmlString.ReplaceChar(kNBSP, ' '); } else { nsCOMPtr 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 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 attrNode; for (j=0; jItem(j, getter_AddRefs(attrNode)); if (NS_SUCCEEDED(result)) { nsCOMPtr 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 child; result = aNode->GetFirstChild(getter_AddRefs(child)); nsAutoString htmlInner; while (child) { nsAutoString innerString; ToHTMLString(child, newIndentString, innerString, deepContent, isPRENode); htmlInner += innerString; nsCOMPtr 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(""); 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& 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 moveNode(nsnull); nsCOMPtr 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 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; jGetAttribute(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"); } }