blob: e55c309105754bbcc8047b7580d9740e21d33ac3 [file] [log] [blame]
/*
* Copyright (C) 2005-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "WebFrameInternal.h"
#import "DOMCSSStyleDeclarationInternal.h"
#import "DOMDocumentFragmentInternal.h"
#import "DOMDocumentInternal.h"
#import "DOMElementInternal.h"
#import "DOMHTMLElementInternal.h"
#import "DOMNodeInternal.h"
#import "DOMRangeInternal.h"
#import "WebArchiveInternal.h"
#import "WebChromeClient.h"
#import "WebDataSourceInternal.h"
#import "WebDocumentLoaderMac.h"
#import "WebDynamicScrollBarsView.h"
#import "WebEditorClient.h"
#import "WebElementDictionary.h"
#import "WebFrameLoaderClient.h"
#import "WebFrameViewInternal.h"
#import "WebHTMLView.h"
#import "WebHTMLViewInternal.h"
#import "WebKitStatisticsPrivate.h"
#import "WebKitVersionChecks.h"
#import "WebNSObjectExtras.h"
#import "WebNSURLExtras.h"
#import "WebScriptDebugger.h"
#import "WebScriptWorldInternal.h"
#import "WebViewInternal.h"
#import <JavaScriptCore/APICast.h>
#import <JavaScriptCore/JSCJSValue.h>
#import <JavaScriptCore/JSContextInternal.h>
#import <JavaScriptCore/JSGlobalObjectInlines.h>
#import <JavaScriptCore/JSLock.h>
#import <JavaScriptCore/JSObject.h>
#import <WebCore/AXObjectCache.h>
#import <WebCore/AccessibilityObject.h>
#import <WebCore/CSSAnimationController.h>
#import <WebCore/CSSStyleDeclaration.h>
#import <WebCore/CachedResourceLoader.h>
#import <WebCore/Chrome.h>
#import <WebCore/ColorMac.h>
#import <WebCore/CompositionHighlight.h>
#import <WebCore/DatabaseManager.h>
#import <WebCore/DocumentFragment.h>
#import <WebCore/DocumentLoader.h>
#import <WebCore/DocumentMarkerController.h>
#import <WebCore/Editing.h>
#import <WebCore/Editor.h>
#import <WebCore/EventHandler.h>
#import <WebCore/EventNames.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameLoadRequest.h>
#import <WebCore/FrameLoader.h>
#import <WebCore/FrameLoaderStateMachine.h>
#import <WebCore/FrameSelection.h>
#import <WebCore/FrameTree.h>
#import <WebCore/GraphicsContext.h>
#import <WebCore/HTMLFrameOwnerElement.h>
#import <WebCore/HTMLNames.h>
#import <WebCore/HistoryItem.h>
#import <WebCore/HitTestResult.h>
#import <WebCore/JSNode.h>
#import <WebCore/LegacyWebArchive.h>
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/Page.h>
#import <WebCore/PlatformEventFactoryMac.h>
#import <WebCore/PluginData.h>
#import <WebCore/PrintContext.h>
#import <WebCore/RenderLayer.h>
#import <WebCore/RenderView.h>
#import <WebCore/RenderWidget.h>
#import <WebCore/RenderedDocumentMarker.h>
#import <WebCore/RuntimeApplicationChecks.h>
#import <WebCore/ScriptController.h>
#import <WebCore/SecurityOrigin.h>
#import <WebCore/SmartReplace.h>
#import <WebCore/StyleProperties.h>
#import <WebCore/SubframeLoader.h>
#import <WebCore/TextIterator.h>
#import <WebCore/ThreadCheck.h>
#import <WebCore/VisibleUnits.h>
#import <WebCore/markup.h>
#import <pal/spi/cg/CoreGraphicsSPI.h>
#if PLATFORM(IOS_FAMILY)
#import "WebMailDelegate.h"
#import "WebResource.h"
#import "WebUIKitDelegate.h"
#import <WebCore/Document.h>
#import <WebCore/FocusController.h>
#import <WebCore/Font.h>
#import <WebCore/FrameSelection.h>
#import <WebCore/HistoryController.h>
#import <WebCore/NodeTraversal.h>
#import <WebCore/RenderLayer.h>
#import <WebCore/TextResourceDecoder.h>
#import <WebCore/WAKScrollView.h>
#import <WebCore/WAKWindow.h>
#import <WebCore/WKGraphics.h>
#import <WebCore/WebCoreThreadRun.h>
#endif
#if USE(QUICK_LOOK)
#import <WebCore/QuickLook.h>
#import <WebCore/WebCoreURLResponseIOS.h>
#endif
using JSC::JSGlobalObject;
using JSC::JSLock;
/*
Here is the current behavior matrix for four types of navigations:
Standard Nav:
Restore form state: YES
Restore scroll and focus state: YES
Cache policy: NSURLRequestUseProtocolCachePolicy
Add to back/forward list: YES
Back/Forward:
Restore form state: YES
Restore scroll and focus state: YES
Cache policy: NSURLRequestReturnCacheDataElseLoad
Add to back/forward list: NO
Reload (meaning only the reload button):
Restore form state: NO
Restore scroll and focus state: YES
Cache policy: NSURLRequestReloadIgnoringCacheData
Add to back/forward list: NO
Repeat load of the same URL (by any other means of navigation other than the reload button, including hitting return in the location field):
Restore form state: NO
Restore scroll and focus state: NO, reset to initial conditions
Cache policy: NSURLRequestReloadIgnoringCacheData
Add to back/forward list: NO
*/
NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey";
NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey";
NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey";
NSString *WebFrameMainDocumentError = @"WebFrameMainDocumentErrorKey";
NSString *WebFrameHasPlugins = @"WebFrameHasPluginsKey";
NSString *WebFrameHasUnloadListener = @"WebFrameHasUnloadListenerKey";
NSString *WebFrameUsesDatabases = @"WebFrameUsesDatabasesKey";
NSString *WebFrameUsesGeolocation = @"WebFrameUsesGeolocationKey";
NSString *WebFrameUsesApplicationCache = @"WebFrameUsesApplicationCacheKey";
NSString *WebFrameCanSuspendActiveDOMObjects = @"WebFrameCanSuspendActiveDOMObjectsKey";
// FIXME: Remove when this key becomes publicly defined
NSString *NSAccessibilityEnhancedUserInterfaceAttribute = @"AXEnhancedUserInterface";
@implementation WebFramePrivate
- (void)dealloc
{
[webFrameView release];
[super dealloc];
}
- (void)setWebFrameView:(WebFrameView *)v
{
[v retain];
[webFrameView release];
webFrameView = v;
}
@end
WebCore::EditableLinkBehavior core(WebKitEditableLinkBehavior editableLinkBehavior)
{
using namespace WebCore;
switch (editableLinkBehavior) {
case WebKitEditableLinkDefaultBehavior:
return EditableLinkDefaultBehavior;
case WebKitEditableLinkAlwaysLive:
return EditableLinkAlwaysLive;
case WebKitEditableLinkOnlyLiveWithShiftKey:
return EditableLinkOnlyLiveWithShiftKey;
case WebKitEditableLinkLiveWhenNotFocused:
return EditableLinkLiveWhenNotFocused;
case WebKitEditableLinkNeverLive:
return EditableLinkNeverLive;
}
ASSERT_NOT_REACHED();
return EditableLinkDefaultBehavior;
}
WebCore::TextDirectionSubmenuInclusionBehavior core(WebTextDirectionSubmenuInclusionBehavior behavior)
{
using namespace WebCore;
switch (behavior) {
case WebTextDirectionSubmenuNeverIncluded:
return TextDirectionSubmenuNeverIncluded;
case WebTextDirectionSubmenuAutomaticallyIncluded:
return TextDirectionSubmenuAutomaticallyIncluded;
case WebTextDirectionSubmenuAlwaysIncluded:
return TextDirectionSubmenuAlwaysIncluded;
}
ASSERT_NOT_REACHED();
return TextDirectionSubmenuNeverIncluded;
}
#if PLATFORM(IOS_FAMILY)
Vector<Vector<String>> vectorForDictationPhrasesArray(NSArray *dictationPhrases)
{
Vector<Vector<String>> result;
for (id dictationPhrase in dictationPhrases) {
if (![dictationPhrase isKindOfClass:[NSArray class]])
continue;
result.append(Vector<String>());
for (id interpretation : (NSArray *)dictationPhrase) {
if (![interpretation isKindOfClass:[NSString class]])
continue;
result.last().append((NSString *)interpretation);
}
}
return result;
}
#endif
@implementation WebFrame (WebInternal)
WebCore::Frame* core(WebFrame *frame)
{
return frame ? frame->_private->coreFrame : 0;
}
WebFrame *kit(WebCore::Frame* frame)
{
if (!frame)
return nil;
WebCore::FrameLoaderClient& frameLoaderClient = frame->loader().client();
if (frameLoaderClient.isEmptyFrameLoaderClient())
return nil;
return static_cast<WebFrameLoaderClient&>(frameLoaderClient).webFrame();
}
WebCore::Page* core(WebView *webView)
{
return [webView page];
}
WebView *kit(WebCore::Page* page)
{
if (!page)
return nil;
if (page->chrome().client().isEmptyChromeClient())
return nil;
return static_cast<WebChromeClient&>(page->chrome().client()).webView();
}
WebView *getWebView(WebFrame *webFrame)
{
auto coreFrame = core(webFrame);
if (!coreFrame)
return nil;
return kit(coreFrame->page());
}
+ (Ref<WebCore::Frame>)_createFrameWithPage:(WebCore::Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView ownerElement:(WebCore::HTMLFrameOwnerElement*)ownerElement
{
WebView *webView = kit(page);
WebFrame *frame = [[self alloc] _initWithWebFrameView:frameView webView:webView];
auto coreFrame = WebCore::Frame::create(page, ownerElement, makeUniqueRef<WebFrameLoaderClient>(frame));
[frame release];
frame->_private->coreFrame = coreFrame.ptr();
coreFrame.get().tree().setName(name);
if (ownerElement) {
ASSERT(ownerElement->document().frame());
ownerElement->document().frame()->tree().appendChild(coreFrame.get());
}
coreFrame.get().init();
[webView _setZoomMultiplier:[webView _realZoomMultiplier] isTextOnly:[webView _realZoomMultiplierIsTextOnly]];
return coreFrame;
}
+ (void)_createMainFrameWithPage:(WebCore::Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView
{
WebView *webView = kit(page);
WebFrame *frame = [[self alloc] _initWithWebFrameView:frameView webView:webView];
frame->_private->coreFrame = &page->mainFrame();
static_cast<WebFrameLoaderClient&>(page->mainFrame().loader().client()).setWebFrame(*frame);
[frame release];
page->mainFrame().tree().setName(name);
page->mainFrame().init();
[webView _setZoomMultiplier:[webView _realZoomMultiplier] isTextOnly:[webView _realZoomMultiplierIsTextOnly]];
}
+ (Ref<WebCore::Frame>)_createSubframeWithOwnerElement:(WebCore::HTMLFrameOwnerElement*)ownerElement frameName:(const String&)name frameView:(WebFrameView *)frameView
{
return [self _createFrameWithPage:ownerElement->document().frame()->page() frameName:name frameView:frameView ownerElement:ownerElement];
}
- (BOOL)_isIncludedInWebKitStatistics
{
return _private && _private->includedInWebKitStatistics;
}
#if PLATFORM(IOS_FAMILY)
static NSURL *createUniqueWebDataURL();
+ (void)_createMainFrameWithSimpleHTMLDocumentWithPage:(WebCore::Page*)page frameView:(WebFrameView *)frameView style:(NSString *)style
{
WebView *webView = kit(page);
WebFrame *frame = [[self alloc] _initWithWebFrameView:frameView webView:webView];
frame->_private->coreFrame = &page->mainFrame();
static_cast<WebFrameLoaderClient&>(page->mainFrame().loader().client()).setWebFrame(*frame);
[frame release];
frame->_private->coreFrame->initWithSimpleHTMLDocument(style, createUniqueWebDataURL());
}
#endif
- (void)_attachScriptDebugger
{
auto& windowProxy = _private->coreFrame->windowProxy();
// Calling ScriptController::globalObject() would create a window proxy, and dispatch corresponding callbacks, which may be premature
// if the script debugger is attached before a document is created. These calls use the debuggerWorld(), we will need to pass a world
// to be able to debug isolated worlds.
if (!windowProxy.existingJSWindowProxy(WebCore::debuggerWorld()))
return;
auto* globalObject = windowProxy.globalObject(WebCore::debuggerWorld());
if (!globalObject)
return;
if (_private->scriptDebugger) {
ASSERT(_private->scriptDebugger.get() == globalObject->debugger());
return;
}
_private->scriptDebugger = makeUnique<WebScriptDebugger>(globalObject);
}
- (void)_detachScriptDebugger
{
_private->scriptDebugger = nullptr;
}
- (id)_initWithWebFrameView:(WebFrameView *)fv webView:(WebView *)v
{
self = [super init];
if (!self)
return nil;
_private = [[WebFramePrivate alloc] init];
// Set includedInWebKitStatistics before calling WebFrameView _setWebFrame, since
// it calls WebFrame _isIncludedInWebKitStatistics.
if ((_private->includedInWebKitStatistics = [[v class] shouldIncludeInWebKitStatistics]))
++WebFrameCount;
if (fv) {
[_private setWebFrameView:fv];
[fv _setWebFrame:self];
}
_private->shouldCreateRenderers = YES;
return self;
}
- (void)_clearCoreFrame
{
_private->coreFrame = 0;
}
- (WebHTMLView *)_webHTMLDocumentView
{
id documentView = [_private->webFrameView documentView];
return [documentView isKindOfClass:[WebHTMLView class]] ? (WebHTMLView *)documentView : nil;
}
- (void)_updateBackgroundAndUpdatesWhileOffscreen
{
WebView *webView = getWebView(self);
BOOL drawsBackground = [webView drawsBackground];
#if !PLATFORM(IOS_FAMILY)
NSColor *backgroundColor = [webView backgroundColor];
#else
CGColorRef backgroundColor = [webView backgroundColor];
#endif
auto coreFrame = _private->coreFrame;
for (auto frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
// Don't call setDrawsBackground:YES here because it may be NO because of a load
// in progress; WebFrameLoaderClient keeps it set to NO during the load process.
WebFrame *webFrame = kit(frame);
if (!drawsBackground)
[[[webFrame frameView] _scrollView] setDrawsBackground:NO];
#if !PLATFORM(IOS_FAMILY)
[[[webFrame frameView] _scrollView] setBackgroundColor:backgroundColor];
#endif
if (auto* view = frame->view()) {
view->setTransparent(!drawsBackground);
#if !PLATFORM(IOS_FAMILY)
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WebCore::Color color = WebCore::colorFromNSColor([backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
ALLOW_DEPRECATED_DECLARATIONS_END
#else
WebCore::Color color = WebCore::Color(backgroundColor);
#endif
view->setBaseBackgroundColor(color);
view->setShouldUpdateWhileOffscreen([webView shouldUpdateWhileOffscreen]);
}
}
}
- (void)_setInternalLoadDelegate:(id)internalLoadDelegate
{
_private->internalLoadDelegate = internalLoadDelegate;
}
- (id)_internalLoadDelegate
{
return _private->internalLoadDelegate;
}
- (void)_unmarkAllBadGrammar
{
auto coreFrame = _private->coreFrame;
for (auto frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
if (auto* document = frame->document())
document->markers().removeMarkers(WebCore::DocumentMarker::Grammar);
}
}
- (void)_unmarkAllMisspellings
{
#if !PLATFORM(IOS_FAMILY)
auto coreFrame = _private->coreFrame;
for (auto frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
if (auto* document = frame->document())
document->markers().removeMarkers(WebCore::DocumentMarker::Spelling);
}
#endif
}
- (BOOL)_hasSelection
{
id documentView = [_private->webFrameView documentView];
// optimization for common case to avoid creating potentially large selection string
if ([documentView isKindOfClass:[WebHTMLView class]])
if (auto coreFrame = _private->coreFrame)
return coreFrame->selection().isRange();
if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
return [[documentView selectedString] length] > 0;
return NO;
}
- (void)_clearSelection
{
id documentView = [_private->webFrameView documentView];
if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
[documentView deselectAll];
}
#if ASSERT_ENABLED
- (BOOL)_atMostOneFrameHasSelection
{
// FIXME: 4186050 is one known case that makes this debug check fail.
BOOL found = NO;
auto coreFrame = _private->coreFrame;
for (auto frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
if ([kit(frame) _hasSelection]) {
if (found)
return NO;
found = YES;
}
}
return YES;
}
#endif // ASSERT_ENABLED
- (WebFrame *)_findFrameWithSelection
{
auto coreFrame = _private->coreFrame;
for (auto frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
WebFrame *webFrame = kit(frame);
if ([webFrame _hasSelection])
return webFrame;
}
return nil;
}
- (void)_clearSelectionInOtherFrames
{
// We rely on WebDocumentSelection protocol implementors to call this method when they become first
// responder. It would be nicer to just notice first responder changes here instead, but there's no
// notification sent when the first responder changes in general (Radar 2573089).
WebFrame *frameWithSelection = [[getWebView(self) mainFrame] _findFrameWithSelection];
if (frameWithSelection != self)
[frameWithSelection _clearSelection];
// While we're in the general area of selection and frames, check that there is only one now.
ASSERT([[getWebView(self) mainFrame] _atMostOneFrameHasSelection]);
}
- (WebDataSource *)_dataSource
{
return dataSource(_private->coreFrame->loader().documentLoader());
}
#if PLATFORM(IOS_FAMILY)
- (BOOL)_isCommitting
{
return _private->isCommitting;
}
- (void)_setIsCommitting:(BOOL)value
{
_private->isCommitting = value;
}
#endif
- (NSArray *)_nodesFromList:(Vector<WebCore::Node*> *)nodesVector
{
size_t size = nodesVector->size();
NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:size];
for (size_t i = 0; i < size; ++i)
[nodes addObject:kit((*nodesVector)[i])];
return nodes;
}
- (NSString *)_selectedString
{
return _private->coreFrame->displayStringModifiedByEncoding(_private->coreFrame->editor().selectedText());
}
- (NSString *)_stringForRange:(DOMRange *)range
{
if (!range)
return @"";
return plainText(*core(range), WebCore::TextIteratorDefaultBehavior, true);
}
- (OptionSet<WebCore::PaintBehavior>)_paintBehaviorForDestinationContext:(CGContextRef)context
{
#if PLATFORM(MAC)
// -currentContextDrawingToScreen returns YES for bitmap contexts.
BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
if (isPrinting)
return OptionSet<WebCore::PaintBehavior>(WebCore::PaintBehavior::FlattenCompositingLayers) | WebCore::PaintBehavior::Snapshotting;
#endif
if (CGContextGetType(context) != kCGContextTypeBitmap)
return WebCore::PaintBehavior::Normal;
// If we're drawing into a bitmap, we could be snapshotting or drawing into a layer-backed view.
if (WebHTMLView *documentView = [self _webHTMLDocumentView]) {
#if PLATFORM(IOS_FAMILY)
return [[documentView window] isInSnapshottingPaint] ? WebCore::PaintBehavior::Snapshotting : WebCore::PaintBehavior::Normal;
#endif
#if PLATFORM(MAC)
if ([documentView _web_isDrawingIntoLayer])
return WebCore::PaintBehavior::Normal;
#endif
}
return OptionSet<WebCore::PaintBehavior>(WebCore::PaintBehavior::FlattenCompositingLayers) | WebCore::PaintBehavior::Snapshotting;
}
- (void)_drawRect:(NSRect)rect contentsOnly:(BOOL)contentsOnly
{
#if !PLATFORM(IOS_FAMILY)
ASSERT([[NSGraphicsContext currentContext] isFlipped]);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
CGContextRef ctx = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
ALLOW_DEPRECATED_DECLARATIONS_END
#else
CGContextRef ctx = WKGetCurrentGraphicsContext();
#endif
WebCore::GraphicsContext context(ctx);
#if PLATFORM(IOS_FAMILY)
WebCore::Frame *frame = core(self);
if (WebCore::Page* page = frame->page())
context.setIsAcceleratedContext(page->settings().acceleratedDrawingEnabled());
#elif PLATFORM(MAC)
if (WebHTMLView *htmlDocumentView = [self _webHTMLDocumentView])
context.setIsAcceleratedContext([htmlDocumentView _web_isDrawingIntoAcceleratedLayer]);
#endif
auto* view = _private->coreFrame->view();
OptionSet<WebCore::PaintBehavior> oldBehavior = view->paintBehavior();
OptionSet<WebCore::PaintBehavior> paintBehavior = oldBehavior;
if (auto* parentFrame = _private->coreFrame->tree().parent()) {
// For subframes, we need to inherit the paint behavior from our parent
if (auto* parentView = parentFrame ? parentFrame->view() : nullptr) {
if (parentView->paintBehavior().contains(WebCore::PaintBehavior::FlattenCompositingLayers))
paintBehavior.add(WebCore::PaintBehavior::FlattenCompositingLayers);
if (parentView->paintBehavior().contains(WebCore::PaintBehavior::Snapshotting))
paintBehavior.add(WebCore::PaintBehavior::Snapshotting);
if (parentView->paintBehavior().contains(WebCore::PaintBehavior::TileFirstPaint))
paintBehavior.add(WebCore::PaintBehavior::TileFirstPaint);
}
} else
paintBehavior.add([self _paintBehaviorForDestinationContext:ctx]);
view->setPaintBehavior(paintBehavior);
if (contentsOnly)
view->paintContents(context, WebCore::enclosingIntRect(rect));
else
view->paint(context, WebCore::enclosingIntRect(rect));
view->setPaintBehavior(oldBehavior);
}
- (BOOL)_getVisibleRect:(NSRect*)rect
{
ASSERT_ARG(rect, rect);
if (auto* ownerRenderer = _private->coreFrame->ownerRenderer()) {
if (ownerRenderer->needsLayout())
return NO;
*rect = ownerRenderer->pixelSnappedAbsoluteClippedOverflowRect();
return YES;
}
return NO;
}
- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string
{
return [self _stringByEvaluatingJavaScriptFromString:string forceUserGesture:true];
}
- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture
{
if (!string)
return @"";
RELEASE_ASSERT(isMainThread());
ASSERT(_private->coreFrame->document());
RetainPtr<WebFrame> protect(self); // Executing arbitrary JavaScript can destroy the frame.
#if PLATFORM(IOS_FAMILY)
ASSERT(WebThreadIsLockedOrDisabled());
JSC::JSGlobalObject* lexicalGlobalObject = _private->coreFrame->script().globalObject(WebCore::mainThreadNormalWorld());
JSC::JSLockHolder jscLock(lexicalGlobalObject);
#endif
JSC::JSValue result = _private->coreFrame->script().executeScriptIgnoringException(string, forceUserGesture);
if (!_private->coreFrame) // In case the script removed our frame from the page.
return @"";
// This bizarre set of rules matches behavior from WebKit for Safari 2.0.
// If you don't like it, use -[WebScriptObject evaluateWebScript:] or
// JSEvaluateScript instead, since they have less surprising semantics.
if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber()))
return @"";
#if !PLATFORM(IOS_FAMILY)
JSC::JSGlobalObject* lexicalGlobalObject = _private->coreFrame->script().globalObject(WebCore::mainThreadNormalWorld());
JSC::JSLockHolder lock(lexicalGlobalObject);
#endif
return result.toWTFString(lexicalGlobalObject);
}
- (NSRect)_caretRectAtPosition:(const WebCore::Position&)pos affinity:(NSSelectionAffinity)affinity
{
WebCore::VisiblePosition visiblePosition(pos, static_cast<WebCore::EAffinity>(affinity));
return visiblePosition.absoluteCaretBounds();
}
- (NSRect)_firstRectForDOMRange:(DOMRange *)range
{
return _private->coreFrame->editor().firstRectForRange(core(range));
}
- (void)_scrollDOMRangeToVisible:(DOMRange *)range
{
bool insideFixed = false; // FIXME: get via firstRectForRange().
NSRect rangeRect = [self _firstRectForDOMRange:range];
auto* startNode = core([range startContainer]);
if (startNode && startNode->renderer()) {
#if !PLATFORM(IOS_FAMILY)
startNode->renderer()->scrollRectToVisible(WebCore::enclosingIntRect(rangeRect), insideFixed, { WebCore::SelectionRevealMode::Reveal, WebCore::ScrollAlignment::alignToEdgeIfNeeded, WebCore::ScrollAlignment::alignToEdgeIfNeeded, WebCore::ShouldAllowCrossOriginScrolling::Yes });
#else
auto* layer = startNode->renderer()->enclosingLayer();
if (layer) {
layer->setAdjustForIOSCaretWhenScrolling(true);
startNode->renderer()->scrollRectToVisible(WebCore::enclosingIntRect(rangeRect), insideFixed, { WebCore::SelectionRevealMode::Reveal, WebCore::ScrollAlignment::alignToEdgeIfNeeded, WebCore::ScrollAlignment::alignToEdgeIfNeeded, WebCore::ShouldAllowCrossOriginScrolling::Yes });
layer->setAdjustForIOSCaretWhenScrolling(false);
_private->coreFrame->selection().setCaretRectNeedsUpdate();
_private->coreFrame->selection().updateAppearance();
}
#endif
}
}
#if PLATFORM(IOS_FAMILY)
- (void)_scrollDOMRangeToVisible:(DOMRange *)range withInset:(CGFloat)inset
{
bool insideFixed = false; // FIXME: get via firstRectForRange().
NSRect rangeRect = NSInsetRect([self _firstRectForDOMRange:range], inset, inset);
auto* startNode = core([range startContainer]);
if (startNode && startNode->renderer()) {
auto* layer = startNode->renderer()->enclosingLayer();
if (layer) {
layer->setAdjustForIOSCaretWhenScrolling(true);
startNode->renderer()->scrollRectToVisible(WebCore::enclosingIntRect(rangeRect), insideFixed, { WebCore::SelectionRevealMode::Reveal, WebCore::ScrollAlignment::alignToEdgeIfNeeded, WebCore::ScrollAlignment::alignToEdgeIfNeeded, WebCore::ShouldAllowCrossOriginScrolling::Yes});
layer->setAdjustForIOSCaretWhenScrolling(false);
auto coreFrame = core(self);
if (coreFrame) {
auto& frameSelection = coreFrame->selection();
frameSelection.setCaretRectNeedsUpdate();
frameSelection.updateAppearance();
}
}
}
}
#endif
- (BOOL)_needsLayout
{
return _private->coreFrame->view() ? _private->coreFrame->view()->needsLayout() : false;
}
#if !PLATFORM(IOS_FAMILY)
- (DOMRange *)_rangeByAlteringCurrentSelection:(WebCore::FrameSelection::EAlteration)alteration direction:(WebCore::SelectionDirection)direction granularity:(WebCore::TextGranularity)granularity
{
if (_private->coreFrame->selection().isNone())
return nil;
WebCore::FrameSelection selection;
selection.setSelection(_private->coreFrame->selection().selection());
selection.modify(alteration, direction, granularity);
return kit(selection.toNormalizedRange().get());
}
#endif
- (WebCore::TextGranularity)_selectionGranularity
{
return _private->coreFrame->selection().granularity();
}
- (NSRange)_convertToNSRange:(WebCore::Range *)range
{
if (!range)
return NSMakeRange(NSNotFound, 0);
auto frame = _private->coreFrame;
if (!frame)
return NSMakeRange(NSNotFound, 0);
auto* element = frame->selection().rootEditableElementOrDocumentElement();
if (!element)
return NSMakeRange(NSNotFound, 0);
return characterRange(makeBoundaryPointBeforeNodeContents(*element), *range);
}
- (RefPtr<WebCore::Range>)_convertToDOMRange:(NSRange)nsrange
{
return [self _convertToDOMRange:nsrange rangeIsRelativeTo:WebRangeIsRelativeTo::EditableRoot];
}
- (RefPtr<WebCore::Range>)_convertToDOMRange:(NSRange)range rangeIsRelativeTo:(WebRangeIsRelativeTo)rangeIsRelativeTo
{
if (range.location == NSNotFound)
return nullptr;
if (rangeIsRelativeTo == WebRangeIsRelativeTo::EditableRoot) {
// Our critical assumption is that this code path is only called by input methods that
// concentrate on a given area containing the selection
// We have to do this because of text fields and textareas. The DOM for those is not
// directly in the document DOM, so serialization is problematic. Our solution is
// to use the root editable element of the selection start as the positional base.
// That fits with AppKit's idea of an input context.
auto* element = _private->coreFrame->selection().rootEditableElementOrDocumentElement();
if (!element)
return nullptr;
return createLiveRange(resolveCharacterRange(makeRangeSelectingNodeContents(*element), range));
}
ASSERT(rangeIsRelativeTo == WebRangeIsRelativeTo::Paragraph);
auto paragraphStart = makeBoundaryPoint(startOfParagraph(_private->coreFrame->selection().selection().visibleStart()));
if (!paragraphStart)
return nullptr;
auto scopeEnd = makeRangeSelectingNodeContents(paragraphStart->container->treeScope().rootNode()).end;
return createLiveRange(WebCore::resolveCharacterRange({ WTFMove(*paragraphStart), WTFMove(scopeEnd) }, range));
}
- (DOMRange *)_convertNSRangeToDOMRange:(NSRange)nsrange
{
return kit([self _convertToDOMRange:nsrange].get());
}
- (NSRange)_convertDOMRangeToNSRange:(DOMRange *)range
{
return [self _convertToNSRange:core(range)];
}
- (DOMRange *)_markDOMRange
{
return kit(_private->coreFrame->editor().mark().toNormalizedRange().get());
}
- (DOMDocumentFragment *)_documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString
{
auto frame = _private->coreFrame;
if (!frame)
return nil;
auto* document = frame->document();
if (!document)
return nil;
return kit(createFragmentFromMarkup(*document, markupString, baseURLString, WebCore::DisallowScriptingContent).ptr());
}
- (DOMDocumentFragment *)_documentFragmentWithNodesAsParagraphs:(NSArray *)nodes
{
auto frame = _private->coreFrame;
if (!frame)
return nil;
auto* document = frame->document();
if (!document)
return nil;
NSEnumerator *nodeEnum = [nodes objectEnumerator];
Vector<WebCore::Node*> nodesVector;
DOMNode *node;
while ((node = [nodeEnum nextObject]))
nodesVector.append(core(node));
auto fragment = document->createDocumentFragment();
for (auto* node : nodesVector) {
auto element = createDefaultParagraphElement(*document);
element->appendChild(*node);
fragment->appendChild(element);
}
return kit(fragment.ptr());
}
- (void)_replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle
{
DOMDocumentFragment *fragment = kit(_private->coreFrame->document()->createDocumentFragment().ptr());
[fragment appendChild:node];
[self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle];
}
- (void)_insertParagraphSeparatorInQuotedContent
{
if (_private->coreFrame->selection().isNone())
return;
_private->coreFrame->editor().insertParagraphSeparatorInQuotedContent();
}
- (WebCore::VisiblePosition)_visiblePositionForPoint:(NSPoint)point
{
// FIXME: Someone with access to Apple's sources could remove this needless wrapper call.
return _private->coreFrame->visiblePositionForPoint(WebCore::IntPoint(point));
}
- (DOMRange *)_characterRangeAtPoint:(NSPoint)point
{
return kit(_private->coreFrame->rangeForPoint(WebCore::IntPoint(point)).get());
}
- (DOMCSSStyleDeclaration *)_typingStyle
{
if (!_private->coreFrame)
return nil;
RefPtr<WebCore::MutableStyleProperties> typingStyle = _private->coreFrame->selection().copyTypingStyle();
if (!typingStyle)
return nil;
return kit(&typingStyle->ensureCSSStyleDeclaration());
}
- (void)_setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(WebCore::EditAction)undoAction
{
if (!_private->coreFrame || !style)
return;
// FIXME: We shouldn't have to create a copy here.
Ref<WebCore::MutableStyleProperties> properties(core(style)->copyProperties());
_private->coreFrame->editor().computeAndSetTypingStyle(properties.get(), undoAction);
}
#if ENABLE(DRAG_SUPPORT) && PLATFORM(MAC)
- (void)_dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation
{
if (!_private->coreFrame)
return;
auto* view = _private->coreFrame->view();
if (!view)
return;
// FIXME: These are fake modifier keys here, but they should be real ones instead.
WebCore::PlatformMouseEvent event(WebCore::IntPoint(windowLoc), WebCore::IntPoint(WebCore::globalPoint(windowLoc, [view->platformWidget() window])),
WebCore::LeftButton, WebCore::PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, WebCore::NoTap);
_private->coreFrame->eventHandler().dragSourceEndedAt(event, (WebCore::DragOperation)operation);
}
#endif // ENABLE(DRAG_SUPPORT) && PLATFORM(MAC)
- (BOOL)_canProvideDocumentSource
{
auto frame = _private->coreFrame;
String mimeType = frame->document()->loader()->writer().mimeType();
auto* pluginData = frame->page() ? &frame->page()->pluginData() : 0;
if (WebCore::MIMETypeRegistry::isTextMIMEType(mimeType)
|| WebCore::Image::supportsType(mimeType)
|| (pluginData && pluginData->supportsWebVisibleMimeType(mimeType, WebCore::PluginData::AllPlugins) && frame->loader().subframeLoader().allowPlugins())
|| (pluginData && pluginData->supportsWebVisibleMimeType(mimeType, WebCore::PluginData::OnlyApplicationPlugins)))
return NO;
return YES;
}
- (BOOL)_canSaveAsWebArchive
{
// Currently, all documents that we can view source for
// (HTML and XML documents) can also be saved as web archives
return [self _canProvideDocumentSource];
}
- (void)_commitData:(NSData *)data
{
// FIXME: This really should be a setting.
auto* document = _private->coreFrame->document();
document->setShouldCreateRenderers(_private->shouldCreateRenderers);
_private->coreFrame->loader().documentLoader()->commitData((const char *)[data bytes], [data length]);
}
@end
@implementation WebFrame (WebPrivate)
// FIXME: This exists only as a convenience for Safari, consider moving there.
- (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor
{
auto coreFrame = _private->coreFrame;
return coreFrame && coreFrame->tree().isDescendantOf(core(ancestor));
}
- (void)_setShouldCreateRenderers:(BOOL)shouldCreateRenderers
{
_private->shouldCreateRenderers = shouldCreateRenderers;
}
#if !PLATFORM(IOS_FAMILY)
- (NSColor *)_bodyBackgroundColor
#else
- (CGColorRef)_bodyBackgroundColor
#endif
{
auto* document = _private->coreFrame->document();
if (!document)
return nil;
auto* body = document->bodyOrFrameset();
if (!body)
return nil;
auto* bodyRenderer = body->renderer();
if (!bodyRenderer)
return nil;
WebCore::Color color = bodyRenderer->style().visitedDependentColorWithColorFilter(WebCore::CSSPropertyBackgroundColor);
if (!color.isValid())
return nil;
#if !PLATFORM(IOS_FAMILY)
return nsColor(color);
#else
return cachedCGColor(color);
#endif
}
- (BOOL)_isFrameSet
{
auto* document = _private->coreFrame->document();
return document && document->isFrameSet();
}
- (BOOL)_firstLayoutDone
{
return _private->coreFrame->loader().stateMachine().firstLayoutDone();
}
- (BOOL)_isVisuallyNonEmpty
{
if (auto* view = _private->coreFrame->view())
return view->isVisuallyNonEmpty();
return NO;
}
static WebFrameLoadType toWebFrameLoadType(WebCore::FrameLoadType frameLoadType)
{
using namespace WebCore;
switch (frameLoadType) {
case FrameLoadType::Standard:
return WebFrameLoadTypeStandard;
case FrameLoadType::Back:
return WebFrameLoadTypeBack;
case FrameLoadType::Forward:
return WebFrameLoadTypeForward;
case FrameLoadType::IndexedBackForward:
return WebFrameLoadTypeIndexedBackForward;
case FrameLoadType::Reload:
return WebFrameLoadTypeReload;
case FrameLoadType::Same:
return WebFrameLoadTypeSame;
case FrameLoadType::RedirectWithLockedBackForwardList:
return WebFrameLoadTypeInternal;
case FrameLoadType::Replace:
return WebFrameLoadTypeReplace;
case FrameLoadType::ReloadFromOrigin:
return WebFrameLoadTypeReloadFromOrigin;
case FrameLoadType::ReloadExpiredOnly:
ASSERT_NOT_REACHED();
return WebFrameLoadTypeReload;
}
}
- (WebFrameLoadType)_loadType
{
return toWebFrameLoadType(_private->coreFrame->loader().loadType());
}
#if PLATFORM(IOS_FAMILY)
- (BOOL)needsLayout
{
// Needed for Mail <rdar://problem/6228038>
return _private->coreFrame ? [self _needsLayout] : NO;
}
- (void)_setLoadsSynchronously:(BOOL)flag
{
_private->coreFrame->loader().setLoadsSynchronously(flag);
}
- (BOOL)_loadsSynchronously
{
return _private->coreFrame->loader().loadsSynchronously();
}
// FIXME: selection
- (NSArray *)_rectsForRange:(DOMRange *)domRange
{
auto* range = core(domRange);
Vector<WebCore::IntRect> intRects;
range->absoluteTextRects(intRects, NO);
unsigned size = intRects.size();
NSMutableArray *rectArray = [NSMutableArray arrayWithCapacity:size];
for (unsigned i = 0; i < size; i++) {
[rectArray addObject:[NSValue valueWithRect:(CGRect )intRects[i]]];
}
return rectArray;
}
- (DOMRange *)_selectionRangeForFirstPoint:(CGPoint)first secondPoint:(CGPoint)second
{
WebCore::VisiblePosition firstPos = [self _visiblePositionForPoint:first];
WebCore::VisiblePosition secondPos = [self _visiblePositionForPoint:second];
WebCore::VisibleSelection selection(firstPos, secondPos);
DOMRange *range = kit(selection.toNormalizedRange().get());
return range;
}
- (DOMRange *)_selectionRangeForPoint:(CGPoint)point
{
WebCore::VisiblePosition pos = [self _visiblePositionForPoint:point];
WebCore::VisibleSelection selection(pos);
DOMRange *range = kit(selection.toNormalizedRange().get());
return range;
}
#endif // PLATFORM(IOS_FAMILY)
- (NSRange)_selectedNSRange
{
return [self _convertToNSRange:_private->coreFrame->selection().toNormalizedRange().get()];
}
- (void)_selectNSRange:(NSRange)range
{
RefPtr<WebCore::Range> domRange = [self _convertToDOMRange:range];
if (domRange)
_private->coreFrame->selection().setSelection(WebCore::VisibleSelection(*domRange, WebCore::SEL_DEFAULT_AFFINITY));
}
- (BOOL)_isDisplayingStandaloneImage
{
auto* document = _private->coreFrame->document();
return document && document->isImageDocument();
}
- (unsigned)_pendingFrameUnloadEventCount
{
return _private->coreFrame->document()->domWindow()->pendingUnloadEventListeners();
}
#if ENABLE(NETSCAPE_PLUGIN_API)
- (void)_recursive_resumeNullEventsForAllNetscapePlugins
{
auto coreFrame = core(self);
for (auto* frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
NSView <WebDocumentView> *documentView = [[kit(frame) frameView] documentView];
if ([documentView isKindOfClass:[WebHTMLView class]])
[(WebHTMLView *)documentView _resumeNullEventsForAllNetscapePlugins];
}
}
- (void)_recursive_pauseNullEventsForAllNetscapePlugins
{
auto coreFrame = core(self);
for (auto* frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
NSView <WebDocumentView> *documentView = [[kit(frame) frameView] documentView];
if ([documentView isKindOfClass:[WebHTMLView class]])
[(WebHTMLView *)documentView _pauseNullEventsForAllNetscapePlugins];
}
}
#endif
#if PLATFORM(IOS_FAMILY)
- (unsigned)formElementsCharacterCount
{
return core(self)->formElementsCharacterCount();
}
- (void)setTimeoutsPaused:(BOOL)flag
{
if ([self _webHTMLDocumentView]) {
if (auto coreFrame = _private->coreFrame)
coreFrame->setTimersPaused(flag);
}
}
- (void)setPluginsPaused:(BOOL)flag
{
WebView *webView = getWebView(self);
if (!webView)
return;
if (flag)
[webView _stopAllPlugIns];
else
[webView _startAllPlugIns];
}
- (void)prepareForPause
{
if ([self _webHTMLDocumentView]) {
if (auto coreFrame = _private->coreFrame)
coreFrame->dispatchPageHideEventBeforePause();
}
}
- (void)resumeFromPause
{
if ([self _webHTMLDocumentView]) {
if (auto coreFrame = _private->coreFrame)
coreFrame->dispatchPageShowEventBeforeResume();
}
}
- (void)selectNSRange:(NSRange)range
{
[self _selectNSRange:range];
}
- (void)selectWithoutClosingTypingNSRange:(NSRange)range
{
RefPtr<WebCore::Range> domRange = [self _convertToDOMRange:range];
if (domRange) {
const auto& newSelection = WebCore::VisibleSelection(*domRange, WebCore::SEL_DEFAULT_AFFINITY);
_private->coreFrame->selection().setSelection(newSelection, { });
_private->coreFrame->editor().ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping();
}
}
- (NSRange)selectedNSRange
{
return [self _selectedNSRange];
}
- (void)forceLayoutAdjustingViewSize:(BOOL)adjust
{
_private->coreFrame->view()->forceLayout(!adjust);
if (adjust)
_private->coreFrame->view()->adjustViewSize();
}
- (void)_handleKeyEvent:(WebEvent *)event
{
core(self)->eventHandler().keyEvent(event);
}
- (void)_selectAll
{
core(self)->selection().selectAll();
}
- (void)_setSelectionFromNone
{
core(self)->selection().setSelectionFromNone();
}
- (void)_restoreViewState
{
ASSERT(!WebThreadIsEnabled() || WebThreadIsLocked());
_private->coreFrame->loader().client().restoreViewState();
}
- (void)_saveViewState
{
ASSERT(!WebThreadIsEnabled() || WebThreadIsLocked());
auto& frameLoader = _private->coreFrame->loader();
auto* item = frameLoader.history().currentItem();
if (item)
frameLoader.client().saveViewStateToItem(*item);
}
- (void)deviceOrientationChanged
{
WebThreadRun(^{
#if ENABLE(ORIENTATION_EVENTS)
WebView *webView = getWebView(self);
[webView _setDeviceOrientation:[[webView _UIKitDelegateForwarder] deviceOrientation]];
#endif
if (auto* frame = core(self))
frame->orientationChanged();
});
}
- (void)setNeedsLayout
{
WebCore::Frame *frame = core(self);
if (frame->view())
frame->view()->setNeedsLayoutAfterViewConfigurationChange();
}
- (CGSize)renderedSizeOfNode:(DOMNode *)node constrainedToWidth:(float)width
{
WebCore::Node* n = core(node);
auto* renderer = n ? n->renderer() : nullptr;
float w = std::min((float)renderer->maxPreferredLogicalWidth(), width);
return is<WebCore::RenderBox>(renderer) ? CGSizeMake(w, downcast<WebCore::RenderBox>(*renderer).height()) : CGSizeMake(0, 0);
}
- (DOMNode *)deepestNodeAtViewportLocation:(CGPoint)aViewportLocation
{
WebCore::Frame *frame = core(self);
return kit(frame->deepestNodeAtLocation(WebCore::FloatPoint(aViewportLocation)));
}
- (DOMNode *)scrollableNodeAtViewportLocation:(CGPoint)aViewportLocation
{
WebCore::Frame *frame = core(self);
WebCore::Node *node = frame->nodeRespondingToScrollWheelEvents(WebCore::FloatPoint(aViewportLocation));
return kit(node);
}
- (DOMNode *)approximateNodeAtViewportLocation:(CGPoint *)aViewportLocation
{
WebCore::Frame *frame = core(self);
WebCore::FloatPoint viewportLocation(*aViewportLocation);
WebCore::FloatPoint adjustedLocation;
WebCore::Node *node = frame->approximateNodeAtViewportLocationLegacy(viewportLocation, adjustedLocation);
*aViewportLocation = adjustedLocation;
return kit(node);
}
- (CGRect)renderRectForPoint:(CGPoint)point isReplaced:(BOOL *)isReplaced fontSize:(float *)fontSize
{
WebCore::Frame *frame = core(self);
bool replaced = false;
CGRect rect = frame->renderRectForPoint(point, &replaced, fontSize);
*isReplaced = replaced;
return rect;
}
- (void)_setProhibitsScrolling:(BOOL)flag
{
WebCore::Frame *frame = core(self);
frame->view()->setProhibitsScrolling(flag);
}
- (void)revealSelectionAtExtent:(BOOL)revealExtent
{
WebCore::Frame *frame = core(self);
WebCore::RevealExtentOption revealExtentOption = revealExtent ? WebCore::RevealExtent : WebCore::DoNotRevealExtent;
frame->selection().revealSelection(WebCore::SelectionRevealMode::Reveal, WebCore::ScrollAlignment::alignToEdgeIfNeeded, revealExtentOption);
}
- (void)resetSelection
{
WebCore::Frame *frame = core(self);
frame->selection().setSelection(frame->selection().selection());
}
- (BOOL)hasEditableSelection
{
return core(self)->selection().selection().isContentEditable();
}
- (int)preferredHeight
{
return core(self)->preferredHeight();
}
- (int)innerLineHeight:(DOMNode *)node
{
if (!node)
return 0;
auto& coreNode = *core(node);
coreNode.document().updateLayout();
auto* renderer = coreNode.renderer();
if (!renderer)
return 0;
return renderer->innerLineHeight();
}
- (void)updateLayout
{
WebCore::Frame *frame = core(self);
frame->updateLayout();
}
- (void)setIsActive:(BOOL)flag
{
WebCore::Frame *frame = core(self);
frame->page()->focusController().setActive(flag);
}
- (void)setSelectionChangeCallbacksDisabled:(BOOL)flag
{
WebCore::Frame *frame = core(self);
frame->setSelectionChangeCallbacksDisabled(flag);
}
- (NSRect)caretRect
{
return core(self)->caretRect();
}
- (NSRect)rectForScrollToVisible
{
return core(self)->rectForScrollToVisible();
}
- (void)setCaretColor:(CGColorRef)color
{
WebCore::Color qColor = color ? WebCore::Color(color) : WebCore::Color::black;
WebCore::Frame *frame = core(self);
frame->selection().setCaretColor(qColor);
}
- (CGColorRef)caretColor
{
auto* frame = core(self);
if (!frame)
return nil;
auto* document = frame->document();
if (!document)
return nil;
auto* focusedElement = document->focusedElement();
if (!focusedElement)
return nil;
auto* renderer = focusedElement->renderer();
if (!renderer)
return nil;
auto color = WebCore::CaretBase::computeCaretColor(renderer->style(), renderer->element());
return color.isValid() ? cachedCGColor(color) : nil;
}
- (NSView *)documentView
{
WebCore::Frame *frame = core(self);
return [[kit(frame) frameView] documentView];
}
- (int)layoutCount
{
WebCore::Frame *frame = core(self);
if (!frame || !frame->view())
return 0;
return frame->view()->layoutContext().layoutCount();
}
- (BOOL)isTelephoneNumberParsingAllowed
{
WebCore::Frame *frame = core(self);
if (!frame || !frame->document())
return false;
return frame->document()->isTelephoneNumberParsingAllowed();
}
- (BOOL)isTelephoneNumberParsingEnabled
{
WebCore::Frame *frame = core(self);
if (!frame || !frame->document())
return false;
return frame->document()->isTelephoneNumberParsingEnabled();
}
- (DOMRange *)selectedDOMRange
{
WebCore::Frame *frame = core(self);
RefPtr<WebCore::Range> range = frame->selection().toNormalizedRange();
return kit(range.get());
}
- (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)affinity closeTyping:(BOOL)closeTyping
{
[self setSelectedDOMRange:range affinity:affinity closeTyping:closeTyping userTriggered:NO];
}
- (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)affinity closeTyping:(BOOL)closeTyping userTriggered:(BOOL)userTriggered
{
using namespace WebCore;
auto& frame = *core(self);
if (!frame.page())
return;
// Ensure the view becomes first responder. This does not happen automatically on iOS because
// we don't forward all the click events to WebKit.
if (NSView *documentView = frame.view()->documentView())
frame.page()->chrome().focusNSView(documentView);
auto coreCloseTyping = closeTyping ? FrameSelection::ShouldCloseTyping::Yes : FrameSelection::ShouldCloseTyping::No;
auto coreUserTriggered = userTriggered ? UserTriggered : NotUserTriggered;
frame.selection().setSelectedRange(core(range), core(affinity), coreCloseTyping, coreUserTriggered);
if (!closeTyping)
frame.editor().ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping();
}
- (NSSelectionAffinity)selectionAffinity
{
WebCore::Frame *frame = core(self);
return (NSSelectionAffinity)(frame->selection().selection().affinity());
}
- (void)expandSelectionToElementContainingCaretSelection
{
WebCore::Frame *frame = core(self);
frame->selection().expandSelectionToElementContainingCaretSelection();
}
- (DOMRange *)elementRangeContainingCaretSelection
{
WebCore::Frame *frame = core(self);
RefPtr<WebCore::Range> range = frame->selection().elementRangeContainingCaretSelection();
return kit(range.get());
}
- (void)expandSelectionToWordContainingCaretSelection
{
WebCore::Frame *frame = core(self);
frame->selection().expandSelectionToWordContainingCaretSelection();
}
- (void)expandSelectionToStartOfWordContainingCaretSelection
{
WebCore::Frame *frame = core(self);
frame->selection().expandSelectionToStartOfWordContainingCaretSelection();
}
- (unichar)characterInRelationToCaretSelection:(int)amount
{
return core(self)->selection().characterInRelationToCaretSelection(amount);
}
- (unichar)characterBeforeCaretSelection
{
return core(self)->selection().characterBeforeCaretSelection();
}
- (unichar)characterAfterCaretSelection
{
return core(self)->selection().characterAfterCaretSelection();
}
- (DOMRange *)wordRangeContainingCaretSelection
{
WebCore::Frame *frame = core(self);
RefPtr<WebCore::Range> range = frame->selection().wordRangeContainingCaretSelection();
return kit(range.get());
}
- (NSString *)wordInRange:(DOMRange *)range
{
if (!range)
return nil;
return [self _stringForRange:range];
}
- (int)wordOffsetInRange:(DOMRange *)range
{
return core(self)->selection().wordOffsetInRange(core(range));
}
- (BOOL)spaceFollowsWordInRange:(DOMRange *)range
{
return core(self)->selection().spaceFollowsWordInRange(core(range));
}
- (NSArray *)wordsInCurrentParagraph
{
return core(self)->wordsInCurrentParagraph();
}
- (BOOL)selectionAtDocumentStart
{
WebCore::Frame *frame = core(self);
if (frame->selection().selection().isNone())
return NO;
frame->document()->updateLayout();
return frame->selection().selectionAtDocumentStart();
}
- (BOOL)selectionAtSentenceStart
{
WebCore::Frame *frame = core(self);
if (frame->selection().selection().isNone())
return NO;
frame->document()->updateLayout();
return frame->selection().selectionAtSentenceStart();
}
- (BOOL)selectionAtWordStart
{
WebCore::Frame *frame = core(self);
if (frame->selection().selection().isNone())
return NO;
frame->document()->updateLayout();
return frame->selection().selectionAtWordStart();
}
- (DOMRange *)rangeByMovingCurrentSelection:(int)amount
{
WebCore::Frame *frame = core(self);
RefPtr<WebCore::Range> range = frame->selection().rangeByMovingCurrentSelection(amount);
return kit(range.get());
}
- (DOMRange *)rangeByExtendingCurrentSelection:(int)amount
{
WebCore::Frame *frame = core(self);
RefPtr<WebCore::Range> range = frame->selection().rangeByExtendingCurrentSelection(amount);
return kit(range.get());
}
- (void)selectNSRange:(NSRange)range onElement:(DOMElement *)element
{
WebCore::Frame *frame = core(self);
WebCore::Document* doc = frame->document();
if (!doc)
return;
auto* node = core(element);
if (!node->isConnected())
return;
frame->selection().selectRangeOnElement(range.location, range.length, *node);
}
- (DOMRange *)markedTextDOMRange
{
WebCore::Frame *frame = core(self);
if (!frame)
return nil;
return kit(frame->editor().compositionRange().get());
}
- (void)setMarkedText:(NSString *)text selectedRange:(NSRange)newSelRange
{
WebCore::Frame *frame = core(self);
if (!frame)
return;
Vector<WebCore::CompositionUnderline> underlines;
frame->page()->chrome().client().suppressFormNotifications();
frame->editor().setComposition(text, underlines, { }, newSelRange.location, NSMaxRange(newSelRange));
frame->page()->chrome().client().restoreFormNotifications();
}
- (void)setMarkedText:(NSString *)text forCandidates:(BOOL)forCandidates
{
WebCore::Frame *frame = core(self);
if (!frame)
return;
Vector<WebCore::CompositionUnderline> underlines;
frame->editor().setComposition(text, underlines, { }, 0, [text length]);
}
- (void)confirmMarkedText:(NSString *)text
{
WebCore::Frame *frame = core(self);
if (!frame || !frame->editor().client())
return;
frame->page()->chrome().client().suppressFormNotifications();
if (text)
frame->editor().confirmComposition(text);
else
frame->editor().confirmMarkedText();
frame->page()->chrome().client().restoreFormNotifications();
}
- (void)setText:(NSString *)text asChildOfElement:(DOMElement *)element
{
if (!element)
return;
WebCore::Frame *frame = core(self);
if (!frame || !frame->document())
return;
frame->editor().setTextAsChildOfElement(text, *core(element));
}
- (void)setDictationPhrases:(NSArray *)dictationPhrases metadata:(id)metadata asChildOfElement:(DOMElement *)element
{
if (!element)
return;
auto* frame = core(self);
if (!frame)
return;
frame->editor().setDictationPhrasesAsChildOfElement(vectorForDictationPhrasesArray(dictationPhrases), metadata, *core(element));
}
- (NSArray *)interpretationsForCurrentRoot
{
return core(self)->interpretationsForCurrentRoot();
}
// Collects the ranges and metadata for all of the mars voltas in the root editable element.
- (void)getDictationResultRanges:(NSArray **)outRanges andMetadatas:(NSArray **)outMetadatas
{
ASSERT(outRanges);
if (!outRanges)
return;
// *outRanges should not already point to an array.
ASSERT(!(*outRanges));
*outRanges = nil;
ASSERT(outMetadatas);
if (!outMetadatas)
return;
// *metadata should not already point to an array.
ASSERT(!(*outMetadatas));
*outMetadatas = nil;
NSMutableArray *ranges = [NSMutableArray array];
NSMutableArray *metadatas = [NSMutableArray array];
auto* frame = core(self);
auto* document = frame->document();
const auto& selection = frame->selection().selection();
WebCore::Element* root = selection.selectionType() == WebCore::VisibleSelection::NoSelection ? frame->document()->bodyOrFrameset() : selection.rootEditableElement();
DOMRange *previousDOMRange = nil;
id previousMetadata = nil;
for (WebCore::Node* node = root; node; node = WebCore::NodeTraversal::next(*node)) {
auto markers = document->markers().markersFor(*node);
for (auto* marker : markers) {
if (marker->type() != WebCore::DocumentMarker::DictationResult)
continue;
id metadata = marker->metadata();
// All result markers should have metadata.
ASSERT(metadata);
if (!metadata)
continue;
auto range = WebCore::Range::create(*document, node, marker->startOffset(), node, marker->endOffset());
DOMRange *domRange = kit(range.ptr());
if (metadata != previousMetadata) {
[metadatas addObject:metadata];
[ranges addObject:domRange];
previousMetadata = metadata;
previousDOMRange = domRange;
} else {
// It is possible for a DocumentMarker to be split by editing. Adjacent markers with the
// the same metadata are for the same result. So combine their ranges.
ASSERT(previousDOMRange == [ranges lastObject]);
[previousDOMRange retain];
[ranges removeLastObject];
DOMNode *startContainer = [domRange startContainer];
int startOffset = [domRange startOffset];
[previousDOMRange setEnd:startContainer offset:startOffset];
[ranges addObject:previousDOMRange];
[previousDOMRange release];
}
}
}
*outRanges = ranges;
*outMetadatas = metadatas;
return;
}
- (id)dictationResultMetadataForRange:(DOMRange *)range
{
if (!range)
return nil;
auto markers = core(self)->document()->markers().markersInRange(*core(range), WebCore::DocumentMarker::DictationResult);
// UIKit should only ever give us a DOMRange for a phrase with alternatives, which should not be part of more than one result.
ASSERT(markers.size() <= 1);
if (markers.size() == 0)
return nil;
return markers[0]->metadata();
}
- (void)recursiveSetUpdateAppearanceEnabled:(BOOL)enabled
{
WebCore::Frame *frame = core(self);
if (frame)
frame->recursiveSetUpdateAppearanceEnabled(enabled);
}
// WebCoreFrameBridge methods used by iOS applications and frameworks
// FIXME: WebCoreFrameBridge is long gone. Can we remove these methods?
+ (NSString *)stringWithData:(NSData *)data textEncodingName:(NSString *)textEncodingName
{
WebCore::TextEncoding encoding(textEncodingName);
if (!encoding.isValid())
encoding = WebCore::WindowsLatin1Encoding();
return encoding.decode(reinterpret_cast<const char*>([data bytes]), [data length]);
}
- (NSRect)caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity
{
return [self _caretRectAtPosition:createLegacyEditingPosition(core(node), offset) affinity:affinity];
}
- (DOMRange *)characterRangeAtPoint:(NSPoint)point
{
return [self _characterRangeAtPoint:point];
}
- (NSRange)convertDOMRangeToNSRange:(DOMRange *)range
{
return [self _convertDOMRangeToNSRange:range];
}
- (DOMRange *)convertNSRangeToDOMRange:(NSRange)nsrange
{
return [self _convertNSRangeToDOMRange:nsrange];
}
- (NSRect)firstRectForDOMRange:(DOMRange *)range
{
return [self _firstRectForDOMRange:range];
}
- (CTFontRef)fontForSelection:(BOOL *)hasMultipleFonts
{
bool multipleFonts = false;
CTFontRef font = nil;
if (_private->coreFrame) {
const WebCore::Font* fd = _private->coreFrame->editor().fontForSelection(multipleFonts);
if (fd)
font = fd->getCTFont();
}
if (hasMultipleFonts)
*hasMultipleFonts = multipleFonts;
return font;
}
- (void)sendScrollEvent
{
ASSERT(WebThreadIsLockedOrDisabled());
_private->coreFrame->eventHandler().sendScrollEvent();
}
- (void)_userScrolled
{
ASSERT(WebThreadIsLockedOrDisabled());
if (auto* view = _private->coreFrame->view())
view->setWasScrolledByUser(true);
}
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture
{
return [self _stringByEvaluatingJavaScriptFromString:string forceUserGesture:forceUserGesture];
}
- (NSString *)stringForRange:(DOMRange *)range
{
return [self _stringForRange:range];
}
//
// FIXME: We needed to add this method for iOS due to the opensource version's inclusion of
// matchStyle:YES. It seems odd that we should need to explicitly match style, given that the
// fragment is being made out of plain text, which shouldn't be carrying any style of its own.
// When we paste that it will pick up its style from the surrounding content. What else would
// we expect? If we flipped that matchStyle bit to NO, we could probably just get rid
// of this method, and call the standard WebKit version.
//
// There's a second problem here, too, which is that ReplaceSelectionCommand sometimes adds
// redundant style.
//
- (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle
{
RefPtr<WebCore::Range> range = _private->coreFrame->selection().toNormalizedRange();
DOMDocumentFragment* fragment = range ? kit(createFragmentFromText(*range, text).ptr()) : nil;
[self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle];
}
- (void)_replaceSelectionWithWebArchive:(WebArchive *)webArchive selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace
{
NSArray* subresources = [webArchive subresources];
for (WebResource* subresource in subresources) {
if (![[self dataSource] subresourceForURL:[subresource URL]])
[[self dataSource] addSubresource:subresource];
}
DOMDocumentFragment* fragment = [[self dataSource] _documentFragmentWithArchive:webArchive];
[self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:NO];
}
#endif // PLATFORM(IOS_FAMILY)
#if ENABLE(TEXT_AUTOSIZING)
- (void)resetTextAutosizingBeforeLayout
{
if (![self _webHTMLDocumentView])
return;
auto coreFrame = core(self);
for (auto* frame = coreFrame; frame; frame = frame->tree().traverseNext(coreFrame)) {
WebCore::Document* doc = frame->document();
if (!doc || !doc->renderView())
continue;
doc->renderView()->resetTextAutosizing();
}
}
- (void)_setVisibleSize:(CGSize)size
{
[self _setTextAutosizingWidth:size.width];
}
- (void)_setTextAutosizingWidth:(CGFloat)width
{
auto* frame = core(self);
auto* page = frame->page();
if (!page)
return;
page->setTextAutosizingWidth(width);
}
#else
- (void)resetTextAutosizingBeforeLayout
{
}
- (void)_setVisibleSize:(CGSize)size
{
}
- (void)_setTextAutosizingWidth:(CGFloat)width
{
}
#endif // ENABLE(TEXT_AUTOSIZING)
- (void)_replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle
{
if (_private->coreFrame->selection().isNone() || !fragment)
return;
_private->coreFrame->editor().replaceSelectionWithFragment(*core(fragment), selectReplacement ? WebCore::Editor::SelectReplacement::Yes : WebCore::Editor::SelectReplacement::No, smartReplace ? WebCore::Editor::SmartReplace::Yes : WebCore::Editor::SmartReplace::No, matchStyle ? WebCore::Editor::MatchStyle::Yes : WebCore::Editor::MatchStyle::No);
}
#if PLATFORM(IOS_FAMILY)
- (void)removeUnchangeableStyles
{
_private->coreFrame->editor().removeUnchangeableStyles();
}
- (BOOL)hasRichlyEditableSelection
{
return _private->coreFrame->selection().selection().isContentRichlyEditable();
}
#endif
- (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace
{
RefPtr<WebCore::Range> range = _private->coreFrame->selection().toNormalizedRange();
DOMDocumentFragment* fragment = range ? kit(createFragmentFromText(*range, text).ptr()) : nil;
[self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES];
}
- (void)_replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace
{
DOMDocumentFragment *fragment = [self _documentFragmentWithMarkupString:markupString baseURLString:baseURLString];
[self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:NO];
}
#if !PLATFORM(IOS_FAMILY)
// Determines whether whitespace needs to be added around aString to preserve proper spacing and
// punctuation when it's inserted into the receiver's text over charRange. Returns by reference
// in beforeString and afterString any whitespace that should be added, unless either or both are
// nil. Both are returned as nil if aString is nil or if smart insertion and deletion are disabled.
- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
{
// give back nil pointers in case of early returns
if (beforeString)
*beforeString = nil;
if (afterString)
*afterString = nil;
// inspect destination
WebCore::Node *startContainer = core([rangeToReplace startContainer]);
WebCore::Node *endContainer = core([rangeToReplace endContainer]);
WebCore::Position startPos(startContainer, [rangeToReplace startOffset], WebCore::Position::PositionIsOffsetInAnchor);
WebCore::Position endPos(endContainer, [rangeToReplace endOffset], WebCore::Position::PositionIsOffsetInAnchor);
WebCore::VisiblePosition startVisiblePos = WebCore::VisiblePosition(startPos, WebCore::VP_DEFAULT_AFFINITY);
WebCore::VisiblePosition endVisiblePos = WebCore::VisiblePosition(endPos, WebCore::VP_DEFAULT_AFFINITY);
// this check also ensures startContainer, startPos, endContainer, and endPos are non-null
if (startVisiblePos.isNull() || endVisiblePos.isNull())
return;
bool addLeadingSpace = startPos.leadingWhitespacePosition(WebCore::VP_DEFAULT_AFFINITY, true).isNull() && !isStartOfParagraph(startVisiblePos);
if (addLeadingSpace)
if (UChar previousChar = startVisiblePos.previous().characterAfter())
addLeadingSpace = !WebCore::isCharacterSmartReplaceExempt(previousChar, true);
bool addTrailingSpace = endPos.trailingWhitespacePosition(WebCore::VP_DEFAULT_AFFINITY, true).isNull() && !isEndOfParagraph(endVisiblePos);
if (addTrailingSpace)
if (UChar thisChar = endVisiblePos.characterAfter())
addTrailingSpace = !WebCore::isCharacterSmartReplaceExempt(thisChar, false);
// inspect source
bool hasWhitespaceAtStart = false;
bool hasWhitespaceAtEnd = false;
unsigned pasteLength = [pasteString length];
if (pasteLength > 0) {
NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
if ([whiteSet characterIsMember:[pasteString characterAtIndex:0]]) {
hasWhitespaceAtStart = YES;
}
if ([whiteSet characterIsMember:[pasteString characterAtIndex:(pasteLength - 1)]]) {
hasWhitespaceAtEnd = YES;
}
}
// issue the verdict
if (beforeString && addLeadingSpace && !hasWhitespaceAtStart)
*beforeString = @" ";
if (afterString && addTrailingSpace && !hasWhitespaceAtEnd)
*afterString = @" ";
}
#endif // !PLATFORM(IOS_FAMILY)
- (NSMutableDictionary *)_cacheabilityDictionary
{
NSMutableDictionary *result = [NSMutableDictionary dictionary];
auto& frameLoader = _private->coreFrame->loader();
auto* documentLoader = frameLoader.documentLoader();
if (documentLoader && !documentLoader->mainDocumentError().isNull())
[result setObject:(NSError *)documentLoader->mainDocumentError() forKey:WebFrameMainDocumentError];
if (frameLoader.subframeLoader().containsPlugins())
[result setObject:@YES forKey:WebFrameHasPlugins];
if (WebCore::DOMWindow* domWindow = _private->coreFrame->document()->domWindow()) {
if (domWindow->hasEventListeners(WebCore::eventNames().unloadEvent))
[result setObject:@YES forKey:WebFrameHasUnloadListener];
if (domWindow->optionalApplicationCache())
[result setObject:@YES forKey:WebFrameUsesApplicationCache];
}
if (auto* document = _private->coreFrame->document()) {
if (WebCore::DatabaseManager::singleton().hasOpenDatabases(*document))
[result setObject:@YES forKey:WebFrameUsesDatabases];
}
return result;
}
- (BOOL)_allowsFollowingLink:(NSURL *)URL
{
if (!_private->coreFrame)
return YES;
return _private->coreFrame->document()->securityOrigin().canDisplay(URL);
}
- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string withGlobalObject:(JSObjectRef)globalObjectRef inScriptWorld:(WebScriptWorld *)world
{
if (!string)
return @"";
if (!world)
return @"";
// Start off with some guess at a frame and a global object, we'll try to do better...!
auto* anyWorldGlobalObject = _private->coreFrame->script().globalObject(WebCore::mainThreadNormalWorld());
// The global object is probably a proxy object? - if so, we know how to use this!
JSC::JSObject* globalObjectObj = toJS(globalObjectRef);
JSC::VM& vm = globalObjectObj->vm();
if (!strcmp(globalObjectObj->classInfo(vm)->className, "JSWindowProxy"))
anyWorldGlobalObject = JSC::jsDynamicCast<WebCore::JSDOMWindow*>(vm, static_cast<WebCore::JSWindowProxy*>(globalObjectObj)->window());
if (!anyWorldGlobalObject)
return @"";
// Get the frame frome the global object we've settled on.
auto* frame = anyWorldGlobalObject->wrapped().frame();
ASSERT(frame->document());
RetainPtr<WebFrame> webFrame(kit(frame)); // Running arbitrary JavaScript can destroy the frame.
JSC::JSValue result = frame->script().executeUserAgentScriptInWorldIgnoringException(*core(world), string, true);
if (!webFrame->_private->coreFrame) // In case the script removed our frame from the page.
return @"";
// This bizarre set of rules matches behavior from WebKit for Safari 2.0.
// If you don't like it, use -[WebScriptObject evaluateWebScript:] or
// JSEvaluateScript instead, since they have less surprising semantics.
if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber()))
return @"";
JSC::JSGlobalObject* lexicalGlobalObject = anyWorldGlobalObject;
JSC::JSLockHolder lock(lexicalGlobalObject);
return result.toWTFString(lexicalGlobalObject);
}
- (JSGlobalContextRef)_globalContextForScriptWorld:(WebScriptWorld *)world
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return 0;
auto* coreWorld = core(world);
if (!coreWorld)
return 0;
return toGlobalRef(coreFrame->script().globalObject(*coreWorld));
}
#if JSC_OBJC_API_ENABLED
- (JSContext *)_javaScriptContextForScriptWorld:(WebScriptWorld *)world
{
JSGlobalContextRef globalContextRef = [self _globalContextForScriptWorld:world];
if (!globalContextRef)
return 0;
return [JSContext contextWithJSGlobalContextRef:globalContextRef];
}
#endif
#if !PLATFORM(IOS_FAMILY)
- (void)setAllowsScrollersToOverlapContent:(BOOL)flag
{
ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
[(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAllowsScrollersToOverlapContent:flag];
}
- (void)setAlwaysHideHorizontalScroller:(BOOL)flag
{
ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
[(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideHorizontalScroller:flag];
}
- (void)setAlwaysHideVerticalScroller:(BOOL)flag
{
ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
[(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideVerticalScroller:flag];
}
#endif
- (void)setAccessibleName:(NSString *)name
{
#if ENABLE(ACCESSIBILITY)
if (!WebCore::AXObjectCache::accessibilityEnabled())
return;
if (!_private->coreFrame || !_private->coreFrame->document())
return;
auto* rootObject = _private->coreFrame->document()->axObjectCache()->rootObject();
if (rootObject) {
String strName(name);
rootObject->setAccessibleName(strName);
}
#endif
}
- (BOOL)enhancedAccessibilityEnabled
{
#if ENABLE(ACCESSIBILITY)
return WebCore::AXObjectCache::accessibilityEnhancedUserInterfaceEnabled();
#else
return NO;
#endif
}
- (void)setEnhancedAccessibility:(BOOL)enable
{
#if ENABLE(ACCESSIBILITY)
WebCore::AXObjectCache::setEnhancedUserInterfaceAccessibility(enable);
#endif
}
- (NSString*)_layerTreeAsText
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return @"";
return coreFrame->layerTreeAsText();
}
- (id)accessibilityRoot
{
#if ENABLE(ACCESSIBILITY)
if (!WebCore::AXObjectCache::accessibilityEnabled()) {
WebCore::AXObjectCache::enableAccessibility();
#if !PLATFORM(IOS_FAMILY)
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WebCore::AXObjectCache::setEnhancedUserInterfaceAccessibility([[NSApp accessibilityAttributeValue:NSAccessibilityEnhancedUserInterfaceAttribute] boolValue]);
ALLOW_DEPRECATED_DECLARATIONS_END
#endif
}
if (!_private->coreFrame)
return nil;
auto* document = _private->coreFrame->document();
if (!document || !document->axObjectCache())
return nil;
auto* rootObject = document->axObjectCache()->rootObjectForFrame(_private->coreFrame);
if (!rootObject)
return nil;
// The root object will be a WebCore scroll view object. In WK1, scroll views are handled
// by the system and the root object should be the web area (instead of the scroll view).
if (rootObject->isAttachment() && rootObject->firstChild())
return rootObject->firstChild()->wrapper();
return rootObject->wrapper();
#else
return nil;
#endif
}
- (void)_clearOpener
{
auto coreFrame = _private->coreFrame;
if (coreFrame)
coreFrame->loader().setOpener(0);
}
- (BOOL)hasRichlyEditableDragCaret
{
if (auto* page = core(self)->page())
return page->dragCaretController().isContentRichlyEditable();
return NO;
}
// Used by pagination code called from AppKit when a standalone web page is printed.
- (NSArray *)_computePageRectsWithPrintScaleFactor:(float)printScaleFactor pageSize:(NSSize)pageSize
{
if (printScaleFactor <= 0) {
LOG_ERROR("printScaleFactor has bad value %.2f", printScaleFactor);
return [NSArray array];
}
if (!_private->coreFrame)
return [NSArray array];
if (!_private->coreFrame->document())
return [NSArray array];
if (!_private->coreFrame->view())
return [NSArray array];
if (!_private->coreFrame->view()->documentView())
return [NSArray array];
auto* root = _private->coreFrame->document()->renderView();
if (!root)
return [NSArray array];
const auto& documentRect = root->documentRect();
float printWidth = root->style().isHorizontalWritingMode() ? static_cast<float>(documentRect.width()) / printScaleFactor : pageSize.width;
float printHeight = root->style().isHorizontalWritingMode() ? pageSize.height : static_cast<float>(documentRect.height()) / printScaleFactor;
WebCore::PrintContext printContext(_private->coreFrame);
printContext.computePageRectsWithPageSize(WebCore::FloatSize(printWidth, printHeight), true);
const Vector<WebCore::IntRect>& pageRects = printContext.pageRects();
size_t size = pageRects.size();
NSMutableArray *pages = [NSMutableArray arrayWithCapacity:size];
for (size_t i = 0; i < size; ++i)
[pages addObject:[NSValue valueWithRect:NSRect(pageRects[i])]];
return pages;
}
#if PLATFORM(IOS_FAMILY)
- (DOMDocumentFragment *)_documentFragmentForText:(NSString *)text
{
return kit(createFragmentFromText(*_private->coreFrame->selection().toNormalizedRange().get(), text).ptr());
}
- (DOMDocumentFragment *)_documentFragmentForWebArchive:(WebArchive *)webArchive
{
return [[self dataSource] _documentFragmentWithArchive:webArchive];
}
- (DOMDocumentFragment *)_documentFragmentForImageData:(NSData *)data withRelativeURLPart:(NSString *)relativeURLPart andMIMEType:(NSString *)mimeType
{
WebResource *resource = [[WebResource alloc] initWithData:data
URL:URL::fakeURLWithRelativePart(relativeURLPart)
MIMEType:mimeType
textEncodingName:nil
frameName:nil];
DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
[resource release];
return fragment;
}
- (BOOL)focusedNodeHasContent
{
auto coreFrame = _private->coreFrame;
WebCore::Element* root;
const auto& selection = coreFrame->selection().selection();
if (selection.isNone() || !selection.isContentEditable())
root = coreFrame->document()->bodyOrFrameset();
else {
// Can't use the focusedNode here because we want the root of the shadow tree for form elements.
root = selection.rootEditableElement();
}
// Early return to avoid the expense of creating VisiblePositions.
// FIXME: We fail to compute a root for SVG, we have a null check here so that we don't crash.
if (!root || !root->hasChildNodes())
return NO;
WebCore::VisiblePosition first(createLegacyEditingPosition(root, 0));
WebCore::VisiblePosition last(createLegacyEditingPosition(root, root->countChildNodes()));
return first != last;
}
- (void)_dispatchDidReceiveTitle:(NSString *)title
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return;
coreFrame->loader().client().dispatchDidReceiveTitle({ title, WebCore::TextDirection::LTR });
}
#endif // PLATFORM(IOS_FAMILY)
- (JSValueRef)jsWrapperForNode:(DOMNode *)node inScriptWorld:(WebScriptWorld *)world
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return 0;
if (!world)
return 0;
WebCore::JSDOMWindow* globalObject = coreFrame->script().globalObject(*core(world));
JSC::JSGlobalObject* lexicalGlobalObject = globalObject;
JSC::JSLockHolder lock(lexicalGlobalObject);
return toRef(lexicalGlobalObject, toJS(lexicalGlobalObject, globalObject, core(node)));
}
- (NSDictionary *)elementAtPoint:(NSPoint)point
{
using namespace WebCore;
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return nil;
constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::IgnoreClipping, HitTestRequest::DisallowUserAgentShadowContent, HitTestRequest::AllowChildFrameContent };
return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler().hitTestResultAtPoint(WebCore::IntPoint(point), hitType)] autorelease];
}
- (NSURL *)_unreachableURL
{
return [[self _dataSource] unreachableURL];
}
@end
@implementation WebFrame
- (instancetype)init
{
return nil;
}
// Should be deprecated.
- (instancetype)initWithName:(NSString *)name webFrameView:(WebFrameView *)view webView:(WebView *)webView
{
return nil;
}
- (void)dealloc
{
if (_private && _private->includedInWebKitStatistics)
--WebFrameCount;
[_private release];
[super dealloc];
}
- (NSString *)name
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return nil;
return coreFrame->tree().uniqueName();
}
- (WebFrameView *)frameView
{
return _private->webFrameView;
}
- (WebView *)webView
{
return getWebView(self);
}
static bool needsMicrosoftMessengerDOMDocumentWorkaround()
{
#if PLATFORM(IOS_FAMILY)
return false;
#else
static bool needsWorkaround = WebCore::MacApplication::isMicrosoftMessenger() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] compare:@"7.1" options:NSNumericSearch] == NSOrderedAscending;
return needsWorkaround;
#endif
}
- (DOMDocument *)DOMDocument
{
if (needsMicrosoftMessengerDOMDocumentWorkaround() && !pthread_main_np())
return nil;
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return nil;
// FIXME: <rdar://problem/5145841> When loading a custom view/representation
// into a web frame, the old document can still be around. This makes sure that
// we'll return nil in those cases.
if (![[self _dataSource] _isDocumentHTML])
return nil;
auto* document = coreFrame->document();
// According to the documentation, we should return nil if the frame doesn't have a document.
// While full-frame images and plugins do have an underlying HTML document, we return nil here to be
// backwards compatible.
if (document && (document->isPluginDocument() || document->isImageDocument()))
return nil;
return kit(coreFrame->document());
}
- (DOMHTMLElement *)frameElement
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return nil;
return kit(coreFrame->ownerElement());
}
- (WebDataSource *)provisionalDataSource
{
auto coreFrame = _private->coreFrame;
return coreFrame ? dataSource(coreFrame->loader().provisionalDocumentLoader()) : nil;
}
- (WebDataSource *)dataSource
{
auto coreFrame = _private->coreFrame;
return coreFrame && coreFrame->loader().frameHasLoaded() ? [self _dataSource] : nil;
}
- (void)loadRequest:(NSURLRequest *)request
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return;
WebCore::ResourceRequest resourceRequest(request);
// Some users of WebKit API incorrectly use "file path as URL" style requests which are invalid.
// By re-writing those URLs here we technically break the -[WebDataSource initialRequest] API
// but that is necessary to implement this quirk only at the API boundary.
// Note that other users of WebKit API use nil requests or requests with nil URLs or empty URLs, so we
// only implement this workaround when the request had a non-nil or non-empty URL.
if (!resourceRequest.url().isValid() && !resourceRequest.url().isEmpty())
resourceRequest.setURL([NSURL URLWithString:[@"file:" stringByAppendingString:[[request URL] absoluteString]]]);
coreFrame->loader().load(WebCore::FrameLoadRequest(*coreFrame, resourceRequest, WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow));
}
static NSURL *createUniqueWebDataURL()
{
CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
CFRelease(UUIDRef);
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"applewebdata://%@", UUIDString]];
CFRelease(UUIDString);
return URL;
}
- (void)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL
{
#if PLATFORM(MAC)
if (!pthread_main_np())
return [[self _webkit_invokeOnMainThread] _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:unreachableURL];
#endif
NSURL *responseURL = nil;
if (baseURL)
baseURL = [baseURL absoluteURL];
else {
baseURL = aboutBlankURL();
responseURL = createUniqueWebDataURL();
}
#if USE(QUICK_LOOK)
if (WebCore::shouldUseQuickLookForMIMEType(MIMEType)) {
NSURL *quickLookURL = responseURL ? responseURL : baseURL;
if (auto request = WebCore::registerQLPreviewConverterIfNeeded(quickLookURL, MIMEType, data)) {
_private->coreFrame->loader().load(WebCore::FrameLoadRequest(*_private->coreFrame, request.get(), WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow));
return;
}
}
#endif
WebCore::ResourceRequest request(baseURL);
WebCore::ResourceResponse response(responseURL, MIMEType, [data length], encodingName);
WebCore::SubstituteData substituteData(WebCore::SharedBuffer::create(data), [unreachableURL absoluteURL], response, WebCore::SubstituteData::SessionHistoryVisibility::Hidden);
_private->coreFrame->loader().load(WebCore::FrameLoadRequest(*_private->coreFrame, request, WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow, substituteData));
}
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
{
WebCoreThreadViolationCheckRoundTwo();
if (!MIMEType)
MIMEType = @"text/html";
[self _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:[baseURL _webkit_URLFromURLOrSchemelessFileURL] unreachableURL:nil];
}
- (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[self _loadData:data MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:baseURL unreachableURL:unreachableURL];
}
- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
{
WebCoreThreadViolationCheckRoundTwo();
[self _loadHTMLString:string baseURL:[baseURL _webkit_URLFromURLOrSchemelessFileURL] unreachableURL:nil];
}
- (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
{
WebCoreThreadViolationCheckRoundTwo();
[self _loadHTMLString:string baseURL:[baseURL _webkit_URLFromURLOrSchemelessFileURL] unreachableURL:[unreachableURL _webkit_URLFromURLOrSchemelessFileURL]];
}
- (void)loadArchive:(WebArchive *)archive
{
if (auto* coreArchive = [archive _coreLegacyWebArchive])
_private->coreFrame->loader().loadArchive(*coreArchive);
}
- (void)stopLoading
{
if (!_private->coreFrame)
return;
_private->coreFrame->loader().stopForUserCancel();
}
- (void)reload
{
_private->coreFrame->loader().reload({ });
}
- (void)reloadFromOrigin
{
_private->coreFrame->loader().reload(WebCore::ReloadOption::FromOrigin);
}
- (WebFrame *)findFrameNamed:(NSString *)name
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return nil;
return kit(coreFrame->tree().find(name, *coreFrame));
}
- (WebFrame *)parentFrame
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return nil;
return [[kit(coreFrame->tree().parent()) retain] autorelease];
}
- (NSArray *)childFrames
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return [NSArray array];
NSMutableArray *children = [NSMutableArray arrayWithCapacity:coreFrame->tree().childCount()];
for (WebCore::Frame* child = coreFrame->tree().firstChild(); child; child = child->tree().nextSibling())
[children addObject:kit(child)];
return children;
}
- (WebScriptObject *)windowObject
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return 0;
return coreFrame->script().windowScriptObject();
}
- (JSGlobalContextRef)globalContext
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return 0;
return toGlobalRef(coreFrame->script().globalObject(WebCore::mainThreadNormalWorld()));
}
#if JSC_OBJC_API_ENABLED
- (JSContext *)javaScriptContext
{
auto coreFrame = _private->coreFrame;
if (!coreFrame)
return 0;
return coreFrame->script().javaScriptContext();
}
#endif
@end