Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #import "WebHTMLRepresentation.h"
     30 
     31 #import "DOMElementInternal.h"
     32 #import "DOMRangeInternal.h"
     33 #import "WebArchive.h"
     34 #import "WebBasePluginPackage.h"
     35 #import "WebDataSourceInternal.h"
     36 #import "WebDocumentPrivate.h"
     37 #import "WebFrameInternal.h"
     38 #import "WebKitNSStringExtras.h"
     39 #import "WebKitStatisticsPrivate.h"
     40 #import "WebNSAttributedStringExtras.h"
     41 #import "WebNSObjectExtras.h"
     42 #import "WebTypesInternal.h"
     43 #import "WebView.h"
     44 #import <Foundation/NSURLResponse.h>
     45 #import <WebCore/Document.h>
     46 #import <WebCore/DocumentLoader.h>
     47 #import <WebCore/Frame.h>
     48 #import <WebCore/FrameLoader.h>
     49 #import <WebCore/FrameLoaderClient.h>
     50 #import <WebCore/HTMLFormControlElement.h>
     51 #import <WebCore/HTMLFormElement.h>
     52 #import <WebCore/HTMLInputElement.h>
     53 #import <WebCore/HTMLNames.h>
     54 #import <WebCore/MIMETypeRegistry.h>
     55 #import <WebCore/Range.h>
     56 #import <WebCore/TextResourceDecoder.h>
     57 #import <WebKit/DOMHTMLInputElement.h>
     58 #import <wtf/Assertions.h>
     59 #import <wtf/StdLibExtras.h>
     60 
     61 using namespace WebCore;
     62 using namespace HTMLNames;
     63 
     64 @interface WebHTMLRepresentationPrivate : NSObject {
     65 @public
     66     WebDataSource *dataSource;
     67 
     68     BOOL hasSentResponseToPlugin;
     69     id <WebPluginManualLoader> manualLoader;
     70     NSView *pluginView;
     71 }
     72 @end
     73 
     74 @implementation WebHTMLRepresentationPrivate
     75 @end
     76 
     77 @implementation WebHTMLRepresentation
     78 
     79 static NSArray *stringArray(const HashSet<String>& set)
     80 {
     81     NSMutableArray *array = [NSMutableArray arrayWithCapacity:set.size()];
     82     HashSet<String>::const_iterator end = set.end();
     83     for (HashSet<String>::const_iterator it = set.begin(); it != end; ++it)
     84         [array addObject:(NSString *)(*it)];
     85     return array;
     86 }
     87 
     88 static NSArray *concatenateArrays(NSArray *first, NSArray *second)
     89 {
     90     NSMutableArray *result = [[first mutableCopy] autorelease];
     91     [result addObjectsFromArray:second];
     92     return result;
     93 }
     94 
     95 + (NSArray *)supportedMIMETypes
     96 {
     97     DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedMIMETypes, (concatenateArrays([self supportedNonImageMIMETypes], [self supportedImageMIMETypes])));
     98     return staticSupportedMIMETypes.get();
     99 }
    100 
    101 + (NSArray *)supportedNonImageMIMETypes
    102 {
    103     DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedNonImageMIMETypes, (stringArray(MIMETypeRegistry::getSupportedNonImageMIMETypes())));
    104     return staticSupportedNonImageMIMETypes.get();
    105 }
    106 
    107 + (NSArray *)supportedImageMIMETypes
    108 {
    109     DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedImageMIMETypes, (stringArray(MIMETypeRegistry::getSupportedImageMIMETypes())));
    110     return staticSupportedImageMIMETypes.get();
    111 }
    112 
    113 - init
    114 {
    115     self = [super init];
    116     if (!self)
    117         return nil;
    118 
    119     _private = [[WebHTMLRepresentationPrivate alloc] init];
    120 
    121     ++WebHTMLRepresentationCount;
    122 
    123     return self;
    124 }
    125 
    126 - (void)dealloc
    127 {
    128     --WebHTMLRepresentationCount;
    129 
    130     [_private release];
    131 
    132     [super dealloc];
    133 }
    134 
    135 - (void)finalize
    136 {
    137     --WebHTMLRepresentationCount;
    138 
    139     [super finalize];
    140 }
    141 
    142 - (void)_redirectDataToManualLoader:(id<WebPluginManualLoader>)manualLoader forPluginView:(NSView *)pluginView
    143 {
    144     _private->manualLoader = manualLoader;
    145     _private->pluginView = pluginView;
    146 }
    147 
    148 - (void)setDataSource:(WebDataSource *)dataSource
    149 {
    150     _private->dataSource = dataSource;
    151 }
    152 
    153 - (BOOL)_isDisplayingWebArchive
    154 {
    155     return [[_private->dataSource _responseMIMEType] _webkit_isCaseInsensitiveEqualToString:@"application/x-webarchive"];
    156 }
    157 
    158 - (void)receivedData:(NSData *)data withDataSource:(WebDataSource *)dataSource
    159 {
    160     WebFrame *webFrame = [dataSource webFrame];
    161     if (webFrame) {
    162         if (!_private->pluginView)
    163             [webFrame _receivedData:data textEncodingName:[[_private->dataSource response] textEncodingName]];
    164 
    165         // If the document is a stand-alone media document, now is the right time to cancel the WebKit load
    166         Frame* coreFrame = core(webFrame);
    167         if (coreFrame->document() && coreFrame->document()->isMediaDocument())
    168             coreFrame->loader()->documentLoader()->cancelMainResourceLoad(coreFrame->loader()->client()->pluginWillHandleLoadError(coreFrame->loader()->documentLoader()->response()));
    169 
    170         if (_private->pluginView) {
    171             if (!_private->hasSentResponseToPlugin) {
    172                 [_private->manualLoader pluginView:_private->pluginView receivedResponse:[dataSource response]];
    173                 _private->hasSentResponseToPlugin = YES;
    174             }
    175 
    176             [_private->manualLoader pluginView:_private->pluginView receivedData:data];
    177         }
    178     }
    179 }
    180 
    181 - (void)receivedError:(NSError *)error withDataSource:(WebDataSource *)dataSource
    182 {
    183     if (_private->pluginView) {
    184         [_private->manualLoader pluginView:_private->pluginView receivedError:error];
    185     }
    186 }
    187 
    188 - (void)finishedLoadingWithDataSource:(WebDataSource *)dataSource
    189 {
    190     WebFrame *frame = [dataSource webFrame];
    191 
    192     if (_private->pluginView) {
    193         [_private->manualLoader pluginViewFinishedLoading:_private->pluginView];
    194         return;
    195     }
    196 
    197     if (frame) {
    198         if (![self _isDisplayingWebArchive]) {
    199             // Telling the frame we received some data and passing nil as the data is our
    200             // way to get work done that is normally done when the first bit of data is
    201             // received, even for the case of a document with no data (like about:blank).
    202             [frame _receivedData:nil textEncodingName:[[_private->dataSource response] textEncodingName]];
    203         }
    204 
    205         WebView *webView = [frame webView];
    206         if ([webView isEditable])
    207             core(frame)->applyEditingStyleToBodyElement();
    208     }
    209 }
    210 
    211 - (BOOL)canProvideDocumentSource
    212 {
    213     return [[_private->dataSource webFrame] _canProvideDocumentSource];
    214 }
    215 
    216 - (BOOL)canSaveAsWebArchive
    217 {
    218     return [[_private->dataSource webFrame] _canSaveAsWebArchive];
    219 }
    220 
    221 - (NSString *)documentSource
    222 {
    223     if ([self _isDisplayingWebArchive]) {
    224         SharedBuffer *parsedArchiveData = [_private->dataSource _documentLoader]->parsedArchiveData();
    225         NSData *nsData = parsedArchiveData ? parsedArchiveData->createNSData() : nil;
    226         NSString *result = [[NSString alloc] initWithData:nsData encoding:NSUTF8StringEncoding];
    227         [nsData release];
    228         return [result autorelease];
    229     }
    230 
    231     Frame* coreFrame = core([_private->dataSource webFrame]);
    232     if (!coreFrame)
    233         return nil;
    234     Document* document = coreFrame->document();
    235     if (!document)
    236         return nil;
    237     TextResourceDecoder* decoder = document->decoder();
    238     if (!decoder)
    239         return nil;
    240     NSData *data = [_private->dataSource data];
    241     if (!data)
    242         return nil;
    243     return decoder->encoding().decode(reinterpret_cast<const char*>([data bytes]), [data length]);
    244 }
    245 
    246 - (NSString *)title
    247 {
    248     return nsStringNilIfEmpty([_private->dataSource _documentLoader]->title());
    249 }
    250 
    251 - (DOMDocument *)DOMDocument
    252 {
    253     return [[_private->dataSource webFrame] DOMDocument];
    254 }
    255 
    256 - (NSAttributedString *)attributedText
    257 {
    258     // FIXME: Implement
    259     return nil;
    260 }
    261 
    262 - (NSAttributedString *)attributedStringFrom:(DOMNode *)startNode startOffset:(int)startOffset to:(DOMNode *)endNode endOffset:(int)endOffset
    263 {
    264     return [NSAttributedString _web_attributedStringFromRange:Range::create(core(startNode)->document(), core(startNode), startOffset, core(endNode), endOffset).get()];
    265 }
    266 
    267 static HTMLFormElement* formElementFromDOMElement(DOMElement *element)
    268 {
    269     Element* node = core(element);
    270     return node && node->hasTagName(formTag) ? static_cast<HTMLFormElement*>(node) : 0;
    271 }
    272 
    273 - (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form
    274 {
    275     HTMLFormElement* formElement = formElementFromDOMElement(form);
    276     if (!formElement)
    277         return nil;
    278     Vector<HTMLFormControlElement*>& elements = formElement->formElements;
    279     AtomicString targetName = name;
    280     for (unsigned i = 0; i < elements.size(); i++) {
    281         HTMLFormControlElement* elt = elements[i];
    282         if (elt->formControlName() == targetName)
    283             return kit(elt);
    284     }
    285     return nil;
    286 }
    287 
    288 static HTMLInputElement* inputElementFromDOMElement(DOMElement* element)
    289 {
    290     Element* node = core(element);
    291     return node && node->hasTagName(inputTag) ? static_cast<HTMLInputElement*>(node) : 0;
    292 }
    293 
    294 - (BOOL)elementDoesAutoComplete:(DOMElement *)element
    295 {
    296     HTMLInputElement* inputElement = inputElementFromDOMElement(element);
    297     return inputElement
    298         && inputElement->inputType() == HTMLInputElement::TEXT
    299         && inputElement->autoComplete();
    300 }
    301 
    302 - (BOOL)elementIsPassword:(DOMElement *)element
    303 {
    304     HTMLInputElement* inputElement = inputElementFromDOMElement(element);
    305     return inputElement
    306         && inputElement->inputType() == HTMLInputElement::PASSWORD;
    307 }
    308 
    309 - (DOMElement *)formForElement:(DOMElement *)element
    310 {
    311     HTMLInputElement* inputElement = inputElementFromDOMElement(element);
    312     return inputElement ? kit(inputElement->form()) : 0;
    313 }
    314 
    315 - (DOMElement *)currentForm
    316 {
    317     return kit(core([_private->dataSource webFrame])->currentForm());
    318 }
    319 
    320 - (NSArray *)controlsInForm:(DOMElement *)form
    321 {
    322     HTMLFormElement* formElement = formElementFromDOMElement(form);
    323     if (!formElement)
    324         return nil;
    325     NSMutableArray *results = nil;
    326     Vector<HTMLFormControlElement*>& elements = formElement->formElements;
    327     for (unsigned i = 0; i < elements.size(); i++) {
    328         if (elements[i]->isEnumeratable()) { // Skip option elements, other duds
    329             DOMElement* de = kit(elements[i]);
    330             if (!results)
    331                 results = [NSMutableArray arrayWithObject:de];
    332             else
    333                 [results addObject:de];
    334         }
    335     }
    336     return results;
    337 }
    338 
    339 - (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element
    340 {
    341     return [self searchForLabels:labels beforeElement:element resultDistance:0 resultIsInCellAbove:0];
    342 }
    343 
    344 - (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element resultDistance:(NSUInteger*)outDistance resultIsInCellAbove:(BOOL*)outIsInCellAbove
    345 {
    346     size_t distance;
    347     bool isInCellAbove;
    348 
    349     NSString *result = core([_private->dataSource webFrame])->searchForLabelsBeforeElement(labels, core(element), &distance, &isInCellAbove);
    350 
    351     if (outDistance) {
    352         if (distance == notFound)
    353             *outDistance = NSNotFound;
    354         else
    355             *outDistance = distance;
    356     }
    357 
    358     if (outIsInCellAbove)
    359         *outIsInCellAbove = isInCellAbove;
    360 
    361     return result;
    362 }
    363 
    364 - (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element
    365 {
    366     return core([_private->dataSource webFrame])->matchLabelsAgainstElement(labels, core(element));
    367 }
    368 
    369 @end
    370