Bug 394473 - "caret/cursor in View Source is invisible since 20070628 {not rendered, seen, can't, cannot, shown, displayed}" [p=chris@pearce.org.nz (Chris Pearce [cpearce]) r=peterv r+sr=roc a1.9=beltzner]
authorreed@reedloden.com
Tue, 26 Feb 2008 17:49:46 -0800
changeset 12305 480b921e52ecedcff6950a5762f2863f0deb7ba5
parent 12304 973fcd6735a3f97e48975ea78f4e094b46fcff31
child 12306 cf7c4f5977c3d57df1c8cb46be32d23542a069d5
push idunknown
push userunknown
push dateunknown
reviewerspeterv
bugs394473, 20070628
milestone1.9b4pre
Bug 394473 - "caret/cursor in View Source is invisible since 20070628 {not rendered, seen, can't, cannot, shown, displayed}" [p=chris@pearce.org.nz (Chris Pearce [cpearce]) r=peterv r+sr=roc a1.9=beltzner]
content/base/src/nsGkAtomList.h
content/events/src/nsEventStateManager.cpp
layout/base/nsCaret.cpp
layout/base/nsCaret.h
toolkit/components/viewsource/content/viewSource.js
toolkit/components/viewsource/content/viewSource.xul
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -756,16 +756,17 @@ GK_ATOM(selected, "selected")
 GK_ATOM(selectedIndex, "selectedIndex")
 GK_ATOM(selectedindex, "selectedindex")
 GK_ATOM(self, "self")
 GK_ATOM(seltype, "seltype")
 GK_ATOM(setcookie, "set-cookie")
 GK_ATOM(setter, "setter")
 GK_ATOM(shape, "shape")
 GK_ATOM(show, "show")
+GK_ATOM(showcaret, "showcaret")
 GK_ATOM(simple, "simple")
 GK_ATOM(single, "single")
 GK_ATOM(size, "size")
 GK_ATOM(sizemode, "sizemode")
 GK_ATOM(sizetopopup, "sizetopopup")
 GK_ATOM(slider, "slider")
 GK_ATOM(small, "small")
 GK_ATOM(smooth, "smooth")
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -221,16 +221,18 @@ enum {
 };
 
 // mask values for ui.key.chromeAccess and ui.key.contentAccess
 #define NS_MODIFIER_SHIFT    1
 #define NS_MODIFIER_CONTROL  2
 #define NS_MODIFIER_ALT      4
 #define NS_MODIFIER_META     8
 
+static PRBool GetWindowShowCaret(nsIDocument *aDocument);
+
 static nsIDocument *
 GetDocumentFromWindow(nsIDOMWindow *aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
   nsCOMPtr<nsIDocument> doc;
 
   if (win) {
     doc = do_QueryInterface(win->GetExtantDocument());
@@ -1036,21 +1038,30 @@ nsEventStateManager::PreHandleEvent(nsPr
 
       ResetBrowseWithCaret();
     }
 
     break;
 
   case NS_LOSTFOCUS:
     {
-      // Hide the caret used in "browse with caret mode"
-      if (mBrowseWithCaret && mPresContext) {
+      // Hide the caret if it's visible.
+      if (mPresContext) {
         nsIPresShell *presShell = mPresContext->GetPresShell();
-        if (presShell)
-           SetContentCaretVisible(presShell, mCurrentFocus, PR_FALSE);
+        if (presShell) {
+           nsCOMPtr<nsICaret> caret;
+           presShell->GetCaret(getter_AddRefs(caret));
+           if (caret) {
+             PRBool caretVisible = PR_FALSE;
+             caret->GetCaretVisible(&caretVisible);
+             if (caretVisible) {
+               SetContentCaretVisible(presShell, mCurrentFocus, PR_FALSE);
+             }
+           }
+        }
       }
 
       // If focus is going to another mozilla window, we wait for the
       // focus event and fire a blur on the old focused content at that time.
       // This allows "-moz-user-focus: ignore" to work.
 
 #if defined(XP_WIN) || defined(XP_OS2)
       if (!static_cast<nsFocusEvent*>(aEvent)->isMozWindowTakingFocus) {
@@ -4709,17 +4720,17 @@ nsEventStateManager::SendFocusBlur(nsPre
       nsCxPusher pusher;
       if (pusher.Push(mDocument)) {
         nsEventDispatcher::Dispatch(mDocument, mPresContext, &event, nsnull,
                                     &status);
       }
     }
   }
 
-  if (mBrowseWithCaret)
+  if (mBrowseWithCaret || GetWindowShowCaret(mDocument))
     SetContentCaretVisible(presShell, aContent, PR_TRUE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEventStateManager::GetFocusedContent(nsIContent** aContent)
 {
@@ -5337,16 +5348,17 @@ nsEventStateManager::SetCaretEnabled(nsI
   aPresShell->GetCaret(getter_AddRefs(caret));
 
   nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
   if (!selCon || !caret)
     return NS_ERROR_FAILURE;
 
   selCon->SetCaretEnabled(aEnabled);
   caret->SetCaretVisible(aEnabled);
+  caret->SetIgnoreUserModify(aEnabled);
 
   return NS_OK;
 }
 
 nsresult
 nsEventStateManager::SetContentCaretVisible(nsIPresShell* aPresShell,
                                             nsIContent *aFocusedContent,
                                             PRBool aVisible)
@@ -5368,38 +5380,54 @@ nsEventStateManager::SetContentCaretVisi
   if (docFrameSelection && caret &&
      (frameSelection == docFrameSelection || !aFocusedContent)) {
     nsISelection* domSelection = docFrameSelection->
       GetSelection(nsISelectionController::SELECTION_NORMAL);
     if (domSelection) {
       // First, tell the caret which selection to use
       caret->SetCaretDOMSelection(domSelection);
 
-      // Ignore user-modify status of nodes when browsing with caret
-      caret->SetIgnoreUserModify(aVisible);
-
       // In content, we need to set the caret
       // the only other case is edit fields, where they have a different frame selection from the doc's
       // in that case they'll take care of making the caret visible themselves
 
       // Then make sure it's visible
       return SetCaretEnabled(aPresShell, aVisible);
     }
   }
 
   return NS_OK;
 }
 
-
 PRBool
 nsEventStateManager::GetBrowseWithCaret()
 {
   return mBrowseWithCaret;
 }
 
+// Checks if the window corresponding to |aDocument| has the 
+// showcaret="true" attribute set.
+static PRBool
+GetWindowShowCaret(nsIDocument *aDocument)
+{
+  if (!aDocument) return PR_FALSE;
+
+  nsPIDOMWindow* window = aDocument->GetWindow();
+  if (!window) return PR_FALSE;
+
+  nsCOMPtr<nsIContent> docContent =
+    do_QueryInterface(window->GetFrameElementInternal());
+  if (!docContent) return PR_FALSE;
+
+  return docContent->AttrValueIs(kNameSpaceID_None,
+                                 nsGkAtoms::showcaret,
+                                 NS_LITERAL_STRING("true"),
+                                 eCaseMatters);
+}
+
 void
 nsEventStateManager::ResetBrowseWithCaret()
 {
   // This is called when browse with caret changes on the fly
   // or when a document gets focused
 
   if (!mPresContext)
     return;
@@ -5431,22 +5459,30 @@ nsEventStateManager::ResetBrowseWithCare
     }
   }
 
   PRPackedBool browseWithCaret =
     nsContentUtils::GetBoolPref("accessibility.browsewithcaret");
 
   mBrowseWithCaret = browseWithCaret;
 
-
-  // Make caret visible or not, depending on what's appropriate
-  // Set caret visibility for focused document only
-  // Others will be set when they get focused again
+  // Make caret visible or not, depending on what's appropriate.
+  // Set caret visibility for focused document only,
+  // others will be set when they get focused again
   if (presShell && gLastFocusedDocument && gLastFocusedDocument == mDocument) {
-    SetContentCaretVisible(presShell, mCurrentFocus, browseWithCaret);
+
+    // Contenteditable nodes should always have a caret.
+    PRBool isFocusEditable =
+      (mCurrentFocus) ? mCurrentFocus->HasFlag(NODE_IS_EDITABLE) : PR_FALSE;
+
+    PRBool caretShouldBeVisible = isFocusEditable ||
+                                  browseWithCaret ||
+                                  GetWindowShowCaret(mDocument);
+
+    SetContentCaretVisible(presShell, mCurrentFocus, caretShouldBeVisible);
   }
 }
 
 //--------------------------------------------------------------------------------
 //-- DocShell Focus Traversal Methods
 //--------------------------------------------------------------------------------
 
 //----------------------------------------
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -219,21 +219,24 @@ NS_IMETHODIMP nsCaret::SetCaretDOMSelect
 }
 
 
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP nsCaret::SetCaretVisible(PRBool inMakeVisible)
 {
   mVisible = inMakeVisible;
   nsresult  err = NS_OK;
-  if (mVisible)
+  if (mVisible) {
     err = StartBlinking();
-  else
+    SetIgnoreUserModify(PR_TRUE);
+  } else {
     err = StopBlinking();
-    
+    SetIgnoreUserModify(PR_FALSE);
+  }
+
   return err;
 }
 
 
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP nsCaret::GetCaretVisible(PRBool *outMakeVisible)
 {
   NS_ENSURE_ARG_POINTER(outMakeVisible);
@@ -541,17 +544,17 @@ nsresult nsCaret::StartBlinking()
 
 
 //-----------------------------------------------------------------------------
 nsresult nsCaret::StopBlinking()
 {
   if (mDrawn)     // erase the caret if necessary
     DrawCaret(PR_TRUE);
 
-  NS_ASSERTION(!mDrawn, "We just erased ourselves");
+  NS_ASSERTION(!mDrawn, "Caret still drawn after StopBlinking().");
   KillTimer();
 
   return NS_OK;
 }
 
 PRBool
 nsCaret::DrawAtPositionWithHint(nsIDOMNode*             aNode,
                                 PRInt32                 aOffset,
@@ -1212,16 +1215,29 @@ nsCaret::GetFrameSelection()
   nsFrameSelection* frameSelection = nsnull;
   privateSelection->GetFrameSelection(&frameSelection);
   return frameSelection;
 }
 
 void
 nsCaret::SetIgnoreUserModify(PRBool aIgnoreUserModify)
 {
+  if (!aIgnoreUserModify && mIgnoreUserModify && mDrawn) {
+    // We're turning off mIgnoreUserModify. If the caret's drawn
+    // in a read-only node we must erase it, else the next call
+    // to DrawCaret() won't erase the old caret, due to the new
+    // mIgnoreUserModify value.
+    nsIFrame *frame = GetCaretFrame();
+    if (frame) {
+      const nsStyleUserInterface* userinterface = frame->GetStyleUserInterface();
+      if (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) {
+        StopBlinking();
+      }
+    }
+  }
   mIgnoreUserModify = aIgnoreUserModify;
 }
 
 //-----------------------------------------------------------------------------
 nsresult NS_NewCaret(nsICaret** aInstancePtrResult)
 {
   NS_PRECONDITION(aInstancePtrResult, "null ptr");
   
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -166,17 +166,19 @@ protected:
     nsCOMPtr<nsIRenderingContext>   mRendContext;
 
     PRUint32              mBlinkRate;         // time for one cyle (off then on), in milliseconds
 
     nscoord               mCaretWidth;   // caret width. this gets calculated laziiy
     nscoord               mBidiIndicatorSize;   // width and height of bidi indicator
 
     PRPackedBool          mVisible;           // is the caret blinking
-    PRPackedBool          mDrawn;             // this should be mutable
+
+    PRPackedBool          mDrawn;             // Denotes when the caret is physically drawn on the screen.
+
     PRPackedBool          mReadOnly;          // it the caret in readonly state (draws differently)      
     PRPackedBool          mShowDuringSelection; // show when text is selected
 
     nsRect                mCaretRect;         // the last caret rect, in the coodinates of the last frame.
 
     nsCOMPtr<nsIContent>  mLastContent;       // store the content the caret was last requested to be drawn
                                               // in (by DrawAtPosition()/DrawCaret()),
                                               // note that this can be different than where it was
--- a/toolkit/components/viewsource/content/viewSource.js
+++ b/toolkit/components/viewsource/content/viewSource.js
@@ -394,17 +394,16 @@ function goToLine(line)
       // <span>...\n<span>...</span></span><span>...</span>
       node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
       selection.extend(node, 0);
     }
   }
 
   var selCon = getSelectionController();
   selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
-  selCon.setCaretEnabled(true);
   selCon.setCaretVisibilityDuringSelection(true);
 
   // Scroll the beginning of the line into view.
   selCon.scrollSelectionIntoView(
     nsISelectionController.SELECTION_NORMAL,
     nsISelectionController.SELECTION_FOCUS_REGION,
     true);
 
@@ -429,17 +428,16 @@ function updateStatusBar()
     return;
   }
   if (selection.focusNode.nodeType != Node.TEXT_NODE) {
     return;
   }
 
   var selCon = getSelectionController();
   selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
-  selCon.setCaretEnabled(true);
   selCon.setCaretVisibilityDuringSelection(true);
 
   var interlinePosition = selection
       .QueryInterface(nsISelectionPrivate).interlinePosition;
 
   var result = {};
   findLocation(null, -1, 
       selection.focusNode, selection.focusOffset, interlinePosition, result);
--- a/toolkit/components/viewsource/content/viewSource.xul
+++ b/toolkit/components/viewsource/content/viewSource.xul
@@ -198,17 +198,17 @@
       </menu>
     </menubar>  
   </toolbox>
 
   <vbox id="appcontent" flex="1"
        ondragdrop="nsDragAndDrop.drop(event, contentAreaDNDObserver);">
 
     <browser id="content" type="content-primary" name="content" src="about:blank" flex="1"
-             disablehistory="true" context="viewSourceContextMenu"/>
+             disablehistory="true" context="viewSourceContextMenu" showcaret="true"/>
     <findbar id="FindToolbar" browserid="content"/>
   </vbox> 
 
   <statusbar id="status-bar" class="chromeclass-status">
     <statusbarpanel id="statusbar-line-col" label="" flex="1"/>
   </statusbar>
 
 </window>