WebKit Bugzilla
Attachment 370669 Details for
Bug 198267
: [iOS] Dropping in an editable element shouldn't result in a ranged selection
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-198267-20190526224955.patch (text/plain), 19.61 KB, created by
Wenson Hsieh
on 2019-05-26 22:49:56 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Wenson Hsieh
Created:
2019-05-26 22:49:56 PDT
Size:
19.61 KB
patch
obsolete
>Subversion Revision: 245779 >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 75df49e2cfd84fa5ac4a281a0c5487e9afdf7a7d..f3d55ebcb65745d8e170a2a838bb4897bb09bac6 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,41 @@ >+2019-05-26 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ [iOS] Dropping in an editable element should result in a ranged selection >+ https://bugs.webkit.org/show_bug.cgi?id=198267 >+ <rdar://problem/51145977> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ When drag and drop was first implemented for iOS in iOS 11, selection behavior when dropping into editable >+ elements matched that of macOS, by leaving the inserted content selected after performing the drop. However, in >+ other parts of the platform (e.g. Notes), both the keyboard and selection views are not shown after a drop. >+ >+ Instead of matching macOS behavior, WebKit on iOS should match the rest of the platform. This is a little >+ tricky, since we use the selection range after a drop to create a TextIndicator snapshot when creating a drag >+ preview. To resolve this, we refactor some of the logic introduced in r245778 to remember the DOM range to >+ snapshot before collapsing the range to the end of the inserted content. >+ >+ Tested by adjusting some existing API tests. >+ >+ * UIProcess/ios/WKContentViewInteraction.mm: >+ (-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:]): >+ >+ Remove some logic that currently presents the keyboard while the user is performing a drop that focuses an >+ editable element. >+ >+ * WebProcess/WebPage/WebPage.h: >+ >+ Add a member variable to keep track of which range should be snapshotted when generating a drop preview. >+ >+ * WebProcess/WebPage/ios/WebPageIOS.mm: >+ (WebKit::WebPage::didConcludeDrop): >+ (WebKit::WebPage::didConcludeEditDrag): >+ >+ Collapse the selection range to the end after an edit drag (i.e., a drop in an editable area that inserted >+ content). >+ >+ (WebKit::WebPage::computeAndSendEditDragSnapshot): >+ > 2019-05-26 Wenson Hsieh <wenson_hsieh@apple.com> > > [iOS] Dropped text, attachments, and images should animate into place >diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >index 6cb5a53f590944d414acd3c04a16efee3880837e..89669dc16f91a574060db195cbe782d222933e78 100644 >--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >@@ -5074,11 +5074,6 @@ - (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information u > if (userIsInteracting) > return YES; > >-#if ENABLE(DRAG_SUPPORT) >- if (_dragDropInteractionState.isPerformingDrop()) >- return YES; >-#endif >- > if (self.isFirstResponder || _becomingFirstResponder) { > // When the software keyboard is being used to enter an url, only the focus activity state is changing. > // In this case, auto focus on the page being navigated to should be disabled, unless a hardware >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h >index 2a8146b745077302e23e43e6a764b126c9e41bbe..bce88c609e42f9dfb7f1f6a5b256745acf640b4c 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.h >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.h >@@ -1777,6 +1777,7 @@ private: > > #if ENABLE(DRAG_SUPPORT) && PLATFORM(IOS_FAMILY) > HashSet<RefPtr<WebCore::HTMLImageElement>> m_pendingImageElementsForDropSnapshot; >+ RefPtr<WebCore::Range> m_rangeForDropSnapshot; > #endif > > bool m_cachedMainFrameIsPinnedToLeftSide { true }; >diff --git a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm >index 9bae7d45a42e9ae1fe0df70737cfbd4abd263e67..c8a343452b734ea9552cbd1235fd579301be6c8d 100644 >--- a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm >+++ b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm >@@ -827,6 +827,7 @@ void WebPage::requestAdditionalItemsForDragSession(const IntPoint& clientPositio > > void WebPage::didConcludeDrop() > { >+ m_rangeForDropSnapshot = nullptr; > m_pendingImageElementsForDropSnapshot.clear(); > } > >@@ -839,9 +840,9 @@ void WebPage::didConcludeEditDrag() > m_pendingImageElementsForDropSnapshot.clear(); > > bool waitingForAnyImageToLoad = false; >- auto& frame = m_page->focusController().focusedOrMainFrame(); >- if (auto range = frame.selection().selection().toNormalizedRange()) { >- for (TextIterator iterator(range.get()); !iterator.atEnd(); iterator.advance()) { >+ auto frame = makeRef(m_page->focusController().focusedOrMainFrame()); >+ if (auto selectionRange = frame->selection().selection().toNormalizedRange()) { >+ for (TextIterator iterator(selectionRange.get()); !iterator.atEnd(); iterator.advance()) { > auto* node = iterator.node(); > if (!is<HTMLImageElement>(node)) > continue; >@@ -853,6 +854,10 @@ void WebPage::didConcludeEditDrag() > waitingForAnyImageToLoad = true; > } > } >+ auto collapsedRange = Range::create(selectionRange->ownerDocument(), selectionRange->endPosition(), selectionRange->endPosition()); >+ frame->selection().setSelectedRange(collapsedRange.ptr(), DOWNSTREAM, FrameSelection::ShouldCloseTyping::Yes, UserTriggered); >+ >+ m_rangeForDropSnapshot = WTFMove(selectionRange); > } > > if (!waitingForAnyImageToLoad) >@@ -874,8 +879,7 @@ void WebPage::computeAndSendEditDragSnapshot() > { > Optional<TextIndicatorData> textIndicatorData; > static auto defaultTextIndicatorOptionsForEditDrag = TextIndicatorOptionIncludeSnapshotOfAllVisibleContentWithoutSelection | TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionPaintAllContent | TextIndicatorOptionIncludeMarginIfRangeMatchesSelection | TextIndicatorOptionPaintBackgrounds | TextIndicatorOptionComputeEstimatedBackgroundColor| TextIndicatorOptionUseSelectionRectForSizing | TextIndicatorOptionIncludeSnapshotWithSelectionHighlight; >- auto& frame = m_page->focusController().focusedOrMainFrame(); >- if (auto range = frame.selection().selection().toNormalizedRange()) { >+ if (auto range = std::exchange(m_rangeForDropSnapshot, nullptr)) { > if (auto textIndicator = TextIndicator::createWithRange(*range, defaultTextIndicatorOptionsForEditDrag, TextIndicatorPresentationTransition::None, { })) > textIndicatorData = textIndicator->data(); > } >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index c598f05447242c17fa5f726e5946dc6481b09916..c84ce22aecb3aa6718aa525c821a25b455a928d6 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,28 @@ >+2019-05-26 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ [iOS] Dropping in an editable element should result in a ranged selection >+ https://bugs.webkit.org/show_bug.cgi?id=198267 >+ <rdar://problem/51145977> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Adjust some existing API tests that currently check for selection rects after a drop. Instead of checking for >+ visible selection rects, simply check for the start caret rect, as determined by WKContentView's >+ -selectionRange. >+ >+ * TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm: >+ (TestWebKitAPI::TEST): >+ (makeCGRectValue): Deleted. >+ (checkSelectionRectsWithLogging): Deleted. >+ * TestWebKitAPI/cocoa/DragAndDropSimulator.h: >+ >+ Replace finalSelectionRects with finalSelectionStartRect. >+ >+ * TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm: >+ (-[DragAndDropSimulator _resetSimulatedState]): >+ (-[DragAndDropSimulator runFrom:to:additionalItemRequestLocations:]): >+ (-[DragAndDropSimulator finalSelectionRects]): Deleted. >+ > 2019-05-26 Wenson Hsieh <wenson_hsieh@apple.com> > > [iOS] Dropped text, attachments, and images should animate into place >diff --git a/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm b/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm >index f1cc240e539c817f388c31afd1e981846b3e5373..0876a4b3b242cc6adc7807e63c245eaf54dafbfd 100644 >--- a/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm >+++ b/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm >@@ -117,11 +117,6 @@ static void loadTestPageAndEnsureInputSession(DragAndDropSimulator *simulator, N > [simulator ensureInputSession]; > } > >-static NSValue *makeCGRectValue(CGFloat x, CGFloat y, CGFloat width, CGFloat height) >-{ >- return [NSValue valueWithCGRect:CGRectMake(x, y, width, height)]; >-} >- > static void checkCGRectIsEqualToCGRectWithLogging(CGRect expected, CGRect observed) > { > BOOL isEqual = CGRectEqualToRect(expected, observed); >@@ -130,13 +125,6 @@ static void checkCGRectIsEqualToCGRectWithLogging(CGRect expected, CGRect observ > NSLog(@"Expected: %@ but observed: %@", NSStringFromCGRect(expected), NSStringFromCGRect(observed)); > } > >-static void checkSelectionRectsWithLogging(NSArray *expected, NSArray *observed) >-{ >- if (![expected isEqualToArray:observed]) >- NSLog(@"Expected selection rects: %@ but observed: %@", expected, observed); >- EXPECT_TRUE([expected isEqualToArray:observed]); >-} >- > static void checkRichTextTypePrecedesPlainTextType(DragAndDropSimulator *simulator) > { > // At least one of "com.apple.flat-rtfd" or "public.rtf" is expected to have higher precedence than "public.utf8-plain-text". >@@ -275,7 +263,7 @@ TEST(DragAndDropTests, ImageToContentEditable) > EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); > EXPECT_TRUE([observedEventNames containsObject:@"drop"]); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(214, 201, 2, 174), [simulator finalSelectionStartRect]); > checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypePNG, kUTTypeFileURL); > checkEstimatedSize(simulator.get(), { 215, 174 }); > EXPECT_TRUE([simulator lastKnownDropProposal].precise); >@@ -320,7 +308,7 @@ TEST(DragAndDropTests, ImageInLinkToInput) > [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; > > EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 2057, 232) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(2156, 241, 2, 232), [simulator finalSelectionStartRect]); > checkSuggestedNameAndEstimatedSize(simulator.get(), @"icon.png", { 215, 174 }); > checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypePNG, 0); > EXPECT_TRUE([simulator lastKnownDropProposal].precise); >@@ -390,7 +378,7 @@ TEST(DragAndDropTests, ContentEditableToContentEditable) > EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); > EXPECT_TRUE([observedEventNames containsObject:@"drop"]); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 961, 227) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(960, 201, 2, 227), [simulator finalSelectionStartRect]); > checkRichTextTypePrecedesPlainTextType(simulator.get()); > EXPECT_TRUE([simulator lastKnownDropProposal].precise); > >@@ -414,7 +402,7 @@ TEST(DragAndDropTests, ContentEditableToTextarea) > EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); > EXPECT_TRUE([observedEventNames containsObject:@"drop"]); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 203, 990, 232) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(1089, 203, 2, 232), [simulator finalSelectionStartRect]); > checkRichTextTypePrecedesPlainTextType(simulator.get()); > EXPECT_TRUE([simulator lastKnownDropProposal].precise); > } >@@ -471,7 +459,7 @@ TEST(DragAndDropTests, ContentEditableMoveParagraphs) > EXPECT_FALSE(firstParagraphOffset == NSNotFound); > EXPECT_FALSE(secondParagraphOffset == NSNotFound); > EXPECT_GT(firstParagraphOffset, secondParagraphOffset); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(190, 100, 130, 20), makeCGRectValue(0, 120, 320, 100), makeCGRectValue(0, 220, 252, 20) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(251, 220, 2, 20), [simulator finalSelectionStartRect]); > EXPECT_TRUE([simulator lastKnownDropProposal].precise); > } > >@@ -497,7 +485,7 @@ TEST(DragAndDropTests, TextAreaToInput) > EXPECT_TRUE([simulator suppressedSelectionCommandsDuringDrop]); > EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.value"].length, 0UL); > EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 990, 232) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(1089, 241, 2, 232), [simulator finalSelectionStartRect]); > } > > TEST(DragAndDropTests, SinglePlainTextWordTypeIdentifiers) >@@ -568,7 +556,7 @@ TEST(DragAndDropTests, LinkToInput) > EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); > EXPECT_TRUE([observedEventNames containsObject:@"drop"]); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 273, 2057, 232) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(2156, 273, 2, 232), [simulator finalSelectionStartRect]); > checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypeURL, 0); > } > >@@ -586,7 +574,7 @@ TEST(DragAndDropTests, BackgroundImageLinkToInput) > EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); > EXPECT_TRUE([observedEventNames containsObject:@"drop"]); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(101, 241, 2057, 232) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(2156, 241, 2, 232), [simulator finalSelectionStartRect]); > checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypeURL, 0); > } > >@@ -604,7 +592,7 @@ TEST(DragAndDropTests, CanPreventStart) > NSArray *observedEventNames = [simulator observedEventNames]; > EXPECT_FALSE([observedEventNames containsObject:@"dragenter"]); > EXPECT_FALSE([observedEventNames containsObject:@"dragover"]); >- checkSelectionRectsWithLogging(@[ ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(0, 0, 0, 0), [simulator finalSelectionStartRect]); > } > > TEST(DragAndDropTests, CanPreventOperation) >@@ -620,7 +608,7 @@ TEST(DragAndDropTests, CanPreventOperation) > NSArray *observedEventNames = [simulator observedEventNames]; > EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); >- checkSelectionRectsWithLogging(@[ ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(0, 0, 0, 0), [simulator finalSelectionStartRect]); > } > > TEST(DragAndDropTests, EnterAndLeaveEvents) >@@ -638,7 +626,7 @@ TEST(DragAndDropTests, EnterAndLeaveEvents) > EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); > EXPECT_TRUE([observedEventNames containsObject:@"dragleave"]); > EXPECT_FALSE([observedEventNames containsObject:@"drop"]); >- checkSelectionRectsWithLogging(@[ ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(0, 0, 0, 0), [simulator finalSelectionStartRect]); > } > > TEST(DragAndDropTests, CanStartDragOnDivWithDraggableAttribute) >@@ -1025,7 +1013,7 @@ TEST(DragAndDropTests, ExternalSourceUTF8PlainTextOnly) > [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; > [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; > EXPECT_WK_STREQ(textPayload.UTF8String, [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 1936, 227) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(1935, 201, 2, 227), [simulator finalSelectionStartRect]); > } > > TEST(DragAndDropTests, ExternalSourceJPEGOnly) >@@ -1045,7 +1033,7 @@ TEST(DragAndDropTests, ExternalSourceJPEGOnly) > [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; > [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; > EXPECT_TRUE([webView editorContainsImageElement]); >- checkSelectionRectsWithLogging(@[ makeCGRectValue(1, 201, 215, 174) ], [simulator finalSelectionRects]); >+ checkCGRectIsEqualToCGRectWithLogging(CGRectMake(214, 201, 2, 223), [simulator finalSelectionStartRect]); > } > > TEST(DragAndDropTests, ExternalSourceTitledNSURL) >diff --git a/Tools/TestWebKitAPI/cocoa/DragAndDropSimulator.h b/Tools/TestWebKitAPI/cocoa/DragAndDropSimulator.h >index c5bc0de792f8f3639f3ceb1c1e0d8ae00f1d5609..999c118b60e451f8c9cdb6ce6e82e5b57ac1e110 100644 >--- a/Tools/TestWebKitAPI/cocoa/DragAndDropSimulator.h >+++ b/Tools/TestWebKitAPI/cocoa/DragAndDropSimulator.h >@@ -108,7 +108,7 @@ typedef NSDictionary<NSNumber *, NSValue *> *ProgressToCGPointValueMap; > > @property (nonatomic, readonly) NSArray *sourceItemProviders; > @property (nonatomic, readonly) NSArray *observedEventNames; >-@property (nonatomic, readonly) NSArray *finalSelectionRects; >+@property (nonatomic, readonly) CGRect finalSelectionStartRect; > @property (nonatomic, readonly) CGRect lastKnownDragCaretRect; > @property (nonatomic, readonly) NSArray<UITargetedDragPreview *> *liftPreviews; > @property (nonatomic, readonly) NSArray *dropPreviews; >diff --git a/Tools/TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm b/Tools/TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm >index 8edae8ce8a9484358532727e6685f01a750cfa20..f6fb657d3339c620bbef6ca96a7a17287cccb92c 100644 >--- a/Tools/TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm >+++ b/Tools/TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm >@@ -303,7 +303,7 @@ @implementation DragAndDropSimulator { > RetainPtr<NSMutableArray> _observedEventNames; > RetainPtr<NSArray> _externalItemProviders; > RetainPtr<NSArray> _sourceItemProviders; >- RetainPtr<NSArray> _finalSelectionRects; >+ CGRect _finalSelectionStartRect; > CGPoint _startLocation; > CGPoint _endLocation; > CGRect _lastKnownDragCaretRect; >@@ -381,7 +381,7 @@ - (void)_resetSimulatedState > _observedEventNames = adoptNS([[NSMutableArray alloc] init]); > _insertedAttachments = adoptNS([[NSMutableArray alloc] init]); > _removedAttachments = adoptNS([[NSMutableArray alloc] init]); >- _finalSelectionRects = @[ ]; >+ _finalSelectionStartRect = CGRectNull; > _dragSession = nil; > _dropSession = nil; > _lastKnownDropProposal = nil; >@@ -463,14 +463,12 @@ - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation additionalItemReq > Util::run(&_isDoneWithCurrentRun); > Util::run(&_isDoneWaitingForDelayedDropPreviews); > [_webView clearMessageHandlers:dragAndDropEventNames()]; >- _finalSelectionRects = [_webView selectionRectsAfterPresentationUpdate]; >+ [_webView waitForNextPresentationUpdate]; > >- [defaultCenter removeObserver:self]; >-} >+ auto contentView = [_webView textInputContentView]; >+ _finalSelectionStartRect = [contentView caretRectForPosition:contentView.selectedTextRange.start]; > >-- (NSArray *)finalSelectionRects >-{ >- return _finalSelectionRects.get(); >+ [defaultCenter removeObserver:self]; > } > > - (void)_concludeDropAndPerformOperationIfNecessary
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 198267
: 370669