blob: 1802ca89c4b930683df5a32306903fa60a013cca [file] [log] [blame]
/*
* Copyright (C) 2005, 2006, 2007 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 Computer, 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 "WebHTMLRepresentation.h"
#import "DOMElementInternal.h"
#import "DOMNodeInternal.h"
#import "DOMRangeInternal.h"
#import "WebArchive.h"
#import "WebBasePluginPackage.h"
#import "WebDataSourceInternal.h"
#import "WebDocumentPrivate.h"
#import "WebFrameInternal.h"
#import "WebKitNSStringExtras.h"
#import "WebKitStatisticsPrivate.h"
#import "WebNSObjectExtras.h"
#import "WebTypesInternal.h"
#import "WebView.h"
#import <Foundation/NSURLResponse.h>
#import <WebCore/Document.h>
#import <WebCore/DocumentLoader.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameLoader.h>
#import <WebCore/FrameLoaderClient.h>
#import <WebCore/HTMLConverter.h>
#import <WebCore/HTMLFormControlElement.h>
#import <WebCore/HTMLFormElement.h>
#import <WebCore/HTMLInputElement.h>
#import <WebCore/HTMLNames.h>
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/Range.h>
#import <WebCore/TextResourceDecoder.h>
#import <WebKit/DOMHTMLInputElement.h>
#import <wtf/Assertions.h>
#import <wtf/StdLibExtras.h>
using namespace WebCore;
using namespace HTMLNames;
@interface WebHTMLRepresentationPrivate : NSObject {
@public
WebDataSource *dataSource;
BOOL hasSentResponseToPlugin;
BOOL includedInWebKitStatistics;
id <WebPluginManualLoader> manualLoader;
NSView *pluginView;
}
@end
@implementation WebHTMLRepresentationPrivate
@end
@implementation WebHTMLRepresentation
static NSArray *stringArray(const HashSet<String>& set)
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:set.size()];
HashSet<String>::const_iterator end = set.end();
for (HashSet<String>::const_iterator it = set.begin(); it != end; ++it)
[array addObject:(NSString *)(*it)];
return array;
}
static NSArray *concatenateArrays(NSArray *first, NSArray *second)
{
NSMutableArray *result = [[first mutableCopy] autorelease];
[result addObjectsFromArray:second];
return result;
}
+ (NSArray *)supportedMIMETypes
{
DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedMIMETypes, (concatenateArrays([self supportedNonImageMIMETypes], [self supportedImageMIMETypes])));
return staticSupportedMIMETypes.get();
}
+ (NSArray *)supportedNonImageMIMETypes
{
DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedNonImageMIMETypes, (stringArray(MIMETypeRegistry::getSupportedNonImageMIMETypes())));
return staticSupportedNonImageMIMETypes.get();
}
+ (NSArray *)supportedImageMIMETypes
{
DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedImageMIMETypes, (stringArray(MIMETypeRegistry::getSupportedImageMIMETypes())));
return staticSupportedImageMIMETypes.get();
}
+ (NSArray *)unsupportedTextMIMETypes
{
DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticUnsupportedTextMIMETypes, (stringArray(MIMETypeRegistry::getUnsupportedTextMIMETypes())));
return staticUnsupportedTextMIMETypes.get();
}
- (id)init
{
self = [super init];
if (!self)
return nil;
_private = [[WebHTMLRepresentationPrivate alloc] init];
return self;
}
- (void)dealloc
{
if (_private && _private->includedInWebKitStatistics)
--WebHTMLRepresentationCount;
[_private release];
[super dealloc];
}
- (void)finalize
{
if (_private && _private->includedInWebKitStatistics)
--WebHTMLRepresentationCount;
[super finalize];
}
- (void)_redirectDataToManualLoader:(id<WebPluginManualLoader>)manualLoader forPluginView:(NSView *)pluginView
{
_private->manualLoader = manualLoader;
_private->pluginView = pluginView;
}
- (void)setDataSource:(WebDataSource *)dataSource
{
_private->dataSource = dataSource;
if (!_private->includedInWebKitStatistics && [[dataSource webFrame] _isIncludedInWebKitStatistics]) {
_private->includedInWebKitStatistics = YES;
++WebHTMLRepresentationCount;
}
}
- (BOOL)_isDisplayingWebArchive
{
return [[_private->dataSource _responseMIMEType] _webkit_isCaseInsensitiveEqualToString:@"application/x-webarchive"];
}
- (void)receivedData:(NSData *)data withDataSource:(WebDataSource *)dataSource
{
WebFrame *webFrame = [dataSource webFrame];
if (!webFrame)
return;
if (!_private->pluginView)
[webFrame _commitData:data];
// If the document is a stand-alone media document, now is the right time to cancel the WebKit load
Frame* coreFrame = core(webFrame);
if (coreFrame->document()->isMediaDocument())
coreFrame->loader()->documentLoader()->cancelMainResourceLoad(coreFrame->loader()->client()->pluginWillHandleLoadError(coreFrame->loader()->documentLoader()->response()));
if (_private->pluginView) {
if (!_private->hasSentResponseToPlugin) {
[_private->manualLoader pluginView:_private->pluginView receivedResponse:[dataSource response]];
_private->hasSentResponseToPlugin = YES;
}
[_private->manualLoader pluginView:_private->pluginView receivedData:data];
}
}
- (void)receivedError:(NSError *)error withDataSource:(WebDataSource *)dataSource
{
if (_private->pluginView) {
[_private->manualLoader pluginView:_private->pluginView receivedError:error];
}
}
- (void)finishedLoadingWithDataSource:(WebDataSource *)dataSource
{
WebFrame* webFrame = [dataSource webFrame];
if (_private->pluginView) {
[_private->manualLoader pluginViewFinishedLoading:_private->pluginView];
return;
}
if (!webFrame)
return;
if (![self _isDisplayingWebArchive]) {
// Telling the frame we received some data and passing nil as the data is our
// way to get work done that is normally done when the first bit of data is
// received, even for the case of a document with no data (like about:blank).
[webFrame _commitData:nil];
}
WebView *webView = [webFrame webView];
if ([webView isEditable])
core(webFrame)->editor()->applyEditingStyleToBodyElement();
}
- (BOOL)canProvideDocumentSource
{
return [[_private->dataSource webFrame] _canProvideDocumentSource];
}
- (BOOL)canSaveAsWebArchive
{
return [[_private->dataSource webFrame] _canSaveAsWebArchive];
}
- (NSString *)documentSource
{
if ([self _isDisplayingWebArchive]) {
SharedBuffer *parsedArchiveData = [_private->dataSource _documentLoader]->parsedArchiveData();
NSData *nsData = parsedArchiveData ? parsedArchiveData->createNSData() : nil;
NSString *result = [[NSString alloc] initWithData:nsData encoding:NSUTF8StringEncoding];
[nsData release];
return [result autorelease];
}
Frame* coreFrame = core([_private->dataSource webFrame]);
if (!coreFrame)
return nil;
Document* document = coreFrame->document();
if (!document)
return nil;
TextResourceDecoder* decoder = document->decoder();
if (!decoder)
return nil;
NSData *data = [_private->dataSource data];
if (!data)
return nil;
return decoder->encoding().decode(reinterpret_cast<const char*>([data bytes]), [data length]);
}
- (NSString *)title
{
return nsStringNilIfEmpty([_private->dataSource _documentLoader]->title().string());
}
- (DOMDocument *)DOMDocument
{
return [[_private->dataSource webFrame] DOMDocument];
}
- (NSAttributedString *)attributedText
{
// FIXME: Implement
return nil;
}
- (NSAttributedString *)attributedStringFrom:(DOMNode *)startNode startOffset:(int)startOffset to:(DOMNode *)endNode endOffset:(int)endOffset
{
return [WebHTMLConverter editingAttributedStringFromRange:Range::create(core(startNode)->document(), core(startNode), startOffset, core(endNode), endOffset).get()];
}
static HTMLFormElement* formElementFromDOMElement(DOMElement *element)
{
Element* node = core(element);
return node && node->hasTagName(formTag) ? static_cast<HTMLFormElement*>(node) : 0;
}
- (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form
{
HTMLFormElement* formElement = formElementFromDOMElement(form);
if (!formElement)
return nil;
const Vector<FormAssociatedElement*>& elements = formElement->associatedElements();
AtomicString targetName = name;
for (unsigned i = 0; i < elements.size(); i++) {
FormAssociatedElement* elt = elements[i];
if (elt->name() == targetName)
return kit(toHTMLElement(elt));
}
return nil;
}
static HTMLInputElement* inputElementFromDOMElement(DOMElement* element)
{
Element* node = core(element);
return node && node->hasTagName(inputTag) ? static_cast<HTMLInputElement*>(node) : 0;
}
- (BOOL)elementDoesAutoComplete:(DOMElement *)element
{
HTMLInputElement* inputElement = inputElementFromDOMElement(element);
return inputElement
&& inputElement->isTextField()
&& !inputElement->isPasswordField()
&& inputElement->autoComplete();
}
- (BOOL)elementIsPassword:(DOMElement *)element
{
HTMLInputElement* inputElement = inputElementFromDOMElement(element);
return inputElement && inputElement->isPasswordField();
}
- (DOMElement *)formForElement:(DOMElement *)element
{
HTMLInputElement* inputElement = inputElementFromDOMElement(element);
return inputElement ? kit(inputElement->form()) : 0;
}
- (DOMElement *)currentForm
{
return kit(core([_private->dataSource webFrame])->selection()->currentForm());
}
- (NSArray *)controlsInForm:(DOMElement *)form
{
HTMLFormElement* formElement = formElementFromDOMElement(form);
if (!formElement)
return nil;
NSMutableArray *results = nil;
const Vector<FormAssociatedElement*>& elements = formElement->associatedElements();
for (unsigned i = 0; i < elements.size(); i++) {
if (elements[i]->isEnumeratable()) { // Skip option elements, other duds
DOMElement* de = kit(toHTMLElement(elements[i]));
if (!results)
results = [NSMutableArray arrayWithObject:de];
else
[results addObject:de];
}
}
return results;
}
- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element
{
return [self searchForLabels:labels beforeElement:element resultDistance:0 resultIsInCellAbove:0];
}
- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element resultDistance:(NSUInteger*)outDistance resultIsInCellAbove:(BOOL*)outIsInCellAbove
{
size_t distance;
bool isInCellAbove;
NSString *result = core([_private->dataSource webFrame])->searchForLabelsBeforeElement(labels, core(element), &distance, &isInCellAbove);
if (outDistance) {
if (distance == notFound)
*outDistance = NSNotFound;
else
*outDistance = distance;
}
if (outIsInCellAbove)
*outIsInCellAbove = isInCellAbove;
return result;
}
- (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element
{
return core([_private->dataSource webFrame])->matchLabelsAgainstElement(labels, core(element));
}
@end