WebFrame.m 118 KB
Newer Older
darin's avatar
darin committed
1
/*
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 * Copyright (C) 2005 Apple Computer, 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.
 */
28

darin's avatar
darin committed
29
#import <WebKit/WebFrameInternal.h>
darin's avatar
darin committed
30

kocienda's avatar
Tests:    
kocienda committed
31
#import <WebKit/DOM.h>
cblu's avatar
Tests:    
cblu committed
32
#import <WebKit/WebArchive.h>
rjw's avatar
WebKit:    
rjw committed
33
#import <WebKit/WebBackForwardList.h>
mjs's avatar
mjs committed
34
#import <WebKit/WebFrameBridge.h>
rjw's avatar
WebKit:    
rjw committed
35
#import <WebKit/WebDataProtocol.h>
mjs's avatar
mjs committed
36
#import <WebKit/WebDataSourceInternal.h>
cblu's avatar
cblu committed
37
#import <WebKit/WebDefaultResourceLoadDelegate.h>
cblu's avatar
Tests:    
cblu committed
38
39
#import <WebKit/WebDefaultUIDelegate.h>
#import <WebKit/WebDocumentInternal.h>
darin's avatar
darin committed
40
#import <WebKit/WebFormDataStream.h>
cblu's avatar
Tests:    
cblu committed
41
#import <WebKit/WebFrameLoadDelegate.h>
darin's avatar
darin committed
42
#import <WebKit/WebFrameViewInternal.h>
cblu's avatar
Tests:    
cblu committed
43
44
#import <WebKit/WebHistoryPrivate.h>
#import <WebKit/WebHistoryItemPrivate.h>
rjw's avatar
WebKit:    
rjw committed
45
#import <WebKit/WebHTMLRepresentationPrivate.h>
tomernic's avatar
tomernic committed
46
#import <WebKit/WebHTMLViewInternal.h>
rjw's avatar
WebKit:    
rjw committed
47
#import <WebKit/WebHTMLViewPrivate.h>
cblu's avatar
Tests:    
cblu committed
48
#import <WebKit/WebKitErrorsPrivate.h>
49
#import <WebKit/WebKitLogging.h>
mjs's avatar
mjs committed
50
#import <WebKit/WebKitNSStringExtras.h>
cblu's avatar
Tests:    
cblu committed
51
#import <WebKit/WebKitStatisticsPrivate.h>
cblu's avatar
cblu committed
52
#import <WebKit/WebNetscapePluginEmbeddedView.h>
53
#import <WebKit/WebNSObjectExtras.h>
cblu's avatar
Tests:    
cblu committed
54
#import <WebKit/WebNSURLExtras.h>
mjs's avatar
mjs committed
55
#import <WebKit/WebNSURLRequestExtras.h>
cblu's avatar
cblu committed
56
#import <WebKit/WebNullPluginView.h>
cblu's avatar
Tests:    
cblu committed
57
#import <WebKit/WebPreferencesPrivate.h>
cblu's avatar
cblu committed
58
#import <WebKit/WebPlugin.h>
59
#import <WebKit/WebPluginController.h>
cblu's avatar
cblu committed
60
#import <WebKit/WebResourceLoadDelegate.h>
cblu's avatar
Tests:    
cblu committed
61
#import <WebKit/WebResourcePrivate.h>
darin's avatar
darin committed
62
#import <WebKit/WebViewInternal.h>
rjw's avatar
WebKit:    
rjw committed
63
#import <WebKit/WebUIDelegate.h>
64
#import <WebKit/WebScriptDebugDelegatePrivate.h>
mjs's avatar
mjs committed
65
#import <WebKitSystemInterface.h>
rjw's avatar
WebKit:    
rjw committed
66

cblu's avatar
Tests:    
cblu committed
67
#import <objc/objc-runtime.h>
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

#ifndef NDEBUG
static const char * const stateNames[] = {
    "WebFrameStateProvisional",
    "WebFrameStateCommittedPage",
    "WebFrameStateComplete"
};
#endif

/*
Here is the current behavior matrix for four types of navigations:

Standard Nav:

 Restore form state:   YES
 Restore scroll and focus state:  YES
 WF Cache policy: NSURLRequestUseProtocolCachePolicy
 Add to back/forward list: YES
 
Back/Forward:

 Restore form state:   YES
 Restore scroll and focus state:  YES
 WF Cache policy: NSURLRequestReturnCacheDataElseLoad
 Add to back/forward list: NO

Reload (meaning only the reload button):

 Restore form state:   NO
 Restore scroll and focus state:  YES
 WF Cache policy: NSURLRequestReloadIgnoringCacheData
 Add to back/forward list: NO

Repeat load of the same URL (by any other means of navigation other than the reload button, including hitting return in the location field):

 Restore form state:   NO
 Restore scroll and focus state:  NO, reset to initial conditions
 WF Cache policy: NSURLRequestReloadIgnoringCacheData
 Add to back/forward list: NO
*/

NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey";
NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey";
NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey";

@interface NSObject (WebExtraPerformMethod)

- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 withObject:(id)object3;

@end

@implementation NSObject (WebExtraPerformMethod)

- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 withObject:(id)object3
{
    return objc_msgSend(self, aSelector, object1, object2, object3);
}

@end

// One day we might want to expand the use of this kind of class such that we'd receive one
// over the bridge, and possibly hand it on through to the FormsDelegate.
// Today it is just used internally to keep some state as we make our way through a bunch
// layers while doing a load.
@interface WebFormState : NSObject
{
kocienda's avatar
Tests:    
kocienda committed
134
    DOMElement *_form;
135
136
137
    NSDictionary *_values;
    WebFrame *_sourceFrame;
}
kocienda's avatar
Tests:    
kocienda committed
138
139
- (id)initWithForm:(DOMElement *)form values:(NSDictionary *)values sourceFrame:(WebFrame *)sourceFrame;
- (DOMElement *)form;
140
141
142
143
144
145
- (NSDictionary *)values;
- (WebFrame *)sourceFrame;
@end

@interface WebFrame (ForwardDecls)
- (void)_loadRequest:(NSURLRequest *)request triggeringAction:(NSDictionary *)action loadType:(WebFrameLoadType)loadType formState:(WebFormState *)formState;
sullivan's avatar
WebKit:    
sullivan committed
146
- (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL;
147
148
- (NSDictionary *)_actionInformationForLoadType:(WebFrameLoadType)loadType isFormSubmission:(BOOL)isFormSubmission event:(NSEvent *)event originalURL:(NSURL *)URL;

149
150
- (void)_saveScrollPositionAndViewStateToItem:(WebHistoryItem *)item;
- (void)_restoreScrollPositionAndViewState;
151
152
153

- (WebHistoryItem *)_createItem: (BOOL)useOriginal;
- (WebHistoryItem *)_createItemTreeWithTargetFrame:(WebFrame *)targetFrame clippedAtTarget:(BOOL)doClip;
sullivan's avatar
WebKit:    
sullivan committed
154
- (WebHistoryItem *)_currentBackForwardListItemToResetTo;
mjs's avatar
mjs committed
155
- (void)_stopLoadingSubframes;
156
157
@end

mjs's avatar
mjs committed
158
159
160
@interface WebFrame (FrameTraversal)
- (WebFrame *)_firstChildFrame;
- (WebFrame *)_lastChildFrame;
mjs's avatar
mjs committed
161
- (unsigned)_childFrameCount;
mjs's avatar
mjs committed
162
163
164
165
166
- (WebFrame *)_previousSiblingFrame;
- (WebFrame *)_nextSiblingFrame;
- (WebFrame *)_traverseNextFrameStayWithin:(WebFrame *)stayWithin;
@end

tomernic's avatar
tomernic committed
167
168
169
170
@interface NSView (WebFramePluginHosting)
- (void)setWebFrame:(WebFrame *)webFrame;
@end

mjs's avatar
mjs committed
171
172
173
174
175
176
@interface WebFramePrivate : NSObject
{
@public
    WebFrameView *webFrameView;
    WebDataSource *dataSource;
    WebDataSource *provisionalDataSource;
mjs's avatar
mjs committed
177
    WebFrameBridge *bridge;
mjs's avatar
mjs committed
178
179
    WebFrameState state;
    WebFrameLoadType loadType;
180
181
    WebHistoryItem *currentItem;        // BF item for our current content
    WebHistoryItem *provisionalItem;    // BF item for where we're trying to go
mjs's avatar
mjs committed
182
                                        // (only known when navigating to a pre-existing BF item)
183
    WebHistoryItem *previousItem;       // BF item for previous content, see _itemForSavingDocState
mjs's avatar
mjs committed
184
185
186
187
188
189
190
191
192
193
194
195

    WebPolicyDecisionListener *listener;
    // state we'll need to continue after waiting for the policy delegate's decision
    NSURLRequest *policyRequest;
    NSString *policyFrameName;
    id policyTarget;
    SEL policySelector;
    WebFormState *policyFormState;
    WebDataSource *policyDataSource;
    WebFrameLoadType policyLoadType;

    BOOL quickRedirectComing;
tomernic's avatar
tomernic committed
196
    BOOL sentRedirectNotification;
mjs's avatar
mjs committed
197
198
199
200
    BOOL isStoppingLoad;
    BOOL delegateIsHandlingProvisionalLoadError;
    BOOL delegateIsDecidingNavigationPolicy;
    BOOL delegateIsHandlingUnimplementablePolicy;
ggaren's avatar
ggaren committed
201
    BOOL firstLayoutDone;
mjs's avatar
mjs committed
202
203
204
205
206
    
    id internalLoadDelegate;
    WebScriptDebugger *scriptDebugger;

    NSString *frameNamespace;
tomernic's avatar
tomernic committed
207
208
    
    NSMutableSet *plugInViews;
ddkilzer's avatar
WebKit:    
ddkilzer committed
209
    NSMutableSet *inspectors;
mjs's avatar
mjs committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
}

- (void)setWebFrameView:(WebFrameView *)v;
- (WebFrameView *)webFrameView;
- (void)setDataSource:(WebDataSource *)d;
- (WebDataSource *)dataSource;
- (void)setProvisionalDataSource:(WebDataSource *)d;
- (WebDataSource *)provisionalDataSource;
- (WebFrameLoadType)loadType;
- (void)setLoadType:(WebFrameLoadType)loadType;

- (void)setProvisionalItem:(WebHistoryItem *)item;
- (WebHistoryItem *)provisionalItem;
- (void)setPreviousItem:(WebHistoryItem *)item;
- (WebHistoryItem *)previousItem;
- (void)setCurrentItem:(WebHistoryItem *)item;
- (WebHistoryItem *)currentItem;

@end

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
@implementation WebFramePrivate

- init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    state = WebFrameStateComplete;
    loadType = WebFrameLoadTypeStandard;
    
    return self;
}

- (void)dealloc
{
    [webFrameView release];
    [dataSource release];
    [provisionalDataSource release];

    [currentItem release];
    [provisionalItem release];
    [previousItem release];
    
mjs's avatar
mjs committed
255
256
    [scriptDebugger release];
    
ddkilzer's avatar
WebKit:    
ddkilzer committed
257
258
    [inspectors release];

259
260
261
262
263
264
    ASSERT(listener == nil);
    ASSERT(policyRequest == nil);
    ASSERT(policyFrameName == nil);
    ASSERT(policyTarget == nil);
    ASSERT(policyFormState == nil);
    ASSERT(policyDataSource == nil);
eseidel's avatar
eseidel committed
265
    ASSERT(frameNamespace == nil);
tomernic's avatar
tomernic committed
266
267
    ASSERT(plugInViews == nil);
    
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
    [super dealloc];
}

- (WebFrameView *)webFrameView { return webFrameView; }
- (void)setWebFrameView: (WebFrameView *)v 
{ 
    [v retain];
    [webFrameView release];
    webFrameView = v;
}

- (WebDataSource *)dataSource { return dataSource; }
- (void)setDataSource: (WebDataSource *)d
{
    [d retain];
    [dataSource release];
    dataSource = d;
}

- (WebDataSource *)provisionalDataSource { return provisionalDataSource; }
- (void)setProvisionalDataSource: (WebDataSource *)d
{
    ASSERT(!d || !provisionalDataSource);
    [d retain];
    [provisionalDataSource release];
    provisionalDataSource = d;
}

- (WebFrameLoadType)loadType { return loadType; }
- (void)setLoadType: (WebFrameLoadType)t
{
    loadType = t;
}

- (WebHistoryItem *)provisionalItem { return provisionalItem; }
- (void)setProvisionalItem: (WebHistoryItem *)item
{
    [item retain];
    [provisionalItem release];
    provisionalItem = item;
}

- (WebHistoryItem *)previousItem { return previousItem; }
- (void)setPreviousItem:(WebHistoryItem *)item
{
    [item retain];
    [previousItem release];
    previousItem = item;
}

- (WebHistoryItem *)currentItem { return currentItem; }
- (void)setCurrentItem:(WebHistoryItem *)item
{
    [item retain];
    [currentItem release];
    currentItem = item;
}

@end

mjs's avatar
mjs committed
328
static inline WebFrame *Frame(WebCoreFrameBridge *bridge)
mjs's avatar
mjs committed
329
{
mjs's avatar
mjs committed
330
    return [(WebFrameBridge *)bridge webFrame];
mjs's avatar
mjs committed
331
332
}

mjs's avatar
mjs committed
333
334
335
@implementation WebFrame (FrameTraversal)
- (WebFrame *)_firstChildFrame
{
mjs's avatar
mjs committed
336
    return Frame([[self _bridge] firstChild]);
mjs's avatar
mjs committed
337
338
339
340
}

- (WebFrame *)_lastChildFrame
{
mjs's avatar
mjs committed
341
    return Frame([[self _bridge] lastChild]);
mjs's avatar
mjs committed
342
343
}

mjs's avatar
mjs committed
344
345
- (unsigned)_childFrameCount
{
mjs's avatar
mjs committed
346
    return [[self _bridge] childCount];
mjs's avatar
mjs committed
347
348
}

mjs's avatar
mjs committed
349
350
- (WebFrame *)_previousSiblingFrame;
{
mjs's avatar
mjs committed
351
    return Frame([[self _bridge] previousSibling]);
mjs's avatar
mjs committed
352
353
354
355
}

- (WebFrame *)_nextSiblingFrame;
{
mjs's avatar
mjs committed
356
    return Frame([[self _bridge] nextSibling]);
mjs's avatar
mjs committed
357
358
359
360
}

- (WebFrame *)_traverseNextFrameStayWithin:(WebFrame *)stayWithin
{
mjs's avatar
mjs committed
361
    return Frame([[self _bridge] traverseNextFrameStayWithin:[stayWithin _bridge]]);
mjs's avatar
mjs committed
362
363
364
365
}

@end

366
367
@implementation WebFrame (WebPrivate)

sullivan's avatar
WebKit:    
sullivan committed
368
- (NSURLRequest *)_webDataRequestForData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName: (NSString *)encodingName baseURL:(NSURL *)URL unreachableURL:(NSURL *)unreachableURL
cblu's avatar
Tests:    
cblu committed
369
{
cblu's avatar
WebKit:    
cblu committed
370
371
    NSURL *fakeURL = [NSURL _web_uniqueWebDataURL];
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:fakeURL] autorelease];
cblu's avatar
Tests:    
cblu committed
372
373
374
    [request _webDataRequestSetData:data];
    [request _webDataRequestSetEncoding:encodingName];
    [request _webDataRequestSetBaseURL:URL];
sullivan's avatar
WebKit:    
sullivan committed
375
    [request _webDataRequestSetUnreachableURL:unreachableURL];
justing's avatar
justing committed
376
    [request _webDataRequestSetMIMEType: MIMEType ? MIMEType : (NSString *)@"text/html"];
cblu's avatar
Tests:    
cblu committed
377
378
379
    return request;
}

sullivan's avatar
WebKit:    
sullivan committed
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
- (BOOL)_shouldReloadToHandleUnreachableURLFromRequest:(NSURLRequest *)request
{
    NSURL *unreachableURL = [request _webDataRequestUnreachableURL];
    if (unreachableURL == nil) {
        return NO;
    }
    
    if (_private->policyLoadType != WebFrameLoadTypeForward
        && _private->policyLoadType != WebFrameLoadTypeBack
        && _private->policyLoadType != WebFrameLoadTypeIndexedBackForward) {
        return NO;
    }
    
    // We only treat unreachableURLs specially during the delegate callbacks
    // for provisional load errors and navigation policy decisions. The former
    // case handles well-formed URLs that can't be loaded, and the latter
    // case handles malformed URLs and unknown schemes. Loading alternate content
    // at other times behaves like a standard load.
    WebDataSource *compareDataSource = nil;
sullivan's avatar
WebKit:    
sullivan committed
399
    if (_private->delegateIsDecidingNavigationPolicy || _private->delegateIsHandlingUnimplementablePolicy) {
sullivan's avatar
WebKit:    
sullivan committed
400
401
402
403
404
405
406
407
        compareDataSource = _private->policyDataSource;
    } else if (_private->delegateIsHandlingProvisionalLoadError) {
        compareDataSource = [self provisionalDataSource];
    }
    
    return compareDataSource != nil && [unreachableURL isEqual:[[compareDataSource request] URL]];
}

mjs's avatar
mjs committed
408
- (void)_loadRequest:(NSURLRequest *)request archive:(WebArchive *)archive
cblu's avatar
Tests:    
cblu committed
409
410
411
412
413
414
{
    WebFrameLoadType loadType;
    
    // note this copies request
    WebDataSource *newDataSource = [[WebDataSource alloc] initWithRequest:request];
    NSMutableURLRequest *r = [newDataSource request];
mjs's avatar
mjs committed
415
    [self _addExtraFieldsToRequest:r mainResource:YES alwaysFromRequest:NO];
cblu's avatar
Tests:    
cblu committed
416
417
418
419
420
421
422
423
    if ([self _shouldTreatURLAsSameAsCurrent:[request URL]]) {
        [r setCachePolicy:NSURLRequestReloadIgnoringCacheData];
        loadType = WebFrameLoadTypeSame;
    } else {
        loadType = WebFrameLoadTypeStandard;
    }
    
    [newDataSource _setOverrideEncoding:[[self dataSource] _overrideEncoding]];
mjs's avatar
mjs committed
424
    [newDataSource _addToUnarchiveState:archive];
cblu's avatar
Tests:    
cblu committed
425
    
sullivan's avatar
WebKit:    
sullivan committed
426
427
428
429
430
431
432
433
    // When we loading alternate content for an unreachable URL that we're
    // visiting in the b/f list, we treat it as a reload so the b/f list 
    // is appropriately maintained.
    if ([self _shouldReloadToHandleUnreachableURLFromRequest:request]) {
        ASSERT(loadType == WebFrameLoadTypeStandard);
        loadType = WebFrameLoadTypeReload;
    }
    
cblu's avatar
Tests:    
cblu committed
434
435
436
437
    [self _loadDataSource:newDataSource withLoadType:loadType formState:nil];
    [newDataSource release];
}

438
439
440
// helper method used in various nav cases below
- (void)_addBackForwardItemClippedAtTarget:(BOOL)doClip
{
sullivan's avatar
WebKit:    
sullivan committed
441
    if ([[self dataSource] _URLForHistory] != nil) {
442
443
444
445
446
447
        WebHistoryItem *bfItem = [[[self webView] mainFrame] _createItemTreeWithTargetFrame:self clippedAtTarget:doClip];
        LOG (BackForward, "for frame %@, adding item  %@\n", [self name], bfItem);
        [[[self webView] backForwardList] addItem:bfItem];
    }
}

mjs's avatar
mjs committed
448
- (WebHistoryItem *)_createItem:(BOOL)useOriginal
449
450
451
{
    WebDataSource *dataSrc = [self dataSource];
    NSURLRequest *request;
cblu's avatar
WebKit:    
cblu committed
452
    NSURL *unreachableURL = [dataSrc unreachableURL];
453
    NSURL *URL;
cblu's avatar
WebKit:    
cblu committed
454
    NSURL *originalURL;
455
456
    WebHistoryItem *bfItem;

darin's avatar
darin committed
457
    if (useOriginal) {
458
        request = [dataSrc _originalRequest];
cblu's avatar
WebKit:    
cblu committed
459
    } else {
460
461
        request = [dataSrc request];
    }
cblu's avatar
WebKit:    
cblu committed
462
463
464
465
466

    if (unreachableURL != nil) {
        URL = unreachableURL;
        originalURL = unreachableURL;
    } else {
sullivan's avatar
WebKit:    
sullivan committed
467
        URL = [request URL];
cblu's avatar
WebKit:    
cblu committed
468
        originalURL = [[dataSrc _originalRequest] URL];
sullivan's avatar
WebKit:    
sullivan committed
469
    }
470
471
472
473
474
475
476
477
478
479
480

    LOG (History, "creating item for %@", request);
    
    // Frames that have never successfully loaded any content
    // may have no URL at all. Currently our history code can't
    // deal with such things, so we nip that in the bud here.
    // Later we may want to learn to live with nil for URL.
    // See bug 3368236 and related bugs for more information.
    if (URL == nil) {
        URL = [NSURL URLWithString:@"about:blank"];
    }
cblu's avatar
WebKit:    
cblu committed
481
482
483
484
    if (originalURL == nil) {
        originalURL = [NSURL URLWithString:@"about:blank"];
    }
    
485
    bfItem = [[[WebHistoryItem alloc] initWithURL:URL target:[self name] parent:[[self parentFrame] name] title:[dataSrc pageTitle]] autorelease];
cblu's avatar
WebKit:    
cblu committed
486
    [bfItem setOriginalURLString:[originalURL _web_originalDataAsString]];
487
488

    // save form state if this is a POST
489
    [bfItem _setFormInfoFromRequest:request];
490
491
492
493
494
495
496
497
498

    // Set the item for which we will save document state
    [_private setPreviousItem:[_private currentItem]];
    [_private setCurrentItem:bfItem];

    return bfItem;
}

/*
sullivan's avatar
WebKit:    
sullivan committed
499
500
501
502
    In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.  
    The item that was the target of the user's navigation is designated as the "targetItem".  
    When this method is called with doClip=YES we're able to create the whole tree except for the target's children, 
    which will be loaded in the future.  That part of the tree will be filled out as the child loads are committed.
503
504
505
*/
- (WebHistoryItem *)_createItemTreeWithTargetFrame:(WebFrame *)targetFrame clippedAtTarget:(BOOL)doClip
{
mjs's avatar
mjs committed
506
    WebHistoryItem *bfItem = [self _createItem:[self parentFrame] ? YES : NO];
507

508
    [self _saveScrollPositionAndViewStateToItem:[_private previousItem]];
509
510
511
512
    if (!(doClip && self == targetFrame)) {
        // save frame state for items that aren't loading (khtml doesn't save those)
        [_private->bridge saveDocumentState];

mjs's avatar
mjs committed
513
514
        for (WebFrame *child = [self _firstChildFrame]; child; child = [child _nextSiblingFrame])
            [bfItem addChildItem:[child _createItemTreeWithTargetFrame:targetFrame clippedAtTarget:doClip]];
515
    }
mjs's avatar
mjs committed
516
    if (self == targetFrame)
517
        [bfItem setIsTargetItem:YES];
mjs's avatar
mjs committed
518

519
520
521
522
523
    return bfItem;
}

- (WebFrame *)_immediateChildFrameNamed:(NSString *)name
{
mjs's avatar
mjs committed
524
    return Frame([[self _bridge] childFrameNamed:name]);
525
526
}

mjs's avatar
mjs committed
527
528
// FIXME: this exists only as a convenience for Safari, consider moving there
- (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor
trey's avatar
WebKit:    
trey committed
529
{
mjs's avatar
mjs committed
530
    return [[self _bridge] isDescendantOfFrame:[ancestor _bridge]];
trey's avatar
WebKit:    
trey committed
531
532
}

sullivan's avatar
WebKit:    
sullivan committed
533
534
535
536
537
- (BOOL)_isFrameSet
{
    return [_private->bridge isFrameSet];
}

538
539
- (void)_detachChildren
{
mjs's avatar
mjs committed
540
541
542
543
544
    // FIXME: is it really necessary to do this in reverse order any more?
    WebFrame *child = [self _lastChildFrame];
    WebFrame *prev = [child _previousSiblingFrame];
    for (; child; child = prev, prev = [child _previousSiblingFrame])
        [child _detachFromParent];
545
546
547
548
}

- (void)_closeOldDataSources
{
mjs's avatar
mjs committed
549
550
551
552
553
554
    // FIXME: is it important for this traversal to be postorder instead of preorder?
    // FIXME: add helpers for postorder traversal?
    for (WebFrame *child = [self _firstChildFrame]; child; child = [child _nextSiblingFrame])
        [child _closeOldDataSources];

    if (_private->dataSource)
555
        [[[self webView] _frameLoadDelegateForwarder] webView:[self webView] willCloseFrame:self];
thatcher's avatar
thatcher committed
556
    [[self webView] setMainFrameDocumentReady:NO];  // stop giving out the actual DOMDocument to observers
557
558
559
560
}

- (void)_detachFromParent
{
thatcher's avatar
thatcher committed
561
    WebFrameBridge *bridge = [_private->bridge retain];
562
563

    [bridge closeURL];
ggaren's avatar
ggaren committed
564
    [self stopLoading];
565

ggaren's avatar
ggaren committed
566
    [self _saveScrollPositionAndViewStateToItem:[_private currentItem]];
567
    [self _detachChildren];
ddkilzer's avatar
WebKit:    
ddkilzer committed
568
    [_private->inspectors makeObjectsPerformSelector:@selector(_webFrameDetached:) withObject:self];
569

mjs's avatar
mjs committed
570
    [_private->webFrameView _setWebFrame:nil]; // needed for now to be compatible w/ old behavior
571
572
573
574

    [self _setDataSource:nil];
    [_private setWebFrameView:nil];

mjs's avatar
mjs committed
575
576
    [self retain]; // retain self temporarily because dealloc can re-enter this method

mjs's avatar
mjs committed
577
    [[[self parentFrame] _bridge] removeChild:bridge];
mjs's avatar
mjs committed
578

thatcher's avatar
thatcher committed
579
580
581
582
    [bridge close];
    [bridge release];

    bridge = nil;
mjs's avatar
mjs committed
583
584
585
    _private->bridge = nil;

    [self release];
586
587
588
589
590
}

- (void)_setDataSource:(WebDataSource *)ds
{
    if (ds == nil && _private->dataSource == nil) {
darin's avatar
darin committed
591
        return;
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
    }

    ASSERT(ds != _private->dataSource);

    if (_private->dataSource) {
        // Make sure that any work that is triggered by resigning first reponder can get done.
        // The main example where this came up is the textDidEndEditing that is sent to the
        // FormsDelegate (3223413).  We need to do this before _detachChildren, since that will
        // remove the views as a side-effect of freeing the bridge, at which point we can't
        // post the FormDelegate messages.
        //
        // Note that this can also take FirstResponder away from a child of our frameView that
        // is not in a child frame's view.  This is OK because we are in the process
        // of loading new content, which will blow away all editors in this top frame, and if
        // a non-editor is firstReponder it will not be affected by endEditingFor:.
        // Potentially one day someone could write a DocView whose editors were not all
        // replaced by loading new content, but that does not apply currently.
        NSView *frameView = [self frameView];
        NSWindow *window = [frameView window];
        NSResponder *firstResp = [window firstResponder];
        if ([firstResp isKindOfClass:[NSView class]]
            && [(NSView *)firstResp isDescendantOf:frameView])
        {
            [window endEditingFor:firstResp];
        }

        [self _detachChildren];

        [_private->dataSource _setWebFrame:nil];
    } else {
mjs's avatar
mjs committed
622
        ASSERT(![self _childFrameCount]);
623
624
625
626
627
628
629
630
    }

    [_private setDataSource:ds];
    [ds _setWebFrame:self];
}

- (void)_setLoadType: (WebFrameLoadType)t
{
mjs's avatar
mjs committed
631
    [_private setLoadType:t];
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
}

- (WebFrameLoadType)_loadType
{
    return [_private loadType];
}

- (void)_makeDocumentView
{
    NSView <WebDocumentView> *documentView = [_private->webFrameView _makeDocumentViewForDataSource:_private->dataSource];
    if (!documentView) {
        return;
    }

    // FIXME: We could save work and not do this for a top-level view that is not a WebHTMLView.
    WebFrameView *v = _private->webFrameView;
648
    [_private->bridge createFrameViewWithNSView:documentView marginWidth:[v _marginWidth] marginHeight:[v _marginHeight]];
darin's avatar
darin committed
649
    [self _updateDrawsBackground];
650
651
652
653
654
655
656
    [_private->bridge installInFrame:[v _scrollView]];

    // Call setDataSource on the document view after it has been placed in the view hierarchy.
    // This what we for the top-level view, so should do this for views in subframes as well.
    [documentView setDataSource:_private->dataSource];
}

trey's avatar
trey committed
657
658
659
660
661
662
663
- (void)_receivedMainResourceError:(NSError *)error
{
    if ([self _state] == WebFrameStateProvisional) {
        NSURL *failedURL = [[_private->provisionalDataSource _originalRequest] URL];
        // When we are pre-commit, the currentItem is where the pageCache data resides
        NSDictionary *pageCache = [[_private currentItem] pageCache];
        [[self _bridge] didNotOpenURL:failedURL pageCache:pageCache];
664
665
        // We're assuming that WebCore invalidates its pageCache state in didNotOpen:pageCache:
        [[_private currentItem] setHasPageCache:NO];
tomernic's avatar
tomernic committed
666
667
668
669
670
671
672
673
        
        // Call -_clientRedirectCancelledOrFinished: here so that the frame load delegate is notified that the redirect's
        // status has changed, if there was a redirect.  The frame load delegate may have saved some state about
        // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:.  Since we are definitely
        // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
        // has ended.
        if (_private->sentRedirectNotification)
            [self _clientRedirectCancelledOrFinished:NO];
trey's avatar
trey committed
674
675
676
    }
}

mjs's avatar
mjs committed
677
- (void)_transitionToCommitted:(NSDictionary *)pageCache
678
679
{
    ASSERT([self webView] != nil);
ggaren's avatar
ggaren committed
680
    
681
682
683
    switch ([self _state]) {
        case WebFrameStateProvisional:
        {
darin's avatar
darin committed
684
            [[[[self frameView] _scrollView] contentView] setCopiesOnScroll:YES];
685
686
687
688

            WebFrameLoadType loadType = [self _loadType];
            if (loadType == WebFrameLoadTypeForward ||
                loadType == WebFrameLoadTypeBack ||
sullivan's avatar
WebKit:    
sullivan committed
689
690
                loadType == WebFrameLoadTypeIndexedBackForward ||
                (loadType == WebFrameLoadTypeReload && [_private->provisionalDataSource unreachableURL] != nil))
691
692
693
694
695
696
697
698
699
700
701
            {
                // Once committed, we want to use current item for saving DocState, and
                // the provisional item for restoring state.
                // Note previousItem must be set before we close the URL, which will
                // happen when the data source is made non-provisional below
                [_private setPreviousItem:[_private currentItem]];
                ASSERT([_private provisionalItem]);
                [_private setCurrentItem:[_private provisionalItem]];
                [_private setProvisionalItem:nil];
            }

ggaren's avatar
ggaren committed
702
703
704
705
706
707
708
709
            // The call to closeURL invokes the unload event handler, which can execute arbitrary
            // JavaScript. If the script initiates a new load, we need to abandon the current load,
            // or the two will stomp each other.
            WebDataSource *pd = _private->provisionalDataSource;
            [[self _bridge] closeURL];
            if (pd != _private->provisionalDataSource)
                return;

710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
            // Set the committed data source on the frame.
            [self _setDataSource:_private->provisionalDataSource];
                
            [self _setProvisionalDataSource: nil];

            [self _setState: WebFrameStateCommittedPage];
        
            // Handle adding the URL to the back/forward list.
            WebDataSource *ds = [self dataSource];
            NSString *ptitle = [ds pageTitle];

            switch (loadType) {
            case WebFrameLoadTypeForward:
            case WebFrameLoadTypeBack:
            case WebFrameLoadTypeIndexedBackForward:
                if ([[self webView] backForwardList]) {
                    // Must grab the current scroll position before disturbing it
727
                    [self _saveScrollPositionAndViewStateToItem:[_private previousItem]];
728
729
730
731
732
733
734
735
736
737
738
739
740
741
                    
                    // Create a document view for this document, or used the cached view.
                    if (pageCache){
                        NSView <WebDocumentView> *cachedView = [pageCache objectForKey: WebPageCacheDocumentViewKey];
                        ASSERT(cachedView != nil);
                        [[self frameView] _setDocumentView: cachedView];
                    }
                    else
                        [self _makeDocumentView];
                }
                break;

            case WebFrameLoadTypeReload:
            case WebFrameLoadTypeSame:
justing's avatar
justing committed
742
            case WebFrameLoadTypeReplace:
743
744
745
746
747
            {
                WebHistoryItem *currItem = [_private currentItem];
                LOG(PageCache, "Clearing back/forward cache, %@\n", [currItem URL]);
                [currItem setHasPageCache:NO];
                if (loadType == WebFrameLoadTypeReload) {
748
                    [self _saveScrollPositionAndViewStateToItem:currItem];
749
                }
750
751
752
753
754
                NSURLRequest *request = [ds request];
                if ([request _webDataRequestUnreachableURL] == nil) {
                    // Sometimes loading a page again leads to a different result because of cookies.  Bugzilla 4072
                    [currItem setURL:[request URL]];
                }
755
756
757
                // Update the last visited time.  Mostly interesting for URL autocompletion
                // statistics.
                NSURL *URL = [[[ds _originalRequest] URL] _webkit_canonicalize];
sullivan's avatar
sullivan committed
758
759
760
761
762
                WebHistory *sharedHistory = [WebHistory optionalSharedHistory];
                WebHistoryItem *oldItem = [sharedHistory itemForURL:URL];
                if (oldItem)
                    [sharedHistory setLastVisitedTimeInterval:[NSDate timeIntervalSinceReferenceDate] forItem:oldItem];
                
763
764
765
766
767
768
769
770
771
772
773
                [self _makeDocumentView];
                break;
            }

            // FIXME - just get rid of this case, and merge WebFrameLoadTypeReloadAllowingStaleData with the above case
            case WebFrameLoadTypeReloadAllowingStaleData:
                [self _makeDocumentView];
                break;
                
            case WebFrameLoadTypeStandard:
                if (![ds _isClientRedirect]) {
sullivan's avatar
WebKit:    
sullivan committed
774
775
776
                    // Add item to history and BF list
                    NSURL *URL = [ds _URLForHistory];
                    if (URL && ![URL _web_isEmpty]){
sullivan's avatar
sullivan committed
777
778
                        ASSERT([self webView]);
                        if (![[[self webView] preferences] privateBrowsingEnabled]) {
779
                            WebHistoryItem *entry = [[WebHistory optionalSharedHistory] addItemForURL:URL];
sullivan's avatar
WebKit:    
sullivan committed
780
781
782
                            if (ptitle)
                                [entry setTitle: ptitle];                            
                        }
783
                        [self _addBackForwardItemClippedAtTarget:YES];
darin's avatar
darin committed
784
                    }
785
786

                } else {
sullivan's avatar
sullivan committed
787
788
789
790
791
792
793
794
795
796
797
                    NSURLRequest *request = [ds request];
                    
                    // update the URL in the BF list that we made before the redirect, unless
                    // this is alternate content for an unreachable URL (we want the BF list
                    // item to remember the unreachable URL in case it becomes reachable later)
                    if ([request _webDataRequestUnreachableURL] == nil) {
                        [[_private currentItem] setURL:[request URL]];

                        // clear out the form data so we don't repost it to the wrong place if we
                        // ever go back/forward to this item
                        [[_private currentItem] _setFormInfoFromRequest:request];
798
799
800

                        // We must also clear out form data so we don't try to restore it into the incoming page,
                        // see -_opened
sullivan's avatar
sullivan committed
801
                    }
802
803
804
805
806
807
808
                }
                [self _makeDocumentView];
                break;
                
            case WebFrameLoadTypeInternal:
                // Add an item to the item tree for this frame
                ASSERT(![ds _isClientRedirect]);
809
810
811
812
813
                WebFrame *parentFrame = [self parentFrame];
                if (parentFrame) {
                    WebHistoryItem *parentItem = [parentFrame->_private currentItem];
                    // The only case where parentItem==nil should be when a parent frame loaded an
                    // empty URL, which doesn't set up a current item in that parent.
mjs's avatar
mjs committed
814
                    if (parentItem)
815
816
817
818
819
                        [parentItem addChildItem:[self _createItem: YES]];
                } else {
                    // See 3556159.  It's not clear if it's valid to be in WebFrameLoadTypeOnLoadEvent
                    // for a top-level frame, but that was a likely explanation for those crashes,
                    // so let's guard against it.
trey's avatar
trey committed
820
                    // ...and all WebFrameLoadTypeOnLoadEvent uses were folded to WebFrameLoadTypeInternal
mjs's avatar
mjs committed
821
                    LOG_ERROR("no parent frame in _transitionToCommitted:, loadType=%d", loadType);
822
823
824
825
826
827
828
829
830
831
832
833
834
835
                }
                [self _makeDocumentView];
                break;

            // FIXME Remove this check when dummy ds is removed.  An exception should be thrown
            // if we're in the WebFrameLoadTypeUninitialized state.
            default:
                ASSERT_NOT_REACHED();
            }

            
            // Tell the client we've committed this URL.
            ASSERT([[self frameView] documentView] != nil);
            [[self webView] _didCommitLoadForFrame: self];
836
            [[[self webView] _frameLoadDelegateForwarder] webView:[self webView] didCommitLoadForFrame:self];
837
838
839
            
            // If we have a title let the WebView know about it.
            if (ptitle) {
840
                [[[self webView] _frameLoadDelegateForwarder] webView:[self webView]
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
                                                           didReceiveTitle:ptitle
                                                                  forFrame:self];
            }
            break;
        }
        
        case WebFrameStateCommittedPage:
        case WebFrameStateComplete:
        default:
        {
            ASSERT_NOT_REACHED();
        }
    }
}

mjs's avatar
mjs committed
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
- (void)_commitProvisionalLoad:(NSDictionary *)pageCache
{
    WebFrameLoadType loadType = [self _loadType];
    bool reload = loadType == WebFrameLoadTypeReload || loadType == WebFrameLoadTypeReloadAllowingStaleData;
    
    WebDataSource *provisionalDataSource = [self provisionalDataSource];
    NSURLResponse *response = [provisionalDataSource response];

    NSDictionary *headers = [response isKindOfClass:[NSHTTPURLResponse class]]
        ? [(NSHTTPURLResponse *)response allHeaderFields] : nil;
    
    if (loadType != WebFrameLoadTypeReplace)
        [self _closeOldDataSources];
    
    if (!pageCache)
        [provisionalDataSource _makeRepresentation];
    
    [self _transitionToCommitted:pageCache];
tomernic's avatar
tomernic committed
874

tomernic's avatar
tomernic committed
875
    // Call -_clientRedirectCancelledOrFinished: here so that the frame load delegate is notified that the redirect's
tomernic's avatar
tomernic committed
876
877
878
    // status has changed, if there was a redirect.  The frame load delegate may have saved some state about
    // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:.  Since we are
    // just about to commit a new page, there cannot possibly be a pending redirect at this point.
tomernic's avatar
tomernic committed
879
880
    if (_private->sentRedirectNotification)
        [self _clientRedirectCancelledOrFinished:NO];
mjs's avatar
mjs committed
881
882
883
    
    NSURL *baseURL = [[provisionalDataSource request] _webDataRequestBaseURL];        
    NSURL *URL = baseURL ? baseURL : [response URL];
ggaren's avatar
ggaren committed
884
885
886
887

    if (!URL || [URL _web_isEmpty])
        URL = [NSURL URLWithString:@"about:blank"];    

mjs's avatar
mjs committed
888
889
890
891
892
893
894
895
896
897
    [[self _bridge] openURL:URL
                    reload:reload 
                    contentType:[response MIMEType]
                    refresh:[headers objectForKey:@"Refresh"]
                    lastModified:(pageCache ? nil : WKGetNSURLResponseLastModifiedDate(response))
                    pageCache:pageCache];
    
    [self _opened];
}

898
899
900
901
902
903
904
905
- (BOOL)_canCachePage
{
    return [[[self webView] backForwardList] _usesPageCache];
}

- (void)_purgePageCache
{
    // This method implements the rule for purging the page cache.
906
907
    unsigned sizeLimit = [[[self webView] backForwardList] pageCacheSize];
    unsigned pagesCached = 0;
908
909
    WebBackForwardList *backForwardList = [[self webView] backForwardList];
    NSArray *backList = [backForwardList backListWithLimit: 999999];
mjs's avatar
mjs committed
910
    WebHistoryItem *oldestNonSnapbackItem = nil;
911
912
913
    
    unsigned i;
    for (i = 0; i < [backList count]; i++){
914
915
        WebHistoryItem *item = [backList objectAtIndex: i];
        if ([item hasPageCache]){
mjs's avatar
mjs committed
916
917
            if (oldestNonSnapbackItem == nil && ![item alwaysAttemptToUsePageCache])
                oldestNonSnapbackItem = item;
918
            pagesCached++;
919
920
        }
    }
mjs's avatar
mjs committed
921

922
    // Snapback items are never directly purged here.
mjs's avatar
mjs committed
923
924
925
    if (pagesCached >= sizeLimit) {
        LOG(PageCache, "Purging back/forward cache, %@\n", [oldestNonSnapbackItem URL]);
        [oldestNonSnapbackItem setHasPageCache:NO];
926
    }
927
928
929
930
931
932
933
934
935
936
937
938
939
}

- (WebFrameState)_state
{
    return _private->state;
}

static CFAbsoluteTime _timeOfLastCompletedLoad;
+ (CFAbsoluteTime)_timeOfLastCompletedLoad
{
    return _timeOfLastCompletedLoad;
}

rjw's avatar
WebKit:    
rjw committed
940
- (BOOL)_createPageCacheForItem:(WebHistoryItem *)item
941
942
943
944
{
    NSMutableDictionary *pageCache;

    [item setHasPageCache: YES];
rjw's avatar
WebKit:    
rjw committed
945
946
947
948
949
950
951

    if (![_private->bridge saveDocumentToPageCache]){
        [item setHasPageCache: NO];
        return NO;
    }
    else {
        pageCache = [item pageCache];
mjs's avatar
mjs committed
952
953
954
        [pageCache setObject:[NSDate date]  forKey: WebPageCacheEntryDateKey];
        [pageCache setObject:[self dataSource] forKey: WebPageCacheDataSourceKey];
        [pageCache setObject:[[self frameView] documentView] forKey: WebPageCacheDocumentViewKey];
rjw's avatar
WebKit:    
rjw committed
955
956
    }
    return YES;
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
}

- (void)_setState: (WebFrameState)newState
{
    LOG(Loading, "%@:  transition from %s to %s", [self name], stateNames[_private->state], stateNames[newState]);
    if ([self webView])
        LOG(Timing, "%@:  transition from %s to %s, %f seconds since start of document load", [self name], stateNames[_private->state], stateNames[newState], CFAbsoluteTimeGetCurrent() - [[[[self webView] mainFrame] dataSource] _loadingStartedTime]);
    
    if (newState == WebFrameStateComplete && self == [[self webView] mainFrame]){
        LOG(DocumentLoad, "completed %@ (%f seconds)", [[[self dataSource] request] URL], CFAbsoluteTimeGetCurrent() - [[self dataSource] _loadingStartedTime]);
    }
    
    _private->state = newState;
    
    if (_private->state == WebFrameStateProvisional) {
ggaren's avatar
ggaren committed
972
        _private->firstLayoutDone = NO;
darin's avatar
darin committed
973
        [_private->bridge provisionalLoadStarted];
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
    
        // FIXME: This is OK as long as no one resizes the window,
        // but in the case where someone does, it means garbage outside
        // the occupied part of the scroll view.
        [[[self frameView] _scrollView] setDrawsBackground:NO];

        // Cache the page, if possible.
        // Don't write to the cache if in the middle of a redirect, since we will want to
        // store the final page we end up on.
        // No point writing to the cache on a reload or loadSame, since we will just write
        // over it again when we leave that page.
        WebHistoryItem *item = [_private currentItem];
        WebFrameLoadType loadType = [self _loadType];
        if ([self _canCachePage]
            && [_private->bridge canCachePage]
            && item
            && !_private->quickRedirectComing
            && loadType != WebFrameLoadTypeReload 
            && loadType != WebFrameLoadTypeReloadAllowingStaleData
            && loadType != WebFrameLoadTypeSame
            && ![[self dataSource] isLoading]
rjw's avatar
WebKit:    
rjw committed
995
            && ![[self dataSource] _isStopping])
996
        {
darin's avatar
darin committed
997
998
999
1000
            if ([[[self dataSource] representation] isKindOfClass: [WebHTMLRepresentation class]]) {
                if (![item pageCache]){

                    // Add the items to this page's cache.
For faster browsing, not all history is shown. View entire blame