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 "WebArchive.h"
     30 #import "WebArchiveInternal.h"
     31 
     32 #import "WebKitLogging.h"
     33 #import "WebNSObjectExtras.h"
     34 #import "WebResourceInternal.h"
     35 #import "WebTypesInternal.h"
     36 #import <JavaScriptCore/InitializeThreading.h>
     37 #import <WebCore/ArchiveResource.h>
     38 #import <WebCore/LegacyWebArchive.h>
     39 #import <WebCore/ThreadCheck.h>
     40 #import <WebCore/WebCoreObjCExtras.h>
     41 
     42 using namespace WebCore;
     43 
     44 NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type";
     45 
     46 static NSString * const WebMainResourceKey = @"WebMainResource";
     47 static NSString * const WebSubresourcesKey = @"WebSubresources";
     48 static NSString * const WebSubframeArchivesKey = @"WebSubframeArchives";
     49 
     50 @interface WebArchivePrivate : NSObject {
     51 @public
     52     WebResource *cachedMainResource;
     53     NSArray *cachedSubresources;
     54     NSArray *cachedSubframeArchives;
     55 @private
     56     RefPtr<LegacyWebArchive> coreArchive;
     57 }
     58 
     59 - (id)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)coreArchive;
     60 - (LegacyWebArchive*)coreArchive;
     61 - (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive;
     62 @end
     63 
     64 @implementation WebArchivePrivate
     65 
     66 + (void)initialize
     67 {
     68     JSC::initializeThreading();
     69 #ifndef BUILDING_ON_TIGER
     70     WebCoreObjCFinalizeOnMainThread(self);
     71 #endif
     72 }
     73 
     74 - (id)init
     75 {
     76     self = [super init];
     77     if (!self)
     78         return nil;
     79     coreArchive = LegacyWebArchive::create();
     80     return self;
     81 }
     82 
     83 - (id)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)_coreArchive
     84 {
     85     self = [super init];
     86     if (!self || !_coreArchive) {
     87         [self release];
     88         return nil;
     89     }
     90     coreArchive = _coreArchive;
     91     return self;
     92 }
     93 
     94 - (LegacyWebArchive*)coreArchive
     95 {
     96     return coreArchive.get();
     97 }
     98 
     99 - (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive
    100 {
    101     ASSERT(coreArchive);
    102     ASSERT(newCoreArchive);
    103     coreArchive = newCoreArchive;
    104 }
    105 
    106 - (void)dealloc
    107 {
    108     if (WebCoreObjCScheduleDeallocateOnMainThread([WebArchivePrivate class], self))
    109         return;
    110 
    111     [cachedMainResource release];
    112     [cachedSubresources release];
    113     [cachedSubframeArchives release];
    114 
    115     [super dealloc];
    116 }
    117 
    118 @end
    119 
    120 @implementation WebArchive
    121 
    122 - (id)init
    123 {
    124     WebCoreThreadViolationCheckRoundTwo();
    125 
    126     self = [super init];
    127     if (!self)
    128         return nil;
    129     _private = [[WebArchivePrivate alloc] init];
    130     return self;
    131 }
    132 
    133 static BOOL isArrayOfClass(id object, Class elementClass)
    134 {
    135     if (![object isKindOfClass:[NSArray class]])
    136         return NO;
    137     NSArray *array = (NSArray *)object;
    138     NSUInteger count = [array count];
    139     for (NSUInteger i = 0; i < count; ++i)
    140         if (![[array objectAtIndex:i] isKindOfClass:elementClass])
    141             return NO;
    142     return YES;
    143 }
    144 
    145 - (id)initWithMainResource:(WebResource *)mainResource subresources:(NSArray *)subresources subframeArchives:(NSArray *)subframeArchives
    146 {
    147 #ifdef MAIL_THREAD_WORKAROUND
    148     if (needMailThreadWorkaround())
    149         return [[self _webkit_invokeOnMainThread] initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives];
    150 #endif
    151 
    152     WebCoreThreadViolationCheckRoundTwo();
    153 
    154     self = [super init];
    155     if (!self)
    156         return nil;
    157 
    158     _private = [[WebArchivePrivate alloc] init];
    159 
    160     _private->cachedMainResource = [mainResource retain];
    161     if (!_private->cachedMainResource) {
    162         [self release];
    163         return nil;
    164     }
    165 
    166     if (!subresources || isArrayOfClass(subresources, [WebResource class]))
    167         _private->cachedSubresources = [subresources retain];
    168     else {
    169         [self release];
    170         return nil;
    171     }
    172 
    173     if (!subframeArchives || isArrayOfClass(subframeArchives, [WebArchive class]))
    174         _private->cachedSubframeArchives = [subframeArchives retain];
    175     else {
    176         [self release];
    177         return nil;
    178     }
    179 
    180     RefPtr<ArchiveResource> coreMainResource = mainResource ? [mainResource _coreResource] : 0;
    181 
    182     Vector<PassRefPtr<ArchiveResource> > coreResources;
    183     NSEnumerator *enumerator = [subresources objectEnumerator];
    184     WebResource *subresource;
    185     while ((subresource = [enumerator nextObject]) != nil)
    186         coreResources.append([subresource _coreResource]);
    187 
    188     Vector<PassRefPtr<LegacyWebArchive> > coreArchives;
    189     enumerator = [subframeArchives objectEnumerator];
    190     WebArchive *subframeArchive;
    191     while ((subframeArchive = [enumerator nextObject]) != nil)
    192         coreArchives.append([subframeArchive->_private coreArchive]);
    193 
    194     [_private setCoreArchive:LegacyWebArchive::create(coreMainResource.release(), coreResources, coreArchives)];
    195     if (![_private coreArchive]) {
    196         [self release];
    197         return nil;
    198     }
    199 
    200     return self;
    201 }
    202 
    203 - (id)initWithData:(NSData *)data
    204 {
    205     WebCoreThreadViolationCheckRoundTwo();
    206 
    207     self = [super init];
    208     if (!self)
    209         return nil;
    210 
    211 #if !LOG_DISABLED
    212     CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    213 #endif
    214 
    215     _private = [[WebArchivePrivate alloc] init];
    216     RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::create(SharedBuffer::wrapNSData(data).get());
    217     if (!coreArchive) {
    218         [self release];
    219         return nil;
    220     }
    221 
    222     [_private setCoreArchive:coreArchive.release()];
    223 
    224 #if !LOG_DISABLED
    225     CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    226     CFAbsoluteTime duration = end - start;
    227 #endif
    228     LOG(Timing, "Parsing web archive with [NSPropertyListSerialization propertyListFromData::::] took %f seconds", duration);
    229 
    230     return self;
    231 }
    232 
    233 - (id)initWithCoder:(NSCoder *)decoder
    234 {
    235     WebResource *mainResource = nil;
    236     NSArray *subresources = nil;
    237     NSArray *subframeArchives = nil;
    238 
    239     @try {
    240         id object = [decoder decodeObjectForKey:WebMainResourceKey];
    241         if ([object isKindOfClass:[WebResource class]])
    242             mainResource = [object retain];
    243         object = [decoder decodeObjectForKey:WebSubresourcesKey];
    244         if (isArrayOfClass(object, [WebResource class]))
    245             subresources = [object retain];
    246         object = [decoder decodeObjectForKey:WebSubframeArchivesKey];
    247         if (isArrayOfClass(object, [WebArchive class]))
    248             subframeArchives = [object retain];
    249     } @catch(id) {
    250         [self release];
    251         return nil;
    252     }
    253 
    254     return [self initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives];
    255 }
    256 
    257 - (void)encodeWithCoder:(NSCoder *)encoder
    258 {
    259     [encoder encodeObject:[self mainResource] forKey:WebMainResourceKey];
    260     [encoder encodeObject:[self subresources] forKey:WebSubresourcesKey];
    261     [encoder encodeObject:[self subframeArchives] forKey:WebSubframeArchivesKey];
    262 }
    263 
    264 - (void)dealloc
    265 {
    266     [_private release];
    267     [super dealloc];
    268 }
    269 
    270 - (id)copyWithZone:(NSZone *)zone
    271 {
    272     return [self retain];
    273 }
    274 
    275 - (WebResource *)mainResource
    276 {
    277 #ifdef MAIL_THREAD_WORKAROUND
    278     if (needMailThreadWorkaround())
    279         return [[self _webkit_invokeOnMainThread] mainResource];
    280 #endif
    281 
    282     WebCoreThreadViolationCheckRoundTwo();
    283 
    284     // Currently from WebKit API perspective, WebArchives are entirely immutable once created
    285     // If they ever become mutable, we'll need to rethink this.
    286     if (!_private->cachedMainResource) {
    287         LegacyWebArchive* coreArchive = [_private coreArchive];
    288         if (coreArchive)
    289             _private->cachedMainResource = [[WebResource alloc] _initWithCoreResource:coreArchive->mainResource()];
    290     }
    291 
    292     return [[_private->cachedMainResource retain] autorelease];
    293 }
    294 
    295 - (NSArray *)subresources
    296 {
    297 #ifdef MAIL_THREAD_WORKAROUND
    298     if (needMailThreadWorkaround())
    299         return [[self _webkit_invokeOnMainThread] subresources];
    300 #endif
    301 
    302     WebCoreThreadViolationCheckRoundTwo();
    303 
    304     // Currently from WebKit API perspective, WebArchives are entirely immutable once created
    305     // If they ever become mutable, we'll need to rethink this.
    306     if (!_private->cachedSubresources) {
    307         LegacyWebArchive* coreArchive = [_private coreArchive];
    308         if (!coreArchive)
    309             _private->cachedSubresources = [[NSArray alloc] init];
    310         else {
    311             const Vector<RefPtr<ArchiveResource> >& subresources(coreArchive->subresources());
    312             NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subresources.size()];
    313             _private->cachedSubresources = mutableArray;
    314             for (unsigned i = 0; i < subresources.size(); ++i) {
    315                 WebResource *resource = [[WebResource alloc] _initWithCoreResource:subresources[i].get()];
    316                 if (resource) {
    317                     [mutableArray addObject:resource];
    318                     [resource release];
    319                 }
    320             }
    321         }
    322     }
    323     // Maintain the WebKit 3 behavior of this API, which is documented and
    324     // relied upon by some clients, of returning nil if there are no subresources.
    325     return [_private->cachedSubresources count] ? [[_private->cachedSubresources retain] autorelease] : nil;
    326 }
    327 
    328 - (NSArray *)subframeArchives
    329 {
    330 #ifdef MAIL_THREAD_WORKAROUND
    331     if (needMailThreadWorkaround())
    332         return [[self _webkit_invokeOnMainThread] subframeArchives];
    333 #endif
    334 
    335     WebCoreThreadViolationCheckRoundTwo();
    336 
    337     // Currently from WebKit API perspective, WebArchives are entirely immutable once created
    338     // If they ever become mutable, we'll need to rethink this.
    339     if (!_private->cachedSubframeArchives) {
    340         LegacyWebArchive* coreArchive = [_private coreArchive];
    341         if (!coreArchive)
    342             _private->cachedSubframeArchives = [[NSArray alloc] init];
    343         else {
    344             const Vector<RefPtr<Archive> >& subframeArchives(coreArchive->subframeArchives());
    345             NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subframeArchives.size()];
    346             _private->cachedSubframeArchives = mutableArray;
    347             for (unsigned i = 0; i < subframeArchives.size(); ++i) {
    348                 WebArchive *archive = [[WebArchive alloc] _initWithCoreLegacyWebArchive:(LegacyWebArchive *)subframeArchives[i].get()];
    349                 [mutableArray addObject:archive];
    350                 [archive release];
    351             }
    352         }
    353     }
    354 
    355     return [[_private->cachedSubframeArchives retain] autorelease];
    356 }
    357 
    358 - (NSData *)data
    359 {
    360     WebCoreThreadViolationCheckRoundTwo();
    361 
    362 #if !LOG_DISABLED
    363     CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    364 #endif
    365 
    366     RetainPtr<CFDataRef> data = [_private coreArchive]->rawDataRepresentation();
    367 
    368 #if !LOG_DISABLED
    369     CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    370     CFAbsoluteTime duration = end - start;
    371 #endif
    372     LOG(Timing, "Serializing web archive to raw CFPropertyList data took %f seconds", duration);
    373 
    374     return [[(NSData *)data.get() retain] autorelease];
    375 }
    376 
    377 @end
    378 
    379 @implementation WebArchive (WebInternal)
    380 
    381 - (id)_initWithCoreLegacyWebArchive:(PassRefPtr<WebCore::LegacyWebArchive>)coreLegacyWebArchive
    382 {
    383     WebCoreThreadViolationCheckRoundTwo();
    384 
    385     self = [super init];
    386     if (!self)
    387         return nil;
    388 
    389     _private = [[WebArchivePrivate alloc] initWithCoreArchive:coreLegacyWebArchive];
    390     if (!_private) {
    391         [self release];
    392         return nil;
    393     }
    394 
    395     return self;
    396 }
    397 
    398 - (WebCore::LegacyWebArchive *)_coreLegacyWebArchive
    399 {
    400     WebCoreThreadViolationCheckRoundTwo();
    401 
    402     return [_private coreArchive];
    403 }
    404 
    405 @end
    406