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