Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008 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 "WebResourceInternal.h"
     30 
     31 #import "WebFrameInternal.h"
     32 #import "WebKitLogging.h"
     33 #import "WebKitVersionChecks.h"
     34 #import "WebNSDictionaryExtras.h"
     35 #import "WebNSObjectExtras.h"
     36 #import "WebNSURLExtras.h"
     37 #import <JavaScriptCore/InitializeThreading.h>
     38 #import <JavaScriptCore/PassRefPtr.h>
     39 #import <WebCore/ArchiveResource.h>
     40 #import <WebCore/LegacyWebArchive.h>
     41 #import <WebCore/RuntimeApplicationChecks.h>
     42 #import <WebCore/TextEncoding.h>
     43 #import <WebCore/ThreadCheck.h>
     44 #import <WebCore/WebCoreObjCExtras.h>
     45 #import <WebCore/WebCoreURLResponse.h>
     46 #import <wtf/Threading.h>
     47 
     48 using namespace WebCore;
     49 
     50 static NSString * const WebResourceDataKey =              @"WebResourceData";
     51 static NSString * const WebResourceFrameNameKey =         @"WebResourceFrameName";
     52 static NSString * const WebResourceMIMETypeKey =          @"WebResourceMIMEType";
     53 static NSString * const WebResourceURLKey =               @"WebResourceURL";
     54 static NSString * const WebResourceTextEncodingNameKey =  @"WebResourceTextEncodingName";
     55 static NSString * const WebResourceResponseKey =          @"WebResourceResponse";
     56 
     57 @interface WebResourcePrivate : NSObject {
     58 @public
     59     ArchiveResource* coreResource;
     60 }
     61 - (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource;
     62 @end
     63 
     64 @implementation WebResourcePrivate
     65 
     66 + (void)initialize
     67 {
     68     JSC::initializeThreading();
     69     WTF::initializeMainThreadToProcessMainThread();
     70 #ifndef BUILDING_ON_TIGER
     71     WebCoreObjCFinalizeOnMainThread(self);
     72 #endif
     73 }
     74 
     75 - (id)init
     76 {
     77     return [super init];
     78 }
     79 
     80 - (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)passedResource
     81 {
     82     self = [super init];
     83     if (!self)
     84         return nil;
     85     // Acquire the PassRefPtr<>'s ref as our own manual ref
     86     coreResource = passedResource.releaseRef();
     87     return self;
     88 }
     89 
     90 - (void)dealloc
     91 {
     92     if (WebCoreObjCScheduleDeallocateOnMainThread([WebResourcePrivate class], self))
     93         return;
     94 
     95     if (coreResource)
     96         coreResource->deref();
     97     [super dealloc];
     98 }
     99 
    100 - (void)finalize
    101 {
    102     if (coreResource)
    103         coreResource->deref();
    104     [super finalize];
    105 }
    106 
    107 @end
    108 
    109 @implementation WebResource
    110 
    111 - (id)init
    112 {
    113     self = [super init];
    114     if (!self)
    115         return nil;
    116     _private = [[WebResourcePrivate alloc] init];
    117     return self;
    118 }
    119 
    120 - (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName
    121 {
    122     return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:nil copyData:YES];
    123 }
    124 
    125 - (id)initWithCoder:(NSCoder *)decoder
    126 {
    127     WebCoreThreadViolationCheckRoundTwo();
    128 
    129     self = [super init];
    130     if (!self)
    131         return nil;
    132 
    133     NSData *data = nil;
    134     NSURL *url = nil;
    135     NSString *mimeType = nil, *textEncoding = nil, *frameName = nil;
    136     NSURLResponse *response = nil;
    137 
    138     @try {
    139         id object = [decoder decodeObjectForKey:WebResourceDataKey];
    140         if ([object isKindOfClass:[NSData class]])
    141             data = object;
    142         object = [decoder decodeObjectForKey:WebResourceURLKey];
    143         if ([object isKindOfClass:[NSURL class]])
    144             url = object;
    145         object = [decoder decodeObjectForKey:WebResourceMIMETypeKey];
    146         if ([object isKindOfClass:[NSString class]])
    147             mimeType = object;
    148         object = [decoder decodeObjectForKey:WebResourceTextEncodingNameKey];
    149         if ([object isKindOfClass:[NSString class]])
    150             textEncoding = object;
    151         object = [decoder decodeObjectForKey:WebResourceFrameNameKey];
    152         if ([object isKindOfClass:[NSString class]])
    153             frameName = object;
    154         object = [decoder decodeObjectForKey:WebResourceResponseKey];
    155         if ([object isKindOfClass:[NSURLResponse class]])
    156             response = object;
    157     } @catch(id) {
    158         [self release];
    159         return nil;
    160     }
    161 
    162     _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(data), url, mimeType, textEncoding, frameName, response)];
    163 
    164     return self;
    165 }
    166 
    167 - (void)encodeWithCoder:(NSCoder *)encoder
    168 {
    169     ArchiveResource *resource = _private->coreResource;
    170 
    171     NSData *data = nil;
    172     NSURL *url = nil;
    173     NSString *mimeType = nil, *textEncoding = nil, *frameName = nil;
    174     NSURLResponse *response = nil;
    175 
    176     if (resource) {
    177         if (resource->data())
    178             data = [resource->data()->createNSData() autorelease];
    179         url = resource->url();
    180         mimeType = resource->mimeType();
    181         textEncoding = resource->textEncoding();
    182         frameName = resource->frameName();
    183         response = resource->response().nsURLResponse();
    184     }
    185     [encoder encodeObject:data forKey:WebResourceDataKey];
    186     [encoder encodeObject:url forKey:WebResourceURLKey];
    187     [encoder encodeObject:mimeType forKey:WebResourceMIMETypeKey];
    188     [encoder encodeObject:textEncoding forKey:WebResourceTextEncodingNameKey];
    189     [encoder encodeObject:frameName forKey:WebResourceFrameNameKey];
    190     [encoder encodeObject:response forKey:WebResourceResponseKey];
    191 }
    192 
    193 - (void)dealloc
    194 {
    195     [_private release];
    196     [super dealloc];
    197 }
    198 
    199 - (id)copyWithZone:(NSZone *)zone
    200 {
    201     return [self retain];
    202 }
    203 
    204 - (NSData *)data
    205 {
    206 #ifdef MAIL_THREAD_WORKAROUND
    207     if (needMailThreadWorkaround())
    208         return [[self _webkit_invokeOnMainThread] data];
    209 #endif
    210 
    211     WebCoreThreadViolationCheckRoundTwo();
    212 
    213     if (!_private->coreResource)
    214         return nil;
    215     if (!_private->coreResource->data())
    216         return nil;
    217     return [_private->coreResource->data()->createNSData() autorelease];
    218 }
    219 
    220 - (NSURL *)URL
    221 {
    222 #ifdef MAIL_THREAD_WORKAROUND
    223     if (needMailThreadWorkaround())
    224         return [[self _webkit_invokeOnMainThread] URL];
    225 #endif
    226 
    227     WebCoreThreadViolationCheckRoundTwo();
    228 
    229     if (!_private->coreResource)
    230         return nil;
    231     NSURL *url = _private->coreResource->url();
    232     return url;
    233 }
    234 
    235 - (NSString *)MIMEType
    236 {
    237 #ifdef MAIL_THREAD_WORKAROUND
    238     if (needMailThreadWorkaround())
    239         return [[self _webkit_invokeOnMainThread] MIMEType];
    240 #endif
    241 
    242     WebCoreThreadViolationCheckRoundTwo();
    243 
    244     if (!_private->coreResource)
    245         return nil;
    246     NSString *mimeType = _private->coreResource->mimeType();
    247     return mimeType;
    248 }
    249 
    250 - (NSString *)textEncodingName
    251 {
    252 #ifdef MAIL_THREAD_WORKAROUND
    253     if (needMailThreadWorkaround())
    254         return [[self _webkit_invokeOnMainThread] textEncodingName];
    255 #endif
    256 
    257     WebCoreThreadViolationCheckRoundTwo();
    258 
    259     if (!_private->coreResource)
    260         return nil;
    261     NSString *textEncodingName = _private->coreResource->textEncoding();
    262     return textEncodingName;
    263 }
    264 
    265 - (NSString *)frameName
    266 {
    267 #ifdef MAIL_THREAD_WORKAROUND
    268     if (needMailThreadWorkaround())
    269         return [[self _webkit_invokeOnMainThread] frameName];
    270 #endif
    271 
    272     WebCoreThreadViolationCheckRoundTwo();
    273 
    274     if (!_private->coreResource)
    275         return nil;
    276     NSString *frameName = _private->coreResource->frameName();
    277     return frameName;
    278 }
    279 
    280 - (NSString *)description
    281 {
    282     return [NSString stringWithFormat:@"<%@ %@>", [self className], [self URL]];
    283 }
    284 
    285 @end
    286 
    287 @implementation WebResource (WebResourceInternal)
    288 
    289 - (id)_initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource
    290 {
    291     self = [super init];
    292     if (!self)
    293         return nil;
    294 
    295     ASSERT(coreResource);
    296 
    297     // WebResources should not be init'ed with nil data, and doing so breaks certain uses of NSHTMLReader
    298     // See <rdar://problem/5820157> for more info
    299     if (!coreResource->data()) {
    300         [self release];
    301         return nil;
    302     }
    303 
    304     _private = [[WebResourcePrivate alloc] initWithCoreResource:coreResource];
    305 
    306     return self;
    307 }
    308 
    309 - (WebCore::ArchiveResource *)_coreResource
    310 {
    311     return _private->coreResource;
    312 }
    313 
    314 @end
    315 
    316 @implementation WebResource (WebResourcePrivate)
    317 
    318 // SPI for Mail (5066325)
    319 // FIXME: This "ignoreWhenUnarchiving" concept is an ugly one - can we find a cleaner solution for those who need this SPI?
    320 - (void)_ignoreWhenUnarchiving
    321 {
    322 #ifdef MAIL_THREAD_WORKAROUND
    323     if (needMailThreadWorkaround()) {
    324         [[self _webkit_invokeOnMainThread] _ignoreWhenUnarchiving];
    325         return;
    326     }
    327 #endif
    328 
    329     WebCoreThreadViolationCheckRoundTwo();
    330 
    331     if (!_private->coreResource)
    332         return;
    333     _private->coreResource->ignoreWhenUnarchiving();
    334 }
    335 
    336 - (id)_initWithData:(NSData *)data
    337                 URL:(NSURL *)URL
    338            MIMEType:(NSString *)MIMEType
    339    textEncodingName:(NSString *)textEncodingName
    340           frameName:(NSString *)frameName
    341            response:(NSURLResponse *)response
    342            copyData:(BOOL)copyData
    343 {
    344 #ifdef MAIL_THREAD_WORKAROUND
    345     if (needMailThreadWorkaround())
    346         return [[self _webkit_invokeOnMainThread] _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:response copyData:copyData];
    347 #endif
    348 
    349     WebCoreThreadViolationCheckRoundTwo();
    350 
    351     self = [super init];
    352     if (!self)
    353         return nil;
    354 
    355     if (!data || !URL || !MIMEType) {
    356         [self release];
    357         return nil;
    358     }
    359 
    360     _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(copyData ? [[data copy] autorelease] : data), URL, MIMEType, textEncodingName, frameName, response)];
    361 
    362     return self;
    363 }
    364 
    365 - (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response
    366 {
    367     // Pass NO for copyData since the data doesn't need to be copied since we know that callers will no longer modify it.
    368     // Copying it will also cause a performance regression.
    369     return [self _initWithData:data
    370                            URL:URL
    371                       MIMEType:[response MIMEType]
    372               textEncodingName:[response textEncodingName]
    373                      frameName:nil
    374                       response:response
    375                       copyData:NO];
    376 }
    377 
    378 - (NSString *)_suggestedFilename
    379 {
    380 #ifdef MAIL_THREAD_WORKAROUND
    381     if (needMailThreadWorkaround())
    382         return [[self _webkit_invokeOnMainThread] _suggestedFilename];
    383 #endif
    384 
    385     WebCoreThreadViolationCheckRoundTwo();
    386 
    387     if (!_private->coreResource)
    388         return nil;
    389     NSString *suggestedFilename = _private->coreResource->response().suggestedFilename();
    390     return suggestedFilename;
    391 }
    392 
    393 - (NSFileWrapper *)_fileWrapperRepresentation
    394 {
    395     NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[self data]] autorelease];
    396     NSString *filename = [self _suggestedFilename];
    397     if (!filename || ![filename length])
    398         filename = [[self URL] _webkit_suggestedFilenameWithMIMEType:[self MIMEType]];
    399     [wrapper setPreferredFilename:filename];
    400     return wrapper;
    401 }
    402 
    403 - (NSURLResponse *)_response
    404 {
    405 #ifdef MAIL_THREAD_WORKAROUND
    406     if (needMailThreadWorkaround())
    407         return [[self _webkit_invokeOnMainThread] _response];
    408 #endif
    409 
    410     WebCoreThreadViolationCheckRoundTwo();
    411 
    412     NSURLResponse *response = nil;
    413     if (_private->coreResource)
    414         response = _private->coreResource->response().nsURLResponse();
    415     return response ? response : [[[NSURLResponse alloc] init] autorelease];
    416 }
    417 
    418 - (NSString *)_stringValue
    419 {
    420 #ifdef MAIL_THREAD_WORKAROUND
    421     if (needMailThreadWorkaround())
    422         return [[self _webkit_invokeOnMainThread] _stringValue];
    423 #endif
    424 
    425     WebCoreThreadViolationCheckRoundTwo();
    426 
    427     WebCore::TextEncoding encoding;
    428     if (_private->coreResource)
    429         encoding = _private->coreResource->textEncoding();
    430     if (!encoding.isValid())
    431         encoding = WindowsLatin1Encoding();
    432 
    433     SharedBuffer* coreData = _private->coreResource ? _private->coreResource->data() : 0;
    434     return encoding.decode(reinterpret_cast<const char*>(coreData ? coreData->data() : 0), coreData ? coreData->size() : 0);
    435 }
    436 
    437 @end
    438 
    439 #ifdef MAIL_THREAD_WORKAROUND
    440 
    441 static const double newMailBundleVersion = 1050.0;
    442 
    443 @implementation WebResource (WebMailThreadWorkaround)
    444 
    445 + (BOOL)_needMailThreadWorkaroundIfCalledOffMainThread
    446 {
    447     static BOOL isOldMail = applicationIsAppleMail() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] doubleValue] < newMailBundleVersion;
    448     return isOldMail;
    449 }
    450 
    451 @end
    452 
    453 #endif
    454