From f8cbfe6e5751bf9ee17267e8bf627a8a45b13bbf Mon Sep 17 00:00:00 2001 From: "svn%xmlterm.org" Date: Mon, 6 Mar 2000 15:16:48 +0000 Subject: [PATCH] xmlterm changes only (not part of the default build). Improved full screen implementation; added bold and underline styles. Centralized UI event handling. Added checks to limit number of output lines displayed. Added capability to interrupt hung output data streams. Nearing full backwards compatibility with xterm. git-svn-id: svn://10.0.0.236/trunk@62265 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/extensions/xmlterm/base/README | 12 +- mozilla/extensions/xmlterm/base/lineterm.h | 16 +- mozilla/extensions/xmlterm/base/ltermEscape.c | 24 + mozilla/extensions/xmlterm/base/ltermIO.c | 45 +- mozilla/extensions/xmlterm/base/ltermInput.c | 23 +- .../extensions/xmlterm/base/ltermPrivate.h | 1 + .../xmlterm/base/mozXMLTermListeners.cpp | 6 + .../xmlterm/base/mozXMLTermSession.cpp | 1216 +++++++++++++---- .../xmlterm/base/mozXMLTermSession.h | 100 +- .../xmlterm/base/mozXMLTermUtils.cpp | 27 + .../extensions/xmlterm/base/mozXMLTermUtils.h | 13 + mozilla/extensions/xmlterm/scripts/xcat | 39 +- mozilla/extensions/xmlterm/scripts/xls | 19 +- mozilla/extensions/xmlterm/scripts/xls.csh | 2 +- mozilla/extensions/xmlterm/tests/DocStream.c | 6 +- mozilla/extensions/xmlterm/tests/HelloWorld.c | 6 +- .../xmlterm/ui/content/XMLTermCommands.js | 11 +- .../xmlterm/ui/content/xmlterm.html | 189 ++- .../xmlterm/ui/content/xmlterm2.html | 438 +++++- .../xmlterm/ui/content/xmlterm2.xul | 2 +- .../extensions/xmlterm/ui/skin/xmltpage.css | 11 +- 21 files changed, 1807 insertions(+), 399 deletions(-) diff --git a/mozilla/extensions/xmlterm/base/README b/mozilla/extensions/xmlterm/base/README index 42ac1dc0bf6..44542fa84d9 100644 --- a/mozilla/extensions/xmlterm/base/README +++ b/mozilla/extensions/xmlterm/base/README @@ -9,9 +9,11 @@ of invoking XMLterm. XMLterm control-flow/layering hierarchy ======================================= - mozilla-executable xmlterm-executable + mozilla-executable geckoterm-executable | | - ../ui/content/xmlterm.html | + ../ui/content/xmlterm.xul | + | | + ../ui/content/xmlterm.html ../ui/content/xmlterm.html | | mozXMLTermShell ../geckoterm/mozGeckoTerm | | | @@ -24,11 +26,11 @@ of invoking XMLterm. mozXMLTermSession mozLineTermAux mozXMLTermListeners | | | | | | - mozXMLTermStream... (LineTerm) (user input) - (rendering ...) (pseudo-TTY/shell) + (rendering...) (LineTerm) (user input) + (pseudo-TTY/shell) -2 Dec 1999 +1 Mar 2000 =========================================================================== FILES: diff --git a/mozilla/extensions/xmlterm/base/lineterm.h b/mozilla/extensions/xmlterm/base/lineterm.h index a65c22e084b..579c1ae79f8 100644 --- a/mozilla/extensions/xmlterm/base/lineterm.h +++ b/mozilla/extensions/xmlterm/base/lineterm.h @@ -273,10 +273,11 @@ int lterm_metacomplete(int lterm, const UNICHAR *buf, int count); * OPCODES contains a bit mask describing the type of output (see below). * Using Extended Backus-Naur Form notation: * - * OPCODES ::= STREAMDATA NEWLINE? + * OPCODES ::= STREAMDATA NEWLINE? ERROR? * COOKIESTR? DOCSTREAM? XMLSTREAM? JSSTREAM? WINSTREAM? * if StreamMode data is being returned. - * (NEWLINE, if set, denotes that the stream has terminated) + * (NEWLINE, if set, denotes that the stream has terminated; + * if ERROR is also set, it means that the stream has terminated abnormally.) * * OPCODES ::= SCREENDATA BELL? ( OUTPUT | CLEAR | INSERT | DELETE | SCROLL )? * if ScreenMode data is being returned. @@ -337,12 +338,13 @@ int lterm_read(int lterm, int timeout, UNICHAR *buf, int count, #define LTERM_COMPLETION_CODE 0x1000U /* Completion requested */ #define LTERM_NEWLINE_CODE 0x2000U /* Complete (new) line */ #define LTERM_HIDE_CODE 0x4000U /* Hide output */ +#define LTERM_ERROR_CODE 0x8000U /* Error in output */ -#define LTERM_COOKIESTR_CODE 0x8000U /* Stream prefixed with cookie */ -#define LTERM_DOCSTREAM_CODE 0x10000U /* Stream contains complete document */ -#define LTERM_XMLSTREAM_CODE 0x20000U /* Stream contains XML, not HTML */ -#define LTERM_JSSTREAM_CODE 0x40000U /* Stream contains Javascript */ -#define LTERM_WINSTREAM_CODE 0x80000U /* Display stream in entire window */ +#define LTERM_COOKIESTR_CODE 0x10000U /* Stream prefixed with cookie */ +#define LTERM_DOCSTREAM_CODE 0x20000U /* Stream contains complete document */ +#define LTERM_XMLSTREAM_CODE 0x40000U /* Stream contains XML, not HTML */ +#define LTERM_JSSTREAM_CODE 0x80000U /* Stream contains Javascript */ +#define LTERM_WINSTREAM_CODE 0x100000U /* Display stream in entire window */ /* LTERM/XTERM 16-bit style mask: diff --git a/mozilla/extensions/xmlterm/base/ltermEscape.c b/mozilla/extensions/xmlterm/base/ltermEscape.c index 6fed6ad1f0a..47905b00c93 100644 --- a/mozilla/extensions/xmlterm/base/ltermEscape.c +++ b/mozilla/extensions/xmlterm/base/ltermEscape.c @@ -741,6 +741,11 @@ static int ltermProcessCSISequence(struct lterms *lts, const UNICHAR *buf, * Ps = 37 / 47 fg/bg White * Ps = 39 / 49 fg/bg Default */ + + /* Enforce default value for parameter (hack!) */ + if (paramCount == 0) + param1 = 0; + LTERM_LOG(ltermProcessCSISequence,52,("Character Attr %d\n", param1)); switch (param1) { case 0: @@ -868,6 +873,7 @@ static int ltermProcessDECPrivateMode(struct lterms *lts, const int *paramValues, int paramCount, UNICHAR uch, int *opcodes) { struct LtermOutput *lto = &(lts->ltermOutput); + int param1, param2; LTERM_LOG(ltermProcessDECPrivateMode,50,("ch='%c', cursorChar=%d, Chars=%d\n", (char) uch, lto->outputCursorChar, lto->outputChars)); @@ -875,13 +881,31 @@ static int ltermProcessDECPrivateMode(struct lterms *lts, /* Set returned opcodes to zero */ *opcodes = 0; + /* Default parameter values: 1, 1 */ + param1 = (paramCount > 0) ? paramValues[0] : 1; + param2 = (paramCount > 1) ? paramValues[1] : 1; + switch (uch) { case U_h_CHAR: /* DEC Private Mode Set (DECSET) */ LTERM_LOG(ltermProcessDECPrivateMode,2,("Unimplemented %c\n", (char) uch)); + if ((param1 % 100) == 47) { + /* Switch to screen mode */ + if (lto->outputMode == LTERM2_LINE_MODE) { + ltermSwitchToScreenMode(lts); + *opcodes = LTERM_SCREENDATA_CODE | LTERM_CLEAR_CODE; + } + } return 0; case U_l_CHAR: /* DEC Private Mode Reset (DECRST) */ LTERM_LOG(ltermProcessDECPrivateMode,2,("Unimplemented %c\n", (char) uch)); + if ((param1 % 100) == 47) { + /* Switch to line mode */ + if (lto->outputMode == LTERM1_SCREEN_MODE) { + ltermSwitchToLineMode(lts); + *opcodes = LTERM_LINEDATA_CODE; + } + } return 0; case U_r_CHAR: /* Restore previously saved DEC Private Mode Values */ diff --git a/mozilla/extensions/xmlterm/base/ltermIO.c b/mozilla/extensions/xmlterm/base/ltermIO.c index 3666f2cd96b..73096c9053c 100644 --- a/mozilla/extensions/xmlterm/base/ltermIO.c +++ b/mozilla/extensions/xmlterm/base/ltermIO.c @@ -438,7 +438,8 @@ int ltermWrite(struct lterms *lts, int *opcodes) /** Reads a (possibly incomplete) line from LTERM. * The LtermRead structure *LTR contains both input and output parameters, * returning opcodes values of - * OPCODES ::= STREAMDATA NEWLINE? COOKIESTR? DOCSTREAM? XMLSTREAM? WINSTREAM? + * OPCODES ::= STREAMDATA NEWLINE? ERROR? + * COOKIESTR? DOCSTREAM? XMLSTREAM? WINSTREAM? * if StreamMode data is being returned. * * OPCODES ::= SCREENDATA BELL? ( OUTPUT | CLEAR | INSERT | DELETE | SCROLL )? @@ -790,10 +791,27 @@ int ltermRead(struct lterms *lts, struct LtermRead *ltr, int timeout) return -1; } +/** Interrupts output operations in response to an input TTY interrupt signal + * @return 0 if successful, + * -1 if an error occurred + */ +int ltermInterruptOutput(struct lterms *lts) +{ + struct LtermOutput *lto = &(lts->ltermOutput); + + if (lto->outputMode == LTERM0_STREAM_MODE) { + /* Abnormally terminate output stream */ + lto->streamOpcodes |= LTERM_ERROR_CODE; + } + + return 0; +} + /** Returns stream data from STDOUT * (Auxiliary procedure for ltermRead.) - * OPCODES ::= STREAMDATA NEWLINE? COOKIESTR? DOCSTREAM? XMLSTREAM? WINSTREAM? + * OPCODES ::= STREAMDATA NEWLINE? ERROR? + * COOKIESTR? DOCSTREAM? XMLSTREAM? WINSTREAM? * The LtermRead structure *LTR contains both input and output parameters. * @return 0 if successful, * -1 if an error occurred while reading, @@ -806,9 +824,28 @@ static int ltermReturnStreamData(struct lterms *lts, struct LtermRead *ltr) LTERM_LOG(ltermReturnStreamData,30,("start\n")); - if (lto->decodedChars == 0) + if (lto->streamOpcodes & LTERM_ERROR_CODE) { + /* Error in stream output; terminate stream abnormally */ + LTERM_LOG(ltermReturnStreamData,32,("Error termination of STREAM mode\n")); + + if (lto->savedOutputMode == LTERM1_SCREEN_MODE) { + if (ltermSwitchToScreenMode(lts) != 0) + return -1; + } else { + if (ltermSwitchToLineMode(lts) != 0) + return -1; + } + + ltr->opcodes = lto->streamOpcodes | LTERM_STREAMDATA_CODE + | LTERM_NEWLINE_CODE; + ltr->read_count = 0; return 0; + } else if (lto->decodedChars == 0) { + /* No output data available; return null opcode */ + return 0; + } + ltr->opcodes = LTERM_STREAMDATA_CODE | lto->streamOpcodes; if (ucslen(lto->streamTerminator) == 0) { @@ -866,7 +903,7 @@ static int ltermReturnStreamData(struct lterms *lts, struct LtermRead *ltr) if (ltermSwitchToLineMode(lts) != 0) return -1; } - LTERM_LOG(ltermReturnStreamData,22,("terminating STREAM mode\n")); + LTERM_LOG(ltermReturnStreamData,32,("terminating STREAM mode\n")); } } diff --git a/mozilla/extensions/xmlterm/base/ltermInput.c b/mozilla/extensions/xmlterm/base/ltermInput.c index 325dd5d945b..3359c11d0da 100644 --- a/mozilla/extensions/xmlterm/base/ltermInput.c +++ b/mozilla/extensions/xmlterm/base/ltermInput.c @@ -460,6 +460,12 @@ static int ltermLineInput(struct lterms *lts, uch = U_LINEFEED; /* essentially newline behaviour otherwise */ } + if (uch == lts->control[TTYINTERRUPT]) { + /* Interrupt output operations, if necessary */ + if (ltermInterruptOutput(lts) != 0) + return -1; + } + if (metaInput) { /* meta input; do not send line */ *opcodes |= LTERM_META_CODE; @@ -536,6 +542,15 @@ static int ltermLineInput(struct lterms *lts, case U_CTL_Y: /* yank */ case U_TAB: /* command completion */ + if ( (lti->inputChars == 0) && (uch == U_CTL_D) && + !((lts->processType == LTERM_TCSH_PROCESS) && + (lti->inputMode >= LTERM3_COMPLETION_MODE)) ) { + /* If lone ^D and not TCSH completion, simply transmit it */ + if (ltermSendData(lts, &uch, 1) != 0) + return -1; + return 0; + } + /* Assert that completion character occurs at end of buffer; * enforced by lterm_write. */ @@ -633,14 +648,6 @@ static int ltermRequestCompletion(struct lterms *lts, UNICHAR uch) LTERM_LOG(ltermRequestCompletion,40, ("++++++++++++ COMPLETION REQUEST uch=0x%X\n\n", uch)); - if ((uch == U_CTL_D) && - !(lts->processType == LTERM_TCSH_PROCESS)){ - /* If ^D and not TCSH, simply transmit character */ - if (ltermSendData(lts, &uch, 1) != 0) - return -1; - return 0; - } - switch (uch) { case U_TAB: /* Send line and copy to echo buffer */ diff --git a/mozilla/extensions/xmlterm/base/ltermPrivate.h b/mozilla/extensions/xmlterm/base/ltermPrivate.h index 0847b13d916..8f80a296ad3 100644 --- a/mozilla/extensions/xmlterm/base/ltermPrivate.h +++ b/mozilla/extensions/xmlterm/base/ltermPrivate.h @@ -535,6 +535,7 @@ extern LtermGlobal ltermGlobal; /* Visible prototypes */ /* ltermIO.c */ +int ltermInterruptOutput(struct lterms *lts); int ltermSendLine(struct lterms *lts, UNICHAR uch, int echoControl, int completionCode); int ltermRead(struct lterms *lts, struct LtermRead *ltr, int timeout); diff --git a/mozilla/extensions/xmlterm/base/mozXMLTermListeners.cpp b/mozilla/extensions/xmlterm/base/mozXMLTermListeners.cpp index 41e300ee230..3e0c3458d1f 100644 --- a/mozilla/extensions/xmlterm/base/mozXMLTermListeners.cpp +++ b/mozilla/extensions/xmlterm/base/mozXMLTermListeners.cpp @@ -330,6 +330,12 @@ mozXMLTermKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN: JSCommand = "ScrollPageDown"; break; + case nsIDOMKeyEvent::DOM_VK_F1: + JSCommand = "HideAll"; + break; + case nsIDOMKeyEvent::DOM_VK_F2: + JSCommand = "ShowAll"; + break; default: // ignore event without consuming return NS_OK; } diff --git a/mozilla/extensions/xmlterm/base/mozXMLTermSession.cpp b/mozilla/extensions/xmlterm/base/mozXMLTermSession.cpp index c05214abeea..46deae76297 100644 --- a/mozilla/extensions/xmlterm/base/mozXMLTermSession.cpp +++ b/mozilla/extensions/xmlterm/base/mozXMLTermSession.cpp @@ -74,9 +74,12 @@ const char* const mozXMLTermSession::sessionElementNames[] = { "command", "stdin", "stdout", - "stderr" + "stderr", + "mixed", + "warning" }; +// Should HTML event names should always be in lower case for DOM to work? const char* const mozXMLTermSession::sessionEventNames[] = { "click", "dblclick" @@ -119,7 +122,7 @@ mozXMLTermSession::mozXMLTermSession() : mStartEntryNode(nsnull), mCurrentEntryNode(nsnull), - mMaxHistory(50), + mMaxHistory(20), mStartEntryNumber(0), mCurrentEntryNumber(0), @@ -142,9 +145,10 @@ mozXMLTermSession::mozXMLTermSession() : mMetaCommandType(NO_META_COMMAND), mAutoDetect(FIRST_LINE), - mLineBreakNeeded(false), mFirstOutputLine(false), + mEntryOutputLines(0), + mPreTextBufferLines(0), mPreTextIncomplete(""), mPreTextBuffered(""), mPreTextDisplayed(""), @@ -456,7 +460,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, { PRInt32 opcodes, opvals, buf_row, buf_col; PRUnichar *buf_str, *buf_style; - PRBool newline, streamData, screenData; + PRBool newline, errorFlag, streamData, screenData; nsAutoString bufString, bufStyle; XMLT_LOG(mozXMLTermSession::ReadAll,60,("\n")); @@ -490,6 +494,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, 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; @@ -507,8 +512,8 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, // Initiate screen mode XMLT_LOG(mozXMLTermSession::ReadAll,62,("Initiate SCREEN mode\n")); - // Break stream output display - result = BreakOutput(); + // Break output display + result = BreakOutput(PR_FALSE); if (NS_FAILED(result)) break; @@ -534,12 +539,26 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, // Delete screen element nsCOMPtr resultNode; mSessionNode->RemoveChild(mScreenNode, getter_AddRefs(resultNode)); + if (NS_FAILED(result)) + break; mScreenNode = nsnull; if (mRestoreInputEcho) { lineTermAux->SetEchoFlag(true); mRestoreInputEcho = false; } + + // Show the caret + // WORKAROUND for some unknown bug in the full screen implementation. + // Without this, if you delete a line using "vi" and save the file, + // the cursor suddenly disappears + nsCOMPtr caret; + if (NS_SUCCEEDED(mPresShell->GetCaret(getter_AddRefs(caret)))) { + caret->SetCaretVisible(PR_TRUE); + caret->SetCaretReadOnly(PR_FALSE); + } + + mPresShell->SetCaretEnabled(PR_TRUE); } if (streamData) { @@ -570,9 +589,9 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, } } else { - // Insecure stream; treat as text fragment + // Insecure stream; do not display streamURL = "http://in.sec.ure"; - streamMarkupType = TEXT_FRAGMENT; + streamMarkupType = INSECURE_FRAGMENT; } if (!(opcodes & LTERM_JSSTREAM_CODE) && @@ -604,8 +623,12 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, mEntryHasOutput = true; } + if (errorFlag) { + mOutputMarkupType = INCOMPLETE_FRAGMENT; + } + // Break stream output display - result = BreakOutput(); + result = BreakOutput(PR_TRUE); if (NS_FAILED(result)) break; @@ -669,7 +692,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, } for (row=0; row < opvals; row++) - NewRow(rowNode); + NewRow(rowNode, getter_AddRefs(resultNode)); } } else if (opcodes & LTERM_DELETE_CODE) { @@ -706,7 +729,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, } for (row=0; row < opvals; row++) - NewRow(rowNode); + NewRow(rowNode, getter_AddRefs(resultNode)); } } else if (opcodes & LTERM_SCROLL_CODE) { @@ -729,7 +752,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, break; } - // Determine cursor position + // Determine cursor position and position cursor PRInt32 cursorRow = 0; PRInt32 cursorCol = 0; result = lineTermAux->GetCursorRow(&cursorRow); @@ -738,26 +761,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, XMLT_LOG(mozXMLTermSession::ReadAll,62, ("cursorRow=%d, cursorCol=%d\n", cursorRow, cursorCol)); - // Get selection - nsCOMPtr selection; - - result = mPresShell->GetSelection(SELECTION_NORMAL, - getter_AddRefs(selection)); - if (NS_SUCCEEDED(result) && selection) { - // Collapse selection to cursor position - nsCOMPtr cursTextNode; - PRInt32 offset; - - result = GetScreenText(cursorRow, cursorCol, - getter_AddRefs(cursTextNode), &offset); - - if (NS_FAILED(result) || !cursTextNode) - break; - - result = selection->Collapse(cursTextNode, offset); - if (NS_FAILED(result)) - break; - } + result = PositionScreenCursor(cursorRow, cursorCol); } else { // Process line data @@ -875,7 +879,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, // Display meta command if (mEntryHasOutput) { // Break previous output display - result = BreakOutput(); + result = BreakOutput(PR_FALSE); // Create new entry block result = NewEntry(promptStr); @@ -1008,7 +1012,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, break; // Break metacommand output display - result = BreakOutput(); + result = BreakOutput(PR_FALSE); } // Reset newline flag @@ -1024,7 +1028,7 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, // Prompt line if (mEntryHasOutput) { // Break previous output display - result = BreakOutput(); + result = BreakOutput(PR_FALSE); // Create new entry block result = NewEntry(promptStr); @@ -1110,10 +1114,11 @@ NS_IMETHODIMP mozXMLTermSession::ReadAll(mozILineTermAux* lineTermAux, // Flush output, splitting off incomplete line result = FlushOutput(SPLIT_INCOMPLETE_FLUSH); - // printf("mozXMLTermSession::ReadAll (flush)\n"); + if (mEntryHasOutput) + PositionOutputCursor(lineTermAux); + result = mPresShell->ScrollSelectionIntoView(SELECTION_NORMAL, SELECTION_FOCUS_REGION); - } // Scroll frame (ignore result) @@ -1290,13 +1295,16 @@ NS_IMETHODIMP mozXMLTermSession::InitStream(const nsString& streamURL, streamMarkupType)); // Break previous output display - result = BreakOutput(); + result = BreakOutput(PR_FALSE); if (NS_FAILED(result)) return result; - if ((streamMarkupType == TEXT_FRAGMENT) || - (streamMarkupType == JS_FRAGMENT) || - (streamMarkupType == HTML_FRAGMENT)) { + if ((streamMarkupType == TEXT_FRAGMENT) || + (streamMarkupType == JS_FRAGMENT) || + (streamMarkupType == HTML_FRAGMENT) || + (streamMarkupType == INSECURE_FRAGMENT) || + (streamMarkupType == OVERFLOW_FRAGMENT) || + (streamMarkupType == INCOMPLETE_FRAGMENT)) { // Initialize fragment buffer mFragmentBuffer = ""; @@ -1379,18 +1387,22 @@ NS_IMETHODIMP mozXMLTermSession::InitStream(const nsString& streamURL, /** Breaks output display by flushing and deleting incomplete lines */ -NS_IMETHODIMP mozXMLTermSession::BreakOutput(void) +NS_IMETHODIMP mozXMLTermSession::BreakOutput(PRBool positionCursorBelow) { nsresult result; - XMLT_LOG(mozXMLTermSession::BreakOutput,70,("mOutputMarkupType=%d\n", - mOutputMarkupType)); + XMLT_LOG(mozXMLTermSession::BreakOutput,70, + ("positionCursorBelow=%x, mOutputMarkupType=%d\n", + positionCursorBelow, mOutputMarkupType)); if (!mEntryHasOutput) return NS_OK; switch (mOutputMarkupType) { + case INSECURE_FRAGMENT: + case OVERFLOW_FRAGMENT: + case INCOMPLETE_FRAGMENT: case TEXT_FRAGMENT: { // Display text fragment using new SPAN node @@ -1408,6 +1420,20 @@ NS_IMETHODIMP mozXMLTermSession::BreakOutput(void) result = mOutputBlockNode->AppendChild(spanNode, getter_AddRefs(resultNode)); + // Handle stream output error messages + switch (mOutputMarkupType) { + case INSECURE_FRAGMENT: + mFragmentBuffer = "XMLTerm: *Error* Insecure stream data; is LTERM_COOKIE set?"; + break; + + case INCOMPLETE_FRAGMENT: + mFragmentBuffer = "XMLTerm: *Error* Incomplete stream data"; + break; + + default: + break; + } + // Display text result = SetDOMText(textNode, mFragmentBuffer); if (NS_FAILED(result)) @@ -1467,6 +1493,7 @@ NS_IMETHODIMP mozXMLTermSession::BreakOutput(void) if (NS_FAILED(result)) return result; + mPreTextBufferLines = 0; mPreTextBuffered = ""; mPreTextDisplayed = ""; mOutputDisplayNode = nsnull; @@ -1478,6 +1505,10 @@ NS_IMETHODIMP mozXMLTermSession::BreakOutput(void) // Revert to plain text type mOutputMarkupType = PLAIN_TEXT; + if (positionCursorBelow) { + PositionOutputCursor(nsnull); + } + return NS_OK; } @@ -1512,14 +1543,32 @@ NS_IMETHODIMP mozXMLTermSession::ProcessOutput(const nsString& aString, 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) { - mFragmentBuffer += aString; - if (newline) - mFragmentBuffer += '\n'; + PRInt32 strLen = mFragmentBuffer.Length()+aString.Length(); + + if (strLen < 100000) { + mFragmentBuffer += aString; + if (newline) + mFragmentBuffer += '\n'; + + } else { + mOutputMarkupType = OVERFLOW_FRAGMENT; + mFragmentBuffer = "XMLTerm: *Error* Stream data overflow ("; + mFragmentBuffer.Append(strLen,10); + mFragmentBuffer.Append(" chars)"); + break; + + } } break; @@ -1555,10 +1604,151 @@ NS_IMETHODIMP mozXMLTermSession::ProcessOutput(const nsString& aString, } +/** Ensures the total number of output lines stays within a limit + * by deleting the oldest output line. + * @param deleteAllOld if true, delete all previous display nodes + * (excluding the current one) + */ +NS_IMETHODIMP mozXMLTermSession::LimitOutputLines(PRBool deleteAllOld) +{ + nsresult result; + nsAutoString attValue; + + XMLT_LOG(mozXMLTermSession::LimitOutputLines,70, + ("deleteAllOld=%d, mEntryOutputLines=%d\n", + deleteAllOld, mEntryOutputLines)); + + nsCOMPtr firstChild; + result = mOutputBlockNode->GetFirstChild(getter_AddRefs(firstChild)); + if (NS_FAILED(result) || !firstChild) + return NS_ERROR_FAILURE; + + result = mozXMLTermUtils::GetNodeAttribute(firstChild, "class", attValue); + if (NS_FAILED(result)) + return result; + + if (!attValue.Equals(sessionElementNames[WARNING_ELEMENT])) { + // Create warning message element + nsCOMPtr divNode, textNode; + nsAutoString tagName = "div"; + nsAutoString elementName = sessionElementNames[WARNING_ELEMENT]; + result = NewElementWithText(tagName, elementName, -1, + mOutputBlockNode, divNode, textNode, + firstChild); + if (NS_FAILED(result) || !divNode || !textNode) + return NS_ERROR_FAILURE; + + firstChild = divNode; + + nsAutoString warningMsg ="XMLTerm: *WARNING* Command output truncated to "; + warningMsg.Append(300,10); + warningMsg.Append(" lines"); + result = SetDOMText(textNode, warningMsg); + } + + PR_ASSERT(mOutputDisplayNode != firstChild); + + nsCOMPtr nextChild; + + PRInt32 decrementedLineCount = 0; + + for (;;) { + result = firstChild->GetNextSibling(getter_AddRefs(nextChild)); + PR_ASSERT(NS_SUCCEEDED(result) && nextChild); + + // Do not modify current display node + if (nextChild.get() == mOutputDisplayNode.get()) + break; + + PRInt32 deleteNode = 0; + + if (deleteAllOld) { + deleteNode = 1; + + } else { + result = mozXMLTermUtils::GetNodeAttribute(nextChild, "class", attValue); + + if (NS_FAILED(result)|| (attValue.Length() == 0)) { + deleteNode = 1; + + } else { + + if (attValue.Equals(sessionElementNames[MIXED_ELEMENT])) { + // Delete single line containing mixed style output + deleteNode = 1; + decrementedLineCount = 1; + + XMLT_LOG(mozXMLTermSession::LimitOutputLines,79, + ("deleted mixed line\n")); + + } else if ( (attValue.Equals(sessionElementNames[STDIN_ELEMENT])) || + (attValue.Equals(sessionElementNames[STDOUT_ELEMENT])) || + (attValue.Equals(sessionElementNames[STDERR_ELEMENT]))) { + // Delete first line from STDIN/STDOUT/STDERR PRE output + + nsCOMPtr 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 + * @param aString string to be processed (may be null string, for dummy line) * @param aStyle style values for string (see lineterm.h) + * (may be a single Unichar, for uniform style) * (if it is a null string, STDOUT style is assumed) * @param newline true if this is a complete line of output */ @@ -1572,21 +1762,21 @@ NS_IMETHODIMP mozXMLTermSession::AppendOutput(const nsString& aString, const PRInt32 styleLength = aStyle.Length(); const PRUnichar *strStyle = aStyle.GetUnicode(); - XMLT_LOG(mozXMLTermSession::AppendOutput,70,("\n")); + XMLT_LOG(mozXMLTermSession::AppendOutput,70,("strLength=%d\n", strLength)); // Check if line has uniform style - PRUnichar allStyles = LTERM_STDOUT_STYLE; PRUnichar uniformStyle = LTERM_STDOUT_STYLE; + PRInt32 styleChanges = 0; if (styleLength > 0) { PRInt32 j; - allStyles = strStyle[0]; uniformStyle = strStyle[0]; - for (j=1; j 0) { + if (mPreTextBufferLines > 0) { mPreTextBuffered += '\n'; } + mPreTextBufferLines++; mPreTextBuffered += mPreTextIncomplete; mPreTextIncomplete = ""; - } - mLineBreakNeeded = false; + if (mPreTextBufferLines > 300) { + // Delete all earlier PRE/mixed blocks and first line of current block + + result = LimitOutputLines(true); + if (NS_FAILED(result)) + return result; + + // Delete first line from PRE text buffer + PRInt32 offset = mPreTextBuffered.FindChar((PRUnichar) U_LINEFEED); + if (offset < 0) { + mPreTextBuffered = ""; + } else { + mPreTextBuffered.Cut(0,offset+1); + } + + mPreTextBufferLines--; + + } else if (mEntryOutputLines+mPreTextBufferLines > 300) { + // Delete oldest PRE/mixed line so as to stay within the limit + + result = LimitOutputLines(false); + if (NS_FAILED(result)) + return result; + } + } XMLT_LOG(mozXMLTermSession::AppendOutput,72, - ("mPreTextDisplayed.Length()=%d, mPreTextBuffered.Length()=%d\n", - mPreTextDisplayed.Length(), mPreTextBuffered.Length())); + ("mPreTextDisplayed.Length()=%d, mPreTextBufferLines()=%d\n", + mPreTextDisplayed.Length(), mPreTextBufferLines)); } else { - // Create uniform style SPAN display node - OutputDisplayType spanDisplayType; - nsAutoString elementName = ""; + // Create uniform style DIV display node - if (uniformStyle == LTERM_STDERR_STYLE) { - // STDERR style - spanDisplayType = SPAN_STDERR_NODE; - elementName = sessionElementNames[STDERR_ELEMENT]; - XMLT_LOG(mozXMLTermSession::AppendOutput,72,("SPAN_STDERR_NODE\n")); + XMLT_LOG(mozXMLTermSession::AppendOutput,72,("DIV_MIXED_NODE\n")); - } else { - // STDOUT style (the default output style) - spanDisplayType = SPAN_STDOUT_NODE; - elementName = sessionElementNames[STDOUT_ELEMENT]; - XMLT_LOG(mozXMLTermSession::AppendOutput,72,("SPAN_STDOUT_NODE\n")); - } - - PRBool preDisplay = (mOutputDisplayType == PRE_STDOUT_NODE) || - (mOutputDisplayType == PRE_STDERR_NODE); - - if (preDisplay) { - // Flush buffer, clearing incomplete line - result = FlushOutput(CLEAR_INCOMPLETE_FLUSH); - } - - if (mLineBreakNeeded) { - // Insert line break element - result = NewBreak(mOutputBlockNode); - mLineBreakNeeded = false; - } - - if (elementName.Length() > 0) { - // Create new SPAN node - nsCOMPtr spanNode, textNode; - nsAutoString tagName = "span"; - result = NewElementWithText(tagName, elementName, -1, - mOutputBlockNode, spanNode, textNode); - - if (NS_FAILED(result) || !spanNode || !textNode) - return NS_ERROR_FAILURE; - - XMLT_LOG(mozXMLTermSession::AppendOutput,72, - ("Creating new SPAN node\n")); - - if (mOutputDisplayNode != nsnull) { - // Replace node - nsCOMPtr resultNode; - result = mOutputBlockNode->ReplaceChild(spanNode, mOutputDisplayNode, - getter_AddRefs(resultNode)); - } else { - // Append node - nsCOMPtr resultNode; - result = mOutputBlockNode->AppendChild(spanNode, - getter_AddRefs(resultNode)); - } - - mOutputDisplayType = spanDisplayType; - mOutputDisplayNode = spanNode; - mOutputTextNode = textNode; - } - - // Display line - result = SetDOMText(mOutputTextNode, aString); + // Flush buffer, clearing incomplete line + result = FlushOutput(CLEAR_INCOMPLETE_FLUSH); if (NS_FAILED(result)) + return result; + + // Create new DIV node + nsAutoString elementName = sessionElementNames[MIXED_ELEMENT]; + nsCOMPtr divNode; + nsAutoString tagName = "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 = "span"; + + PR_ASSERT(strLength > 0); + + for (k=1; k 300) { + // Delete oldest PRE/mixed line so as to stay within the limit + result = LimitOutputLines(false); + if (NS_FAILED(result)) + return result; + } + + if (passwordPrompt) { + result = mOutputBlockNode->RemoveChild(mOutputDisplayNode, + getter_AddRefs(resultNode)); + } mOutputDisplayType = NO_NODE; mOutputDisplayNode = nsnull; mOutputTextNode = nsnull; - mLineBreakNeeded = true; } } @@ -1874,11 +2130,11 @@ NS_IMETHODIMP mozXMLTermSession::AppendLineLS(const nsString& aString, for (j=0; j= 0, all '#' characters in aString - * are substituted by entryNumber) + * (if entryNumber >= 0, all '#' characters in + * id/onclick/ondblclick attribute values are substituted + * with entryNumber) * @param beforeNode child node before which to insert fragment; * if null, insert after last child node * (default value is null) @@ -2019,6 +2276,15 @@ NS_IMETHODIMP mozXMLTermSession::AppendLineLS(const nsString& aString, 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)); @@ -2068,7 +2334,7 @@ NS_IMETHODIMP mozXMLTermSession::AppendLineLS(const nsString& aString, return result; // Refresh attributes of inserted child node (deep) - DeepRefreshAttributes(resultNode, entryNumber); + DeepRefreshEventHandlers(resultNode); childNode = nextChild; } @@ -2078,58 +2344,268 @@ NS_IMETHODIMP mozXMLTermSession::AppendLineLS(const nsString& aString, } -/** Deep refresh of selected attributes for DOM elements - * (WORKAROUND for inserting HTML fragments properly) - * @param domNode DOM node of branch to be refreshed - * @param entryNumber entry number (default value = -1) - * (if entryNumber >= 0, all '#' characters in event - * handler scripts are substituted by entryNumber) +/** Substitute all occurrences of the '#' character in aString with + * aNumber, if aNumber >= 0; + * @ param aString string to be modified + * @ param aNumber number to substituted */ -NS_IMETHODIMP mozXMLTermSession::DeepRefreshAttributes( - nsCOMPtr& domNode, - PRInt32 entryNumber) +void mozXMLTermSession::SubstituteCommandNumber(nsString& aString, + PRInt32 aNumber) { - static const PRInt32 REFRESH_ATTRIBUTE_NAMES = 3; - static const char* const refreshAttributeNames[] = { - "id", - "onclick", - "ondblclick" - }; - nsresult result; + if (aNumber < 0) + return; + + PRInt32 numberOffset; nsAutoString numberString = ""; - if (entryNumber >= 0) - numberString.Append(entryNumber,10); + numberString.Append(aNumber,10); + + for (;;) { + // Search for '#' character + numberOffset = aString.FindChar((PRUnichar) '#'); + + if (numberOffset < 0) + break; + + // Substitute '#' with supplied number + aString.Cut(numberOffset,1); + aString.Insert(numberString, numberOffset); + } +} + + +/** Sanitize event handler attribute values by imposing syntax checks. + * @param aAttrValue attribute value to be sanitized + * @param aEventName name of event being handled ("click", "dblclick", ...) + */ +void mozXMLTermSession::SanitizeAttribute(nsString& aAttrValue, + const char* aEventName) +{ + // ****************NOTE*************** + // At the moment this method simply prevents the word function and the + // the character '{' both occurring in the event handler attribute. + // NEEDS TO BE IMPROVED TO ENFORCE STRICTER REQUIREMENTS + // such as: the event handler attribute should always be of the form + // "return EventHandler(str_arg1, num_arg2, str_arg3, str_arg4);" + + if ((aAttrValue.FindChar((PRUnichar)'{') >= 0) && + (aAttrValue.Find("function") >= 0)) { + // Character '{' and string "function" both found in attribute value; + // set to null string + + char* temCString = aAttrValue.ToNewCString(); + XMLT_WARNING("mozXMLTermSession::SanitizeAttribute: Warning - deleted attribute on%s='%s'\n", aEventName, temCString); + nsCRT::free(temCString); + + aAttrValue = ""; + } + + return; +} + + +/** Deep sanitizing of event handler attributes ("on*") prior to insertion + * of HTML fragments, to enfore consistent UI behaviour in XMLTerm and + * for security. The following actions are carried out: + * 1. Any SCRIPT tags in the fragment are simply deleted + * 2. All event handler attributes, except a few selected ones, are deleted. + * 3. The retained event handler attribute values are subject to strict + * checks. + * 4. If entryNumber >= 0, all '#' characters in the ID attribute and + * retained event handler attributes are substituted with entryNumber. + * + * @param domNode DOM node for HTML fragment to be sanitized + * @param parentNode parent DOM node (needed to delete SCRIPT elements; + * set to null if root element) + * @param entryNumber entry number (default value = -1) + */ +NS_IMETHODIMP mozXMLTermSession::DeepSanitizeFragment( + nsCOMPtr& 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 = ""; + result = domElement->GetTagName(tagName); + + if (NS_SUCCEEDED(result) && tagName.EqualsIgnoreCase("script")) { + // Remove script element and return + + XMLT_WARNING("mozXMLTermSession::DeepSanitizeFragment: Warning - rejected SCRIPT element in inserted HTML fragment\n"); + + if (parentNode) { + nsCOMPtr 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.Length() > 0)) { + // 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 = ""; + + 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.EqualsIgnoreCase("on")) { + // Delete event handler attribute + + XMLT_LOG(mozXMLTermSession::DeepSanitizeFragment,79, + ("Deleting event handler in fragment\n")); + + result = domElement->SetAttribute(attrName, nullStr); + if (NS_FAILED(result)) + return result; + } + } + + } + } + + } + } + + } + } + + if (entryNumber >= 0) { + // Process ID attribute + attName = "id"; + + result = domElement->GetAttribute(attName, attValue); + + if (NS_SUCCEEDED(result) && (attValue.Length() > 0)) { + // Modify attribute value + SubstituteCommandNumber(attValue, entryNumber); + domElement->SetAttribute(attName, attValue); + } + } + + for (j=0; j 0) { + SubstituteCommandNumber(attValue, entryNumber); + + // Sanitize attribute value + SanitizeAttribute(attValue, sessionEventNames[j]); + + // Insert attribute value + domElement->SetAttribute(attName, attValue); + } + } + + } + + // Iterate over all child nodes for deep refresh + nsCOMPtr 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; - for (j=0; jGetAttribute(attName, attValue); + if (NS_SUCCEEDED(result) && (attValue.Length() > 0)) { - - if (entryNumber >= 0) { - // Search for number character - PRInt32 numberOffset = attValue.FindChar((PRUnichar) '#'); - - if (numberOffset >= 0) { - // Substitute number sign with supplied number - attValue.Cut(numberOffset,1); - attValue.Insert(numberString, numberOffset); - } - } - // Refresh attribute value domElement->SetAttribute(attName, attValue); - - XMLT_LOG(mozXMLTermSession::DeepRefreshEventAttributes,82, - ("Refreshing on%s attribute\n",sessionEventNames[j] )); } } @@ -2140,7 +2616,7 @@ NS_IMETHODIMP mozXMLTermSession::DeepRefreshAttributes( return NS_OK; while (child) { - DeepRefreshAttributes(child, entryNumber); + DeepRefreshEventHandlers(child); nsCOMPtr temp = child; result = temp->GetNextSibling(getter_AddRefs(child)); @@ -2168,7 +2644,8 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) flushAction, mOutputDisplayType)); PRBool preDisplay = (mOutputDisplayType == PRE_STDOUT_NODE) || - (mOutputDisplayType == PRE_STDERR_NODE); + (mOutputDisplayType == PRE_STDERR_NODE) || + (mOutputDisplayType == PRE_STDIN_NODE); if (preDisplay) { // PRE text display @@ -2178,6 +2655,10 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) 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; @@ -2190,8 +2671,8 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) // Clear incomplete PRE text mPreTextIncomplete = ""; - if (mPreTextBuffered.Length() == 0) { - // Remove last text node + if ((mPreTextBufferLines == 0) && (mPreTextBuffered.Length() == 0)) { + // Remove lone text node nsCOMPtr resultNode; result = mOutputDisplayNode->RemoveChild(mOutputTextNode, getter_AddRefs(resultNode)); @@ -2218,6 +2699,9 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) nsAutoString outString = mPreTextBuffered; outString += mPreTextIncomplete; + // Increment total output line count for entry + mEntryOutputLines += mPreTextBufferLines; + if (outString != mPreTextDisplayed) { // Display updated buffer mPreTextDisplayed = outString; @@ -2228,29 +2712,36 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) 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 != DISPLAY_INCOMPLETE_FLUSH) { + // Split/clear/close incomplete line + mOutputDisplayNode = nsnull; + mOutputDisplayType = NO_NODE; + mOutputTextNode = nsnull; - if ( (flushAction == SPLIT_INCOMPLETE_FLUSH) && - (preTextSplit.Length() > 0) ) { - // Create new PRE element with incomplete text - nsAutoString styleStr = ""; + if ( (flushAction == SPLIT_INCOMPLETE_FLUSH) && + (preTextSplit.Length() > 0) ) { + // Create new PRE element with incomplete text + nsAutoString styleStr = ""; - if (preDisplayType == PRE_STDERR_NODE) { - styleStr += (PRUnichar) LTERM_STDERR_STYLE; - } else { - styleStr += (PRUnichar) LTERM_STDOUT_STYLE; - } + if (preDisplayType == PRE_STDIN_NODE) { + styleStr += (PRUnichar) LTERM_STDIN_STYLE; - XMLT_LOG(mozXMLTermSession::FlushOutput,72,("splitting\n")); + } else if (preDisplayType == PRE_STDERR_NODE) { + styleStr += (PRUnichar) LTERM_STDERR_STYLE; - AppendOutput(preTextSplit, styleStr, false); + } else { + styleStr += (PRUnichar) LTERM_STDOUT_STYLE; } + + XMLT_LOG(mozXMLTermSession::FlushOutput,72,("splitting\n")); + + AppendOutput(preTextSplit, styleStr, false); + + FlushOutput(DISPLAY_INCOMPLETE_FLUSH); } } @@ -2269,6 +2760,7 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) mOutputDisplayNode = nsnull; mOutputDisplayType = NO_NODE; mOutputTextNode = nsnull; + } } @@ -2278,6 +2770,55 @@ NS_IMETHODIMP mozXMLTermSession::FlushOutput(FlushActionType flushAction) } +/** Positions cursor below the last output element */ +void mozXMLTermSession::PositionOutputCursor(mozILineTermAux* lineTermAux) +{ + nsresult result; + + XMLT_LOG(mozXMLTermSession::PositionOutputCursor,80,("\n")); + + if (!mOutputTextNode) { + // Append dummy output line + nsCOMPtr spanNode, textNode; + nsAutoString tagName = "span"; + nsAutoString elementName = sessionElementNames[STDOUT_ELEMENT]; + result = NewElementWithText(tagName, elementName, -1, + mOutputBlockNode, spanNode, textNode); + + if (NS_FAILED(result) || !spanNode || !textNode) + return; + + mOutputDisplayType = SPAN_DUMMY_NODE; + mOutputDisplayNode = spanNode; + mOutputTextNode = textNode; + mOutputTextOffset = 0; + } + + // Get selection + nsCOMPtr selection; + + result = mPresShell->GetSelection(SELECTION_NORMAL, + getter_AddRefs(selection)); + if (NS_SUCCEEDED(result) && selection) { + // Position cursor at end of line + nsCOMPtr domText( do_QueryInterface(mOutputTextNode) ); + nsAutoString text = ""; + domText->GetData(text); + + PRInt32 textOffset = text.Length(); + if (lineTermAux && (mOutputDisplayType == PRE_STDIN_NODE)) { + // Get cursor column + PRInt32 cursorCol = 0; + lineTermAux->GetCursorColumn(&cursorCol); + textOffset = cursorCol - mOutputTextOffset; + if (textOffset > text.Length()) + textOffset = text.Length(); + } + result = selection->Collapse(mOutputTextNode, textOffset); + } +} + + /** Scrolls document to align bottom and left margin with screen */ NS_IMETHODIMP mozXMLTermSession::ScrollToBottomLeft(void) { @@ -2578,6 +3119,8 @@ NS_IMETHODIMP mozXMLTermSession::NewEntry(const nsString& aPrompt) // No command output processed yet mEntryHasOutput = false; + mEntryOutputLines = 0; + return NS_OK; } @@ -2605,9 +3148,18 @@ NS_IMETHODIMP mozXMLTermSession::NewScreen(void) mScreenNode = divNode; // Create individual row elements + nsCOMPtr resultNode; PRInt32 row; for (row=0; row < mScreenRows; row++) { - NewRow(nsnull); + NewRow(nsnull, getter_AddRefs(resultNode)); + } + + // Collapse selection to bottom of screen (for scrolling) + result = PositionScreenCursor(0, 0); + + if (NS_SUCCEEDED(result)) { + result = mPresShell->ScrollSelectionIntoView(SELECTION_NORMAL, + SELECTION_FOCUS_REGION); } return NS_OK; @@ -2655,18 +3207,15 @@ NS_IMETHODIMP mozXMLTermSession::GetRow(PRInt32 aRow, nsIDOMNode** aRowNode) } -/** Returns DOM text node and offset corresponding to screen row/col position +/** Positions cursor to specified screen row/col position */ -NS_IMETHODIMP mozXMLTermSession::GetScreenText(PRInt32 aRow, PRInt32 aCol, - nsIDOMNode** aTextNode, - PRInt32 *aOffset) +NS_IMETHODIMP mozXMLTermSession::PositionScreenCursor(PRInt32 aRow, + PRInt32 aCol) { nsresult result; - XMLT_LOG(mozXMLTermSession::GetScreenText,60,("row=%d, col=%d\n",aRow,aCol)); - - if (!aTextNode || !aOffset) - return NS_ERROR_NULL_POINTER; + XMLT_LOG(mozXMLTermSession::PositionScreenCursor,60, + ("row=%d, col=%d\n",aRow,aCol)); // Get row node nsCOMPtr rowNode; @@ -2674,27 +3223,75 @@ NS_IMETHODIMP mozXMLTermSession::GetScreenText(PRInt32 aRow, PRInt32 aCol, if (NS_FAILED(result) || !rowNode) return NS_ERROR_FAILURE; - // Get text node - nsCOMPtr textNode; - result = rowNode->GetFirstChild(getter_AddRefs(textNode)); - if (NS_FAILED(result) || !textNode) - return result; + 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; - result = textNode->GetNodeType(&nodeType); - if (NS_FAILED(result)) - return result; + PRUint32 j; + PRInt32 prevCols = 0; + PRInt32 textOffset = 0; + nsCOMPtr textNode = nsnull; + nsCOMPtr childNode; + nsAutoString text = ""; - if (nodeType == nsIDOMNode::TEXT_NODE) { - /* Text node */ - *aTextNode = textNode.get(); - NS_ADDREF(*aTextNode); - *aOffset = aCol; + for (j=0; jItem(j, getter_AddRefs(childNode)); + if (NS_FAILED(result) || !childNode) + return NS_ERROR_FAILURE; - } else { - /* Not a text node */ - *aTextNode = nsnull; - *aOffset = 0; + 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+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; + + result = mPresShell->GetSelection(SELECTION_NORMAL, + getter_AddRefs(selection)); + + if (NS_SUCCEEDED(result) && selection) { + // Collapse selection to cursor position + result = selection->Collapse(textNode, textOffset); } return NS_OK; @@ -2706,7 +3303,8 @@ NS_IMETHODIMP mozXMLTermSession::GetScreenText(PRInt32 aRow, PRInt32 aCol, * child of the SCREEN element before beforeRowNode, or at the * end if beforeRowNode is null. */ -NS_IMETHODIMP mozXMLTermSession::NewRow(nsIDOMNode* beforeRowNode) +NS_IMETHODIMP mozXMLTermSession::NewRow(nsIDOMNode* beforeRowNode, + nsIDOMNode** resultNode) { nsresult result; @@ -2734,16 +3332,12 @@ NS_IMETHODIMP mozXMLTermSession::NewRow(nsIDOMNode* beforeRowNode) val = "1"; preElement->SetAttribute(att, val); - nsCOMPtr resultNode; - if (beforeRowNode) { // Insert row node - result = mScreenNode->InsertBefore(preNode, beforeRowNode, - getter_AddRefs(resultNode)); + result = mScreenNode->InsertBefore(preNode, beforeRowNode, resultNode); } else { // Append row node - result = mScreenNode->AppendChild(preNode, - getter_AddRefs(resultNode)); + result = mScreenNode->AppendChild(preNode, resultNode); } return NS_OK; @@ -2761,18 +3355,145 @@ NS_IMETHODIMP mozXMLTermSession::DisplayRow(const nsString& aString, PRInt32 aRow) { nsresult result; - PRInt32 offset; - XMLT_LOG(mozXMLTermSession::DisplayRow,70,("aRow=%d\n", aRow)); + const PRInt32 strLength = aString.Length(); + const PRInt32 styleLength = aStyle.Length(); + const PRUnichar *strStyle = aStyle.GetUnicode(); - nsCOMPtr rowTextNode; - result = GetScreenText(aRow, 0, getter_AddRefs(rowTextNode), &offset); - if (NS_FAILED(result) || !rowTextNode) - return result; + XMLT_LOG(mozXMLTermSession::DisplayRow,70, + ("aRow=%d, strLength=%d, styleLength=%d\n", + aRow, strLength, styleLength)); - result = SetDOMText(rowTextNode, aString); - if (NS_FAILED(result)) - return result; + // 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 = "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& blockNode, - nsCOMPtr& textNode) + nsCOMPtr& textNode, + nsIDOMNode* beforeNode) { nsresult result; XMLT_LOG(mozXMLTermSession::NewElementWithText,80,("\n")); // Create block element - result = NewElement(tagName, name, number, parentNode, blockNode); + result = NewElement(tagName, name, number, parentNode, blockNode, + beforeNode); if (NS_FAILED(result) || !blockNode) return NS_ERROR_FAILURE; @@ -2900,11 +3626,15 @@ NS_IMETHODIMP mozXMLTermSession::NewAnchor(const nsString& classAttribute, * (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) + nsCOMPtr& blockNode, + nsIDOMNode* beforeNode) { nsresult result; @@ -2934,11 +3664,21 @@ NS_IMETHODIMP mozXMLTermSession::NewElement(const nsString& tagName, } } - // Append child to parent nsCOMPtr newBlockNode = do_QueryInterface(newElement); - result = parentNode->AppendChild(newBlockNode, getter_AddRefs(blockNode)); - if (NS_FAILED(result) || !blockNode) - return NS_ERROR_FAILURE; + + 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; } @@ -3082,9 +3822,9 @@ NS_IMETHODIMP mozXMLTermSession::SetEventAttributes(const nsString& name, nsAutoString attName("on"); attName += sessionEventNames[j]; - nsAutoString attValue("return "); + nsAutoString attValue("return HandleEvent(event, '"); attValue += sessionEventNames[j]; - attValue += "XMLTerm('"; + attValue += "','"; attValue += name; attValue += "','"; attValue.Append(number,10); diff --git a/mozilla/extensions/xmlterm/base/mozXMLTermSession.h b/mozilla/extensions/xmlterm/base/mozXMLTermSession.h index 27f3515399a..2c459fc758f 100644 --- a/mozilla/extensions/xmlterm/base/mozXMLTermSession.h +++ b/mozilla/extensions/xmlterm/base/mozXMLTermSession.h @@ -118,10 +118,12 @@ protected: STDIN_ELEMENT, STDOUT_ELEMENT, STDERR_ELEMENT, + MIXED_ELEMENT, + WARNING_ELEMENT, SESSION_ELEMENT_TYPES }; - /** user-generated input event type */ + /** allowed user-generated input event type */ enum SessionEventType { CLICK_EVENT = 0, DBLCLICK_EVENT, @@ -141,9 +143,9 @@ protected: NO_NODE = 0, PRE_STDOUT_NODE, PRE_STDERR_NODE, - SPAN_STDOUT_NODE, - SPAN_STDERR_NODE, - SPAN_MIXED_NODE, + PRE_STDIN_NODE, + DIV_MIXED_NODE, + SPAN_DUMMY_NODE, OUTPUT_DISPLAY_TYPES }; @@ -155,6 +157,9 @@ protected: HTML_FRAGMENT, HTML_DOCUMENT, XML_DOCUMENT, + INSECURE_FRAGMENT, + OVERFLOW_FRAGMENT, + INCOMPLETE_FRAGMENT, OUTPUT_MARKUP_TYPES }; @@ -237,7 +242,7 @@ protected: PRBool streamIsSecure); /** Breaks output display by flushing and deleting incomplete lines */ - NS_IMETHOD BreakOutput(void); + NS_IMETHOD BreakOutput(PRBool positionCursorBelow); /** Processes output string with specified style * @param aString string to be processed @@ -251,6 +256,13 @@ protected: PRBool newline, PRBool streamOutput); + /** Ensures the total number of output lines stays within a limit + * by deleting the oldest output line. + * @param deleteAllOld if true, delete all previous display nodes + * (excluding the current one) + */ + NS_IMETHOD LimitOutputLines(PRBool deleteAllOld); + /** 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 @@ -289,15 +301,45 @@ protected: nsIDOMNode* beforeNode = nsnull, PRBool replace = false); - /** Deep refresh of selected attributes for DOM elements + /** 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 SubstituteCommandNumber(nsString& aString, + PRInt32 aNumber); + + /** Sanitize event handler attribute values by imposing syntax checks. + * @param aAttrValue attribute value to be sanitized + * @param aEventName name of event being handled ("Click", "DblClick", ...) + */ + void SanitizeAttribute(nsString& aAttrValue, + const char* aEventName); + + /** 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_IMETHOD DeepSanitizeFragment(nsCOMPtr& domNode, + nsIDOMNode* parentNode, + PRInt32 entryNumber); + + /** 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 - * @param entryNumber entry number (default value = -1) - * (if entryNumber >= 0, all '#' characters in event - * handler scripts are substituted by entryNumber) */ - NS_IMETHOD DeepRefreshAttributes(nsCOMPtr& domNode, - PRInt32 entryNumber = -1); + NS_IMETHOD DeepRefreshEventHandlers(nsCOMPtr& domNode); /** Forces display of data in output buffer * @param flushAction type of flush action: display, split-off, clear, or @@ -305,6 +347,9 @@ protected: */ NS_IMETHOD FlushOutput(FlushActionType flushAction); + /** Positions cursor below the last output element */ + void PositionOutputCursor(mozILineTermAux* lineTermAux); + /** Scrolls document to align bottom and left margin with screen */ NS_IMETHOD ScrollToBottomLeft(void); @@ -336,18 +381,17 @@ protected: */ NS_IMETHOD GetRow(PRInt32 aRow, nsIDOMNode** aRowNode); - /** Returns DOM text node and offset corresponding to screen row/col position + /** Positions cursor to specified screen row/col position */ - NS_IMETHOD GetScreenText(PRInt32 aRow, PRInt32 aCol, - nsIDOMNode** aTextNode, - PRInt32 *aOffset); + NS_IMETHOD PositionScreenCursor(PRInt32 aRow, PRInt32 aCol); /** 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_IMETHOD NewRow(nsIDOMNode* beforeRowNode); + NS_IMETHOD NewRow(nsIDOMNode* beforeRowNode, + nsIDOMNode** resultNode); /** Displays screen output string with specified style * @param aString string to be processed @@ -375,12 +419,16 @@ protected: * @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_IMETHOD NewElementWithText(const nsString& tagName, const nsString& name, PRInt32 number, nsIDOMNode* parentNode, nsCOMPtr& blockNode, - nsCOMPtr& textNode); + nsCOMPtr& textNode, + nsIDOMNode* beforeNode = nsnull); /** Creates an empty anchor (A) element with tag name tagName with attributes * CLASS="classAttribute", and ID="classAttribute#", and appends it as a @@ -407,11 +455,15 @@ protected: * (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_IMETHOD NewElement(const nsString& tagName, const nsString& name, PRInt32 number, nsIDOMNode* parentNode, - nsCOMPtr& blockNode); + nsCOMPtr& blockNode, + nsIDOMNode* beforeNode = nsnull); /** Creates a new DOM text node, and appends it as a child of the * specified parent. @@ -571,6 +623,9 @@ protected: /** current text node for command output */ nsCOMPtr mOutputTextNode; + /** line offset of current text node for command output */ + PRInt32 mOutputTextOffset; + /** current XMLTerm stream interface for stream display */ nsCOMPtr mXMLTermStream; @@ -592,13 +647,16 @@ protected: AutoDetectOption mAutoDetect; - /** flag indicating whether a line break needs to be output */ - PRBool mLineBreakNeeded; - /** flag marking the first line of output */ PRBool mFirstOutputLine; + /** total count of PRE/mixed text lines displayed for this entry */ + PRInt32 mEntryOutputLines; + + /** count of PRE complete text lines in buffer */ + PRInt32 mPreTextBufferLines; + /** buffer for incomplete line of PRE text */ nsString mPreTextIncomplete; diff --git a/mozilla/extensions/xmlterm/base/mozXMLTermUtils.cpp b/mozilla/extensions/xmlterm/base/mozXMLTermUtils.cpp index 7a1cde30c73..18e2a117373 100644 --- a/mozilla/extensions/xmlterm/base/mozXMLTermUtils.cpp +++ b/mozilla/extensions/xmlterm/base/mozXMLTermUtils.cpp @@ -38,6 +38,7 @@ #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIDOMWindowCollection.h" +#include "nsIDOMElement.h" #include "nsIDocument.h" #include "mozXMLT.h" @@ -275,6 +276,32 @@ mozXMLTermUtils::ExecuteScript(nsIDOMDocument* aDOMDocument, } +/** Returns the specified attribute value associated with a DOM node, + * or a null string if the attribute is not defined, or if the DOM node + * is not an element. + * @param aDOMNode node whose attribute is to be determined + * @param aAttName attribute to be determined + * @param aAttValue output attribute value + * @return NS_OK if no errors occurred + */ +NS_EXPORT nsresult +mozXMLTermUtils::GetNodeAttribute(nsIDOMNode* aDOMNode, + const char* aAttName, + nsString& aAttValue) +{ + XMLT_LOG(mozXMLTermUtils::GetNodeAttribute,20,("aAttName=%s\n", aAttName)); + + nsCOMPtr domElement = do_QueryInterface(aDOMNode); + if (!domElement) { + aAttValue = ""; + return NS_OK; + } + + nsAutoString attName = aAttName; + return domElement->GetAttribute(attName, aAttValue); +} + + /** Returns a timestamp string containing the local time, if at least * deltaSec seconds have elapsed since the last timestamp. Otherwise, * a null string is returned. diff --git a/mozilla/extensions/xmlterm/base/mozXMLTermUtils.h b/mozilla/extensions/xmlterm/base/mozXMLTermUtils.h index d7aa8494631..aaa72ce7584 100644 --- a/mozilla/extensions/xmlterm/base/mozXMLTermUtils.h +++ b/mozilla/extensions/xmlterm/base/mozXMLTermUtils.h @@ -103,6 +103,19 @@ class mozXMLTermUtils const nsString& aScript, nsString& aOutput); + /** Returns the specified attribute value associated with a DOM node, + * or a null string if the attribute is not defined, or if the DOM node + * is not an element. + * @param aDOMNode node whose attribute is to be determined + * @param aAttName attribute to be determined + * @param aAttValue output attribute value + * @return NS_OK if no errors occurred + */ + static NS_EXPORT nsresult + GetNodeAttribute(nsIDOMNode* aDOMNode, + const char* aAttName, + nsString& aAttValue); + /** Returns a timestamp string containing the local time, if at least * deltaSec seconds have elapsed since the last timestamp. Otherwise, * a null string is returned. diff --git a/mozilla/extensions/xmlterm/scripts/xcat b/mozilla/extensions/xmlterm/scripts/xcat index a9151b87476..695baf8e5bf 100755 --- a/mozilla/extensions/xmlterm/scripts/xcat +++ b/mozilla/extensions/xmlterm/scripts/xcat @@ -23,13 +23,14 @@ my $dir = cwd(); foreach my $url (@ARGV) { # for each argument my ($protocol, $slashpair, $username, $host, $port, $pathname); + # Check if argument is a valid URL if ( $url =~ m%\b # initiator ([a-zA-Z]\w*)?: # protocol (//)? # slashpair (?:([\w.]+)@)? # username ([\w\-]+(?:\.[\w\-]+)*)? # host (?::(\d+))? # port - (/\S*?)? # pathname + (/\S*?) # pathname (?=>|\"|\'|\s|[.,!](?:\s|\Z)|\Z) # terminator (look ahead) %x ) { @@ -45,6 +46,7 @@ foreach my $url (@ARGV) { # for each argument ## print STDERR "Not an URL; assume local file $url\n"; + # Create a file URL ($protocol, $slashpair, $username, $host, $port, $pathname) = ("file", "//", "", "", "", $url); @@ -105,7 +107,7 @@ foreach my $url (@ARGV) { # for each argument (?:([\w.]+)@)? # username ([\w\-]+(?:\.[\w\-]+)*)? # host (?::(\d+))? # port - (/\S*?)? # pathname + (/\S*?) # pathname (?=>|\"|\'|\s|[.,!](?:\s|\Z)|\Z) # terminator (look ahead) %x ) { $urlvals[$nurl] = $&; @@ -120,7 +122,7 @@ foreach my $url (@ARGV) { # for each argument $urldesc = $urlstr if !$urldesc; my $clickcmd = - qq%onclick="return clickXMLTerm('textlink',-#,'$urlstr')"%; + qq%onClick="return HandleEvent(event,'click','textlink',-\#,'$urlstr')"%; print "\e{S$cookie\a"; # HTML stream escape sequence if ($nurl >= 2) { @@ -138,25 +140,28 @@ foreach my $url (@ARGV) { # for each argument print "
";
 
         while () {
-            s/&/&/g;
-            s//">"/g;
-                s%\b                              # initiator
-                 ([a-zA-Z]\w*)?:                  # protocol
-                 (//)?                            # slashpair
-                 (?:([\w.]+)@)?                   # username
-                 ([\w\-]+(?:\.[\w\-]+)*)?         # host
-                 (?::(\d+))?                      # port
-                 (/\S*?)?                         # pathname
-                 (?=>|\"|\'|\s|[.,!](?:\s|\Z)|\Z) # terminator (look ahead)
-                 %$&%xg;
-                s/">"/>/g;
+            s/&/&/g;       # Replace & with &
+            s//">"/g;      # Temporarily replace > with ">"
+                               # to allow termination of  etc.
+
+            s%\b                              # URL word boundary
+             ([a-zA-Z]\w*)?:                  # protocol
+             (//)?                            # slashpair
+             (?:([\w.]+)@)?                   # username
+             ([\w\-]+(?:\.[\w\-]+)*)?         # host
+             (?::(\d+))?                      # port
+             (/\S*?)                          # pathname
+             (?=>|\"|\'|\s|[.,!](?:\s|\Z)|\Z) # URL terminator (look ahead)
+             %$&%xg;
+
+            s/">"/>/g;   # Replace ">" with > in the end
 
             print $_;
         }
 
         print "
"; - print "\000"; # Terminate HTML stream + print "\000"; # Terminate HTML stream close INFILE; } else { # unknown file type diff --git a/mozilla/extensions/xmlterm/scripts/xls b/mozilla/extensions/xmlterm/scripts/xls index 0c8b8364981..b5d9eb44c18 100755 --- a/mozilla/extensions/xmlterm/scripts/xls +++ b/mozilla/extensions/xmlterm/scripts/xls @@ -46,7 +46,7 @@ foreach $file (glob("*")) { # for each file in current directory if ($opt_window) { $sendcmd = "createln"; } else { - $sendcmd = "sendln"; + $sendcmd = "exec"; } my ($filetype, $fileimg, $sendtxt1, $sendtxt2); @@ -74,22 +74,23 @@ foreach $file (glob("*")) { # for each file in current directory $_ = ; close INFILE; + # Extract content URL and the icon URL (if any) my @urlvals; my $nurl = 0; - while ( m%\b # initiator - (http|file|mailto): # scheme + while ( m%\b # URL word boundary + (http|file|mailto): # protocol (//)? # slashpair (?:([\w.]+)@)? # username ([\w\-]+(?:\.[\w\-]+)*)? # host (?::(\d+))? # port - (/\S*?)? # pathname - (?=>|\"|\'|\s|[.,!](?:\s|\Z)|\Z) # terminator (look ahead) + (/\S*?) # pathname + (?=>|\"|\'|\s|[.,!](?:\s|\Z)|\Z) # URL terminator (look ahead) %x ) { $urlvals[$nurl] = $&; s%$&%%; $nurl++; } - s%\A\s*(\S.*?)?\s*\Z%$1%; + s%\A\s*(\S.*?)?\s*\Z%$1%; # trim leading/trailing space if ($nurl >= 1) { my $urldesc = $_; @@ -100,9 +101,9 @@ foreach $file (glob("*")) { # for each file in current directory $filetype = "urlfile"; if ($nurl >= 2) { - $fileimg = "$urlvals[1]"; + $fileimg = "$urlvals[1]"; # use icon URL } else { - $fileimg = "$imgdir/$img{$filetype}"; + $fileimg = "$imgdir/$img{$filetype}"; #default URL icon } $sendtxt1 = "$urlstr"; $sendtxt2 = "$urlstr"; @@ -124,7 +125,7 @@ foreach $file (glob("*")) { # for each file in current directory my $tail = $comps[$#comps]; # file name my $clickcmd = - qq%onclick="return clickXMLTerm('$sendcmd',-\#,'$sendtxt1','$sendtxt2')"%; + qq%onClick="return HandleEvent(event,'click','$sendcmd',-\#,'$sendtxt1','$sendtxt2')"%; $rowimg .= ""; $rowtxt .= ""; diff --git a/mozilla/extensions/xmlterm/scripts/xls.csh b/mozilla/extensions/xmlterm/scripts/xls.csh index 1cd43bcc240..c34493f45c9 100755 --- a/mozilla/extensions/xmlterm/scripts/xls.csh +++ b/mozilla/extensions/xmlterm/scripts/xls.csh @@ -55,7 +55,7 @@ foreach file ($files) else set cmd="sendln" endif - set clickcmd="onclick="'"'"return clickXMLTerm('$cmd',-1,'$sendtxt')"'"' + set clickcmd="onClick="'"'"return ClickXMLTerm('$cmd',-1,'$sendtxt')"'"' set rowimg="${rowimg}" set rowtxt="${rowtxt}" diff --git a/mozilla/extensions/xmlterm/tests/DocStream.c b/mozilla/extensions/xmlterm/tests/DocStream.c index 8240abe2058..733623199e5 100644 --- a/mozilla/extensions/xmlterm/tests/DocStream.c +++ b/mozilla/extensions/xmlterm/tests/DocStream.c @@ -20,11 +20,11 @@ int main(void) \ Please click a button
\ \ + onClick=\"return HandleEvent(event, 'click', 'sendln','#','b')\"> \ \ + onClick=\"return HandleEvent(event, 'click', 'sendln','#','e')\"> \ \ + onClick=\"return HandleEvent(event, 'click', 'sendln','#','q')\"> \
\ "); diff --git a/mozilla/extensions/xmlterm/tests/HelloWorld.c b/mozilla/extensions/xmlterm/tests/HelloWorld.c index b03255e9b22..18ec6e3a729 100644 --- a/mozilla/extensions/xmlterm/tests/HelloWorld.c +++ b/mozilla/extensions/xmlterm/tests/HelloWorld.c @@ -18,11 +18,11 @@ int main(void) \ Please click a button
\ \ + onClick=\"return HandleEvent(event, 'click', 'sendln','#','b')\"> \ \ + onClick=\"return HandleEvent(event, 'click', 'sendln','#','e')\"> \ \ + onClick=\"return HandleEvent(event, 'click', 'sendln','#','q')\"> \
"); printf("%c", '\0'); /* Escape sequence signalling end of HTML */ diff --git a/mozilla/extensions/xmlterm/ui/content/XMLTermCommands.js b/mozilla/extensions/xmlterm/ui/content/XMLTermCommands.js index da763cda2ae..eb75775a75e 100644 --- a/mozilla/extensions/xmlterm/ui/content/XMLTermCommands.js +++ b/mozilla/extensions/xmlterm/ui/content/XMLTermCommands.js @@ -4,10 +4,10 @@ function StartupXMLTerm() { dump("StartupXMLTerm:"+window.frames[0].name+"\n"); if (window.frames.length == 2) { - window.frames[1].xmltbrowser = window.frames[0]; - xmltwin = frames[1]; + xmltwin = window.frames[1]; + xmltwin.xmltbrowser = window.frames[0]; } else { - xmltwin = frames[0]; + xmltwin = window.frames[0]; } dump("StartupXMLterm: WINDOW.ARGUMENTS="+window.arguments+"\n"); @@ -27,11 +27,10 @@ function StartupXMLTerm() { return; } - // Store the XMLTerm shell in the window + // Store the XMLTerm shell in current window and in the XMLTerm frame window.xmlterm = xmltshell; xmltwin.xmlterm = xmltshell; // Initialize XMLTerm shell in content window with argvals - window.xmlterm.Init(xmltwin, "", - window.arguments); + window.xmlterm.Init(xmltwin, "", window.arguments); } diff --git a/mozilla/extensions/xmlterm/ui/content/xmlterm.html b/mozilla/extensions/xmlterm/ui/content/xmlterm.html index 8c675ce9d6f..a12997a462b 100644 --- a/mozilla/extensions/xmlterm/ui/content/xmlterm.html +++ b/mozilla/extensions/xmlterm/ui/content/xmlterm.html @@ -8,6 +8,11 @@ + + + + + +
+

+ + +

+
+ + + diff --git a/mozilla/extensions/xmlterm/ui/content/xmlterm2.xul b/mozilla/extensions/xmlterm/ui/content/xmlterm2.xul index 2f3a3d0f8f8..271635b03c7 100644 --- a/mozilla/extensions/xmlterm/ui/content/xmlterm2.xul +++ b/mozilla/extensions/xmlterm/ui/content/xmlterm2.xul @@ -36,6 +36,6 @@ html:src="about:blank" flex="100%"/> + html:src="chrome://xmlterm/content/xmlterm2.html" flex="100%"/> diff --git a/mozilla/extensions/xmlterm/ui/skin/xmltpage.css b/mozilla/extensions/xmlterm/ui/skin/xmltpage.css index d6d2b86022c..3e92b23eb89 100644 --- a/mozilla/extensions/xmlterm/ui/skin/xmltpage.css +++ b/mozilla/extensions/xmlterm/ui/skin/xmltpage.css @@ -14,11 +14,16 @@ SPAN.prompt { color: blue } SPAN.command { color: blue } /* Screen display */ -PRE.row { margin: 0; padding-top: 0; padding-bottom: 0 } -SPAN.row { margin: 0; padding-top: 0; padding-bottom: 0 } +PRE.row { margin: 0; padding-top: 0; padding-bottom: 0 } +SPAN.row { margin: 0; padding-top: 0; padding-bottom: 0 } +SPAN.boldstyle { font-weight: bold; margin: 0; padding-top: 0; padding-bottom: 0 } +SPAN.underlinestyle { text-decoration: underline; margin: 0; padding-top: 0; padding-bottom: 0 } +SPAN.blinkstyle { font-weight: bold; margin: 0; padding-top: 0; padding-bottom: 0 } +SPAN.inversestyle { font-weight: bold; margin: 0; padding-top: 0; padding-bottom: 0 } /* Tips */ -SPAN.tips { font-family: sans-serif } +SPAN.tiphead { font-family: sans-serif; font-weight: bold } +SPAN.tipdata { font-family: sans-serif } SPAN.moretips { font-family: sans-serif; color: blue; cursor: hand; font-weight: bold; text-decoration: underline };