Changeset 14086 in webkit


Ignore:
Timestamp:
Apr 27, 2006 1:11:45 PM (18 years ago)
Author:
justing
Message:

LayoutTests:

Reviewed by darin


<http://bugzilla.opendarwin.org/show_bug.cgi?id=8624>
Placeholders aren't always removed during paste
<rdar://problem/4059807>
Seed: Mail: pasting quoted content sometimes adds a phantom newline


If a webkit-block-placeholder has been introduced by a bug in deletion,
we shouldn't remove it during paste if it is now acting as a linebreak.

  • editing/pasteboard/bad-placeholder-expected.checksum: Added.
  • editing/pasteboard/bad-placeholder-expected.png: Added.
  • editing/pasteboard/bad-placeholder-expected.txt: Added.
  • editing/pasteboard/bad-placeholder.html: Added. Generic placeholder brs should be removed if they are displaced by pasted content.
  • editing/pasteboard/displaced-generic-placeholder-expected.checksum: Added.
  • editing/pasteboard/displaced-generic-placeholder-expected.png: Added.
  • editing/pasteboard/displaced-generic-placeholder-expected.txt: Added.
  • editing/pasteboard/displaced-generic-placeholder.html: Added. So should placeholder brs with our special class on them.
  • editing/pasteboard/displaced-placeholder-expected.checksum: Added.
  • editing/pasteboard/displaced-placeholder-expected.png: Added.
  • editing/pasteboard/displaced-placeholder-expected.txt: Added.
  • editing/pasteboard/displaced-placeholder.html: Added. Removed an unnecessary collapsed br.
  • editing/pasteboard/paste-text-010-expected.txt: Two fixes, an extra line was present after pasting with an interchange newline.
  • editing/pasteboard/paste-text-016-expected.checksum: Added.
  • editing/pasteboard/paste-text-016-expected.png: Added.
  • editing/pasteboard/paste-text-016-expected.txt: Added.
  • editing/pasteboard/paste-text-017-expected.checksum: Added.
  • editing/pasteboard/paste-text-017-expected.png: Added.
  • editing/pasteboard/paste-text-017-expected.txt: Added. The code that interprets the incoming br as a br in strict mode had a bug.
  • editing/pasteboard/quirks-mode-br-1-expected.checksum: Added.
  • editing/pasteboard/quirks-mode-br-1-expected.png: Added.
  • editing/pasteboard/quirks-mode-br-1-expected.txt: Added.
  • editing/pasteboard/quirks-mode-br-1.html: Added. The linePlaceholder removal was negating the work to interpret the last incoming br as a br in strict mode.
  • editing/pasteboard/quirks-mode-br-2-expected.checksum: Added.
  • editing/pasteboard/quirks-mode-br-2-expected.png: Added.
  • editing/pasteboard/quirks-mode-br-2-expected.txt: Added.
  • editing/pasteboard/quirks-mode-br-2.html: Added. Don't let collapsed brs become rendered as a result of the paste operation.
  • editing/pasteboard/unrendered-br-expected.checksum: Added.
  • editing/pasteboard/unrendered-br-expected.png: Added.
  • editing/pasteboard/unrendered-br-expected.txt: Added.
  • editing/pasteboard/unrendered-br.html: Added.

WebCore:

Reviewed by darin


<http://bugzilla.opendarwin.org/show_bug.cgi?id=8624>
Placeholders aren't always removed during paste
<rdar://problem/4059807>
Seed: Mail: pasting quoted content sometimes adds a phantom newline

  • editing/CompositeEditCommand.cpp: Added a FIXME.
  • editing/ReplaceSelectionCommand.cpp: (WebCore::ReplaceSelectionCommand::doApply): Removed two no-op setEndingSelection calls. Store away a br at the position where we'll start inserting content in case the br a) is made unnecessary by the insertion (it's collapsed away) b) was acting as a placeholder and should therefore be displaced by inserted content or c) was acting as a line break and, as a result of the insertion, is now acting as a placeholder. Don't only store away brs that have the webkit-block-placeholder class on them. Any br that does any of the three things just mentioned should be removed. The linePlaceholder removal was run after the code that makes sure to interpret incoming brs strictly, and was negating that work in certain cases.


(WebCore::ReplaceSelectionCommand::removeEndBRIfNeeded): Described above.

  • editing/ReplaceSelectionCommand.h:
  • editing/VisiblePosition.cpp: (WebCore::isEqualIgnoringAffinity): Added a workaround for 8622. We want this function to return true even if one of the two visible positions has been incorrectly canonicalized.
Location:
trunk
Files:
24 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r14082 r14086  
     12006-04-27  Justin Garcia  <justin.garcia@apple.com>
     2
     3        Reviewed by darin
     4       
     5        <http://bugzilla.opendarwin.org/show_bug.cgi?id=8624>
     6        Placeholders aren't always removed during paste
     7        <rdar://problem/4059807>
     8        Seed: Mail: pasting quoted content sometimes adds a phantom newline
     9       
     10        If a webkit-block-placeholder has been introduced by a bug in deletion,
     11        we shouldn't remove it during paste if it is now acting as a linebreak.
     12        * editing/pasteboard/bad-placeholder-expected.checksum: Added.
     13        * editing/pasteboard/bad-placeholder-expected.png: Added.
     14        * editing/pasteboard/bad-placeholder-expected.txt: Added.
     15        * editing/pasteboard/bad-placeholder.html: Added.
     16        Generic placeholder brs should be removed if they are displaced by pasted content.
     17        * editing/pasteboard/displaced-generic-placeholder-expected.checksum: Added.
     18        * editing/pasteboard/displaced-generic-placeholder-expected.png: Added.
     19        * editing/pasteboard/displaced-generic-placeholder-expected.txt: Added.
     20        * editing/pasteboard/displaced-generic-placeholder.html: Added.
     21        So should placeholder brs with our special class on them.
     22        * editing/pasteboard/displaced-placeholder-expected.checksum: Added.
     23        * editing/pasteboard/displaced-placeholder-expected.png: Added.
     24        * editing/pasteboard/displaced-placeholder-expected.txt: Added.
     25        * editing/pasteboard/displaced-placeholder.html: Added.
     26        Removed an unnecessary collapsed br.
     27        * editing/pasteboard/paste-text-010-expected.txt:
     28        Two fixes, an extra line was present after pasting with an interchange newline.
     29        * editing/pasteboard/paste-text-016-expected.checksum: Added.
     30        * editing/pasteboard/paste-text-016-expected.png: Added.
     31        * editing/pasteboard/paste-text-016-expected.txt: Added.
     32        * editing/pasteboard/paste-text-017-expected.checksum: Added.
     33        * editing/pasteboard/paste-text-017-expected.png: Added.
     34        * editing/pasteboard/paste-text-017-expected.txt: Added.
     35        The code that interprets the incoming br as a br in strict mode had a bug.
     36        * editing/pasteboard/quirks-mode-br-1-expected.checksum: Added.
     37        * editing/pasteboard/quirks-mode-br-1-expected.png: Added.
     38        * editing/pasteboard/quirks-mode-br-1-expected.txt: Added.
     39        * editing/pasteboard/quirks-mode-br-1.html: Added.
     40        The linePlaceholder removal was negating the work to interpret the last
     41        incoming br as a br in strict mode.
     42        * editing/pasteboard/quirks-mode-br-2-expected.checksum: Added.
     43        * editing/pasteboard/quirks-mode-br-2-expected.png: Added.
     44        * editing/pasteboard/quirks-mode-br-2-expected.txt: Added.
     45        * editing/pasteboard/quirks-mode-br-2.html: Added.
     46        Don't let collapsed brs become rendered as a result of the paste operation.
     47        * editing/pasteboard/unrendered-br-expected.checksum: Added.
     48        * editing/pasteboard/unrendered-br-expected.png: Added.
     49        * editing/pasteboard/unrendered-br-expected.txt: Added.
     50        * editing/pasteboard/unrendered-br.html: Added.
     51
    1522006-04-25  Tim Omernick  <timo@apple.com>
    253
  • trunk/LayoutTests/editing/pasteboard/paste-text-010-expected.txt

    r13869 r14086  
    4747EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
    4848EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 3 of DIV > BODY > HTML > #document to 3 of DIV > BODY > HTML > #document givenAction:WebViewInsertActionPasted
    49 EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 7 of #text > DIV > BODY > HTML > #document to 7 of #text > DIV > BODY > HTML > #document toDOMRange:range from 7 of #text > DIV > BODY > HTML > #document to 7 of #text > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
     49EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 7 of #text > DIV > BODY > HTML > #document to 7 of #text > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
    5050EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
    5151EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
     
    6868        RenderText {#text} at (88,70) size 74x28
    6969          text run at (88,70) width 74: "of men."
    70         RenderBR {BR} at (162,92) size 0x0
    7170caret: position 7 of child 4 {#text} of child 1 {DIV} of child 1 {BODY} of child 0 {HTML} of document
  • trunk/LayoutTests/editing/pasteboard/paste-text-016-expected.checksum

    r12710 r14086  
    1 7d233a099920f1881928deadf93f1504
     1b0627a2c344122f441226913df740d4a
  • trunk/LayoutTests/editing/pasteboard/paste-text-016-expected.txt

    r13869 r14086  
    1919EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
    2020EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 2 of P > DIV > DIV > BODY > HTML > #document to 2 of P > DIV > DIV > BODY > HTML > #document givenAction:WebViewInsertActionPasted
    21 EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 3 of P > DIV > DIV > BODY > HTML > #document to 3 of P > DIV > DIV > BODY > HTML > #document toDOMRange:range from 3 of P > DIV > DIV > BODY > HTML > #document to 3 of P > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
     21EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of #text > P > DIV > DIV > BODY > HTML > #document to 0 of #text > P > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
     22EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
    2223EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
    2324layer at (0,0) size 800x600
     
    3839          text run at (0,18) width 378: "***TEST*** line should be second, following the first line."
    3940      RenderBlock {DIV} at (0,36) size 784x12
    40       RenderBlock {DIV} at (0,48) size 784x224
    41         RenderBlock {DIV} at (0,0) size 784x224 [border: (2px solid #FF0000)]
    42           RenderBlock {P} at (14,14) size 756x112
     41      RenderBlock {DIV} at (0,48) size 784x196
     42        RenderBlock {DIV} at (0,0) size 784x196 [border: (2px solid #FF0000)]
     43          RenderBlock {P} at (14,14) size 756x84
    4344            RenderBlock (anonymous) at (0,0) size 756x28
    4445              RenderText {#text} at (0,0) size 319x28
     
    4849              RenderText {#text} at (0,0) size 130x28
    4950                text run at (0,0) width 130: "***TEST***"
    50             RenderBlock (anonymous) at (0,56) size 756x56
    51               RenderBR {BR} at (0,0) size 0x28
    52               RenderText {#text} at (0,28) size 128x28
    53                 text run at (0,28) width 128: "Another line."
     51            RenderBlock (anonymous) at (0,56) size 756x28
     52              RenderText {#text} at (0,0) size 128x28
     53                text run at (0,0) width 128: "Another line."
     54          RenderBlock {P} at (14,98) size 756x0
     55          RenderBlock (anonymous) at (14,98) size 756x28
     56            RenderText {#text} at (0,0) size 6x28
     57              text run at (0,0) width 6: " "
    5458          RenderBlock {P} at (14,126) size 756x0
    5559          RenderBlock (anonymous) at (14,126) size 756x28
    5660            RenderText {#text} at (0,0) size 6x28
    5761              text run at (0,0) width 6: " "
    58           RenderBlock {P} at (14,154) size 756x0
    59           RenderBlock (anonymous) at (14,154) size 756x28
    60             RenderText {#text} at (0,0) size 6x28
    61               text run at (0,0) width 6: " "
    62           RenderBlock {P} at (14,182) size 756x28
     62          RenderBlock {P} at (14,154) size 756x28
    6363            RenderBR {BR} at (0,0) size 0x28
    64 caret: position 0 of child 3 {BR} of child 0 {P} of child 1 {DIV} of child 7 {DIV} of child 1 {BODY} of child 0 {HTML} of document
     64caret: position 0 of child 3 {#text} of child 0 {P} of child 1 {DIV} of child 7 {DIV} of child 1 {BODY} of child 0 {HTML} of document
  • trunk/LayoutTests/editing/pasteboard/paste-text-017-expected.checksum

    r13644 r14086  
    1 2136d853988645543964c905ec7d2b08
     11e05ffac22396c85428d0466c50757de
  • trunk/LayoutTests/editing/pasteboard/paste-text-017-expected.txt

    r13869 r14086  
    3333          RenderText {#text} at (0,28) size 490x28
    3434            text run at (0,28) width 490: "Should see a blank line between \"two\" and \"three\""
    35       RenderBlock {DIV} at (0,236) size 784x144
    36         RenderBlock {DIV} at (0,0) size 784x144 [border: (2px solid #FF0000)]
     35      RenderBlock {DIV} at (0,236) size 784x116
     36        RenderBlock {DIV} at (0,0) size 784x116 [border: (2px solid #FF0000)]
    3737          RenderBlock {DIV} at (2,2) size 780x28
    3838            RenderText {#text} at (0,0) size 35x28
     
    4444              text run at (0,0) width 36: "two"
    4545          RenderBlock {DIV} at (2,86) size 780x28
    46             RenderBR {BR} at (0,0) size 0x28
    47           RenderBlock {DIV} at (2,114) size 780x28
    4846            RenderText {#text} at (0,0) size 49x28
    4947              text run at (0,0) width 49: "three"
    50 caret: position 0 of child 0 {BR} of child 6 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
     48caret: position 5 of child 0 {#text} of child 7 {DIV} of child 1 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
  • trunk/WebCore/ChangeLog

    r14082 r14086  
     12006-04-27  Justin Garcia  <justin.garcia@apple.com>
     2
     3        Reviewed by darin
     4       
     5        <http://bugzilla.opendarwin.org/show_bug.cgi?id=8624>
     6        Placeholders aren't always removed during paste
     7        <rdar://problem/4059807>
     8        Seed: Mail: pasting quoted content sometimes adds a phantom newline
     9
     10        * editing/CompositeEditCommand.cpp: Added a FIXME.
     11        * editing/ReplaceSelectionCommand.cpp:
     12        (WebCore::ReplaceSelectionCommand::doApply):
     13        Removed two no-op setEndingSelection calls.
     14        Store away a br at the position where we'll start inserting content in case the
     15        br a) is made unnecessary by the insertion (it's collapsed away) b) was acting
     16        as a placeholder and should therefore be displaced by inserted content or c) was
     17        acting as a line break and, as a result of the insertion, is now acting as a
     18        placeholder. 
     19        Don't only store away brs that have the webkit-block-placeholder class on them.
     20        Any br that does any of the three things just mentioned should be removed.
     21        The linePlaceholder removal was run after the code that makes sure to interpret
     22        incoming brs strictly, and was negating that work in certain cases.
     23       
     24        (WebCore::ReplaceSelectionCommand::removeEndBRIfNeeded): Described above.
     25        * editing/ReplaceSelectionCommand.h:
     26        * editing/VisiblePosition.cpp:
     27        (WebCore::isEqualIgnoringAffinity):
     28        Added a workaround for 8622.  We want this function to return true even if one of
     29        the two visible positions has been incorrectly canonicalized.
     30
    1312006-04-26  Tim Omernick  <timo@apple.com>
    232
  • trunk/WebCore/editing/CompositeEditCommand.cpp

    r14079 r14086  
    555555}
    556556
     557// FIXME: This function should never be used.  If we're in a state where a webkit-block-placeholder
     558// isn't the only thing in a block (there are bugs that can make this happen), this function will
     559// return a placeholder that might be far away from where the editing operation that's using it intends
     560// to operate.  Also a br is a placeholder if its acting like a placeholder, i.e. <div><br></div>, and
     561// this function only returs brs with our special class on them.
    557562Node *CompositeEditCommand::findBlockPlaceholder(Node *node)
    558563{
  • trunk/WebCore/editing/ReplaceSelectionCommand.cpp

    r14071 r14086  
    543543                if (!isEndOfDocument(visibleStart))
    544544                    setEndingSelection(visibleStart.next());
    545             } else {
     545            } else
    546546                insertParagraphSeparator();
    547                 setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY));
    548             }
    549547        }
    550548        startPos = endingSelection().start();
     
    556554                if (!isEndOfDocument(visibleStart))
    557555                    setEndingSelection(visibleStart.next());
    558             } else {
     556            } else
    559557                insertParagraphSeparator();
    560                 setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY));
    561             }
    562558        }
    563559        // We split the current paragraph in two to avoid nesting the blocks from the fragment inside the current block.
     
    575571    }
    576572   
     573    // NOTE: This would be an incorrect usage of downstream() if downstream() were changed to mean the last position after
     574    // p that maps to the same visible position as p (since in the case where a br is at the end of a block and collapsed
     575    // away, there are positions after the br which map to the same visible position as [br, 0]). 
     576    Node* endBR = startPos.downstream().node()->hasTagName(brTag) ? startPos.downstream().node() : 0;
     577   
    577578    if (startAtStartOfBlock && startBlock->inDocument())
    578579        startPos = Position(startBlock, 0);
     
    594595    if (!fragment.firstChild())
    595596        return;
    596    
    597     // check for a line placeholder, and store it away for possible removal later.
    598     Node *block = startPos.node()->enclosingBlockFlowElement();
    599     Node *linePlaceholder = findBlockPlaceholder(block);
    600     if (!linePlaceholder) {
    601         Position downstream = startPos.downstream();
    602         // NOTE: the check for brTag offset 0 could be false negative after
    603         // positionAvoidingSpecialElementBoundary() because "downstream" is
    604         // now a "second deepest position"
    605         downstream = positionAvoidingSpecialElementBoundary(downstream);
    606         if (downstream.node()->hasTagName(brTag) && downstream.offset() == 0 &&
    607             fragment.hasInterchangeNewlineAtEnd() &&
    608             isStartOfParagraph(VisiblePosition(downstream, VP_DEFAULT_AFFINITY)))
    609             linePlaceholder = downstream.node();
    610     }
    611597   
    612598    // check whether to "smart replace" needs to add leading and/or trailing space
     
    753739   
    754740    Position lastPositionToSelect;
     741   
     742    removeEndBRIfNeeded(endBR);
    755743
    756744    // step 4 : handle trailing newline
    757745    if (fragment.hasInterchangeNewlineAtEnd()) {
    758         removeLinePlaceholderIfNeeded(linePlaceholder);
    759746
    760747        if (!m_lastNodeInserted) {
     
    780767        }
    781768    } else {
    782         if (m_lastNodeInserted && m_lastNodeInserted->hasTagName(brTag) && !document()->inStrictMode()) {
    783             updateLayout();
    784             VisiblePosition pos(Position(m_lastNodeInserted.get(), 1), DOWNSTREAM);
    785             if (isEndOfBlock(pos)) {
    786                 Node *next = m_lastNodeInserted->traverseNextNode();
    787                 bool hasTrailingBR = next && next->hasTagName(brTag) && m_lastNodeInserted->enclosingBlockFlowElement() == next->enclosingBlockFlowElement();
    788                 if (!hasTrailingBR) {
    789                     // Insert an "extra" BR at the end of the block.
    790                     insertNodeBefore(createBreakElement(document()).get(), m_lastNodeInserted.get());
    791                 }
    792             }
     769        // We want to honor the last incoming line break, so, if it will collapse away because of quirks mode,
     770        // add an extra one.
     771        // FIXME: If <div><br></div> is pasted, the br will be expanded.  That's fine, if this code is about
     772        // interpreting incoming brs strictly, but if that's true then we should expand all incoming brs, not
     773        // just the last one.
     774        if (m_lastNodeInserted && m_lastNodeInserted->hasTagName(brTag) &&
     775            !document()->inStrictMode() && isEndOfBlock(VisiblePosition(Position(m_lastNodeInserted.get(), 0)))) {
     776            insertNodeBeforeAndUpdateNodesInserted(createBreakElement(document()).get(), m_lastNodeInserted.get());
    793777        }
    794778    }
     
    797781        fixupNodeStyles(fragment.nodes(), fragment.renderingInfo());
    798782   
     783    // Make sure that content after the end of the selection being pasted into is in the same paragraph as the
     784    // last bit of content that was inserted.
    799785    if (mergeEnd) {
    800786        VisiblePosition afterInsertedContent(positionAfterNode(m_lastNodeInserted.get()));
     
    807793   
    808794    completeHTMLReplacement(lastPositionToSelect);
    809    
    810     // step 5 : mop up
    811     removeLinePlaceholderIfNeeded(linePlaceholder);
    812 }
    813 
    814 void ReplaceSelectionCommand::removeLinePlaceholderIfNeeded(Node *linePlaceholder)
    815 {
    816     if (!linePlaceholder)
    817         return;
    818        
    819     updateLayout();
    820     if (linePlaceholder->inDocument()) {
    821         VisiblePosition placeholderPos(linePlaceholder, linePlaceholder->renderer()->caretMinOffset(), DOWNSTREAM);
    822         if (placeholderPos.next().isNull() ||
    823             !(isStartOfParagraph(placeholderPos) && isEndOfParagraph(placeholderPos))) {
    824            
    825             removeNodeAndPruneAncestors(linePlaceholder);
    826         }
    827     }
     795}
     796
     797void ReplaceSelectionCommand::removeEndBRIfNeeded(Node* endBR)
     798{
     799    if (!endBR || !endBR->inDocument())
     800        return;
     801       
     802    VisiblePosition visiblePos(Position(endBR, 0));
     803   
     804    if (// The br is collapsed away and so is unnecessary.
     805        !document()->inStrictMode() && isEndOfBlock(visiblePos) ||
     806        // A br that was originally holding a line open should be displaced by inserted content.
     807        // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
     808        isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos))
     809        removeNodeAndPruneAncestors(endBR);
    828810}
    829811
  • trunk/WebCore/editing/ReplaceSelectionCommand.h

    r14071 r14086  
    129129    void updateNodesInserted(Node *);
    130130    void fixupNodeStyles(const NodeVector&, const RenderingInfoMap&);
    131     void removeLinePlaceholderIfNeeded(Node *);
     131    void removeEndBRIfNeeded(Node*);
    132132   
    133133    bool shouldMergeStart(const ReplacementFragment&, const Selection&);
  • trunk/WebCore/editing/VisiblePosition.cpp

    r13825 r14086  
    231231bool isEqualIgnoringAffinity(const VisiblePosition &a, const VisiblePosition &b)
    232232{
    233     bool result = a.deepEquivalent() == b.deepEquivalent();
     233    bool result = a.deepEquivalent() == b.deepEquivalent() ||
     234                  // FIXME (8622): This is a slow but temporary workaround.
     235                  a.deepEquivalent().downstream() == b.deepEquivalent().downstream();
    234236    if (result) {
    235237        // We want to catch cases where positions are equal, but affinities are not, since
Note: See TracChangeset for help on using the changeset viewer.