blob: db1d7239890879e94b033e5f917802248da8d06f [file] [log] [blame]
/*
* Copyright (C) 2013 Apple 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
*/
WI.Frame = class Frame extends WI.Object
{
constructor(id, name, securityOrigin, loaderIdentifier, mainResource)
{
super();
console.assert(id);
this._id = id;
this._name = null;
this._securityOrigin = null;
this._resourceCollection = new WI.ResourceCollection;
this._provisionalResourceCollection = new WI.ResourceCollection;
this._extraScriptCollection = new WI.ScriptCollection;
this._childFrameCollection = new WI.FrameCollection;
this._childFrameIdentifierMap = new Map;
this._parentFrame = null;
this._isMainFrame = false;
this._domContentReadyEventTimestamp = NaN;
this._loadEventTimestamp = NaN;
this._executionContextList = new WI.ExecutionContextList;
this.initialize(name, securityOrigin, loaderIdentifier, mainResource);
}
// Public
get resourceCollection() { return this._resourceCollection; }
get extraScriptCollection() { return this._extraScriptCollection; }
get childFrameCollection() { return this._childFrameCollection; }
initialize(name, securityOrigin, loaderIdentifier, mainResource)
{
console.assert(loaderIdentifier);
console.assert(mainResource);
var oldName = this._name;
var oldSecurityOrigin = this._securityOrigin;
var oldMainResource = this._mainResource;
this._name = name || null;
this._securityOrigin = securityOrigin || null;
this._loaderIdentifier = loaderIdentifier || null;
this._mainResource = mainResource;
this._mainResource._parentFrame = this;
if (oldMainResource && this._mainResource !== oldMainResource)
this._disassociateWithResource(oldMainResource);
this.removeAllResources();
this.removeAllChildFrames();
this.clearExecutionContexts();
this.clearProvisionalLoad();
if (this._mainResource !== oldMainResource)
this._dispatchMainResourceDidChangeEvent(oldMainResource);
if (this._securityOrigin !== oldSecurityOrigin)
this.dispatchEventToListeners(WI.Frame.Event.SecurityOriginDidChange, {oldSecurityOrigin});
if (this._name !== oldName)
this.dispatchEventToListeners(WI.Frame.Event.NameDidChange, {oldName});
}
startProvisionalLoad(provisionalMainResource)
{
console.assert(provisionalMainResource);
this._provisionalMainResource = provisionalMainResource;
this._provisionalMainResource._parentFrame = this;
this._provisionalLoaderIdentifier = provisionalMainResource.loaderIdentifier;
this._provisionalResourceCollection.clear();
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalLoadStarted);
}
commitProvisionalLoad(securityOrigin)
{
console.assert(this._provisionalMainResource);
console.assert(this._provisionalLoaderIdentifier);
if (!this._provisionalLoaderIdentifier)
return;
var oldSecurityOrigin = this._securityOrigin;
var oldMainResource = this._mainResource;
this._securityOrigin = securityOrigin || null;
this._loaderIdentifier = this._provisionalLoaderIdentifier;
this._mainResource = this._provisionalMainResource;
this._domContentReadyEventTimestamp = NaN;
this._loadEventTimestamp = NaN;
if (oldMainResource && this._mainResource !== oldMainResource)
this._disassociateWithResource(oldMainResource);
this.removeAllResources();
this._resourceCollection = this._provisionalResourceCollection;
this._provisionalResourceCollection = new WI.ResourceCollection;
this._extraScriptCollection.clear();
this.clearExecutionContexts(true);
this.clearProvisionalLoad(true);
this.removeAllChildFrames();
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalLoadCommitted);
if (this._mainResource !== oldMainResource)
this._dispatchMainResourceDidChangeEvent(oldMainResource);
if (this._securityOrigin !== oldSecurityOrigin)
this.dispatchEventToListeners(WI.Frame.Event.SecurityOriginDidChange, {oldSecurityOrigin});
}
clearProvisionalLoad(skipProvisionalLoadClearedEvent)
{
if (!this._provisionalLoaderIdentifier)
return;
this._provisionalLoaderIdentifier = null;
this._provisionalMainResource = null;
this._provisionalResourceCollection.clear();
if (!skipProvisionalLoadClearedEvent)
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalLoadCleared);
}
get id()
{
return this._id;
}
get loaderIdentifier()
{
return this._loaderIdentifier;
}
get provisionalLoaderIdentifier()
{
return this._provisionalLoaderIdentifier;
}
get name()
{
return this._name;
}
get securityOrigin()
{
return this._securityOrigin;
}
get url()
{
return this._mainResource.url;
}
get urlComponents()
{
return this._mainResource.urlComponents;
}
get domTree()
{
if (!this._domTree)
this._domTree = new WI.DOMTree(this);
return this._domTree;
}
get pageExecutionContext()
{
return this._executionContextList.pageExecutionContext;
}
get executionContextList()
{
return this._executionContextList;
}
clearExecutionContexts(committingProvisionalLoad)
{
if (this._executionContextList.contexts.length) {
let contexts = this._executionContextList.contexts.slice();
this._executionContextList.clear();
this.dispatchEventToListeners(WI.Frame.Event.ExecutionContextsCleared, {committingProvisionalLoad: !!committingProvisionalLoad, contexts});
}
}
addExecutionContext(context)
{
this._executionContextList.add(context);
this.dispatchEventToListeners(WI.Frame.Event.ExecutionContextAdded, {context});
if (this._executionContextList.pageExecutionContext === context)
this.dispatchEventToListeners(WI.Frame.Event.PageExecutionContextChanged);
}
get mainResource()
{
return this._mainResource;
}
get provisionalMainResource()
{
return this._provisionalMainResource;
}
get parentFrame()
{
return this._parentFrame;
}
get domContentReadyEventTimestamp()
{
return this._domContentReadyEventTimestamp;
}
get loadEventTimestamp()
{
return this._loadEventTimestamp;
}
isMainFrame()
{
return this._isMainFrame;
}
markAsMainFrame()
{
this._isMainFrame = true;
}
unmarkAsMainFrame()
{
this._isMainFrame = false;
}
markDOMContentReadyEvent(timestamp)
{
console.assert(isNaN(this._domContentReadyEventTimestamp));
this._domContentReadyEventTimestamp = timestamp || NaN;
}
markLoadEvent(timestamp)
{
console.assert(isNaN(this._loadEventTimestamp));
this._loadEventTimestamp = timestamp || NaN;
}
isDetached()
{
var frame = this;
while (frame) {
if (frame.isMainFrame())
return false;
frame = frame.parentFrame;
}
return true;
}
childFrameForIdentifier(frameId)
{
return this._childFrameIdentifierMap.get(frameId) || null;
}
addChildFrame(frame)
{
console.assert(frame instanceof WI.Frame);
if (!(frame instanceof WI.Frame))
return;
if (frame._parentFrame === this)
return;
if (frame._parentFrame)
frame._parentFrame.removeChildFrame(frame);
this._childFrameCollection.add(frame);
this._childFrameIdentifierMap.set(frame._id, frame);
frame._parentFrame = this;
this.dispatchEventToListeners(WI.Frame.Event.ChildFrameWasAdded, {childFrame: frame});
}
removeChildFrame(frameOrFrameId)
{
console.assert(frameOrFrameId);
let childFrameId = frameOrFrameId;
if (childFrameId instanceof WI.Frame)
childFrameId = frameOrFrameId._id;
// Fetch the frame by id even if we were passed a WI.Frame.
// We do this incase the WI.Frame is a new object that isn't
// in _childFrameCollection, but the id is a valid child frame.
let childFrame = this.childFrameForIdentifier(childFrameId);
console.assert(childFrame instanceof WI.Frame);
if (!(childFrame instanceof WI.Frame))
return;
console.assert(childFrame.parentFrame === this);
this._childFrameCollection.remove(childFrame);
this._childFrameIdentifierMap.delete(childFrame._id);
childFrame._detachFromParentFrame();
this.dispatchEventToListeners(WI.Frame.Event.ChildFrameWasRemoved, {childFrame});
}
removeAllChildFrames()
{
this._detachFromParentFrame();
for (let childFrame of this._childFrameCollection)
childFrame.removeAllChildFrames();
this._childFrameCollection.clear();
this._childFrameIdentifierMap.clear();
this.dispatchEventToListeners(WI.Frame.Event.AllChildFramesRemoved);
}
resourceForURL(url, recursivelySearchChildFrames)
{
var resource = this._resourceCollection.resourceForURL(url);
if (resource)
return resource;
// Check the main resources of the child frames for the requested URL.
for (let childFrame of this._childFrameCollection) {
resource = childFrame.mainResource;
if (resource.url === url)
return resource;
}
if (!recursivelySearchChildFrames)
return null;
// Recursively search resources of child frames.
for (let childFrame of this._childFrameCollection) {
resource = childFrame.resourceForURL(url, true);
if (resource)
return resource;
}
return null;
}
resourceCollectionForType(type)
{
return this._resourceCollection.resourceCollectionForType(type);
}
addResource(resource)
{
console.assert(resource instanceof WI.Resource);
if (!(resource instanceof WI.Resource))
return;
if (resource.parentFrame === this)
return;
if (resource.parentFrame)
resource.parentFrame.remove(resource);
this._associateWithResource(resource);
if (this._isProvisionalResource(resource)) {
this._provisionalResourceCollection.add(resource);
this.dispatchEventToListeners(WI.Frame.Event.ProvisionalResourceWasAdded, {resource});
} else {
this._resourceCollection.add(resource);
this.dispatchEventToListeners(WI.Frame.Event.ResourceWasAdded, {resource});
}
}
removeResource(resource)
{
// This does not remove provisional resources.
this._resourceCollection.remove(resource);
this._disassociateWithResource(resource);
this.dispatchEventToListeners(WI.Frame.Event.ResourceWasRemoved, {resource});
}
removeAllResources()
{
// This does not remove provisional resources, use clearProvisionalLoad for that.
if (!this._resourceCollection.size)
return;
for (let resource of this._resourceCollection)
this._disassociateWithResource(resource);
this._resourceCollection.clear();
this.dispatchEventToListeners(WI.Frame.Event.AllResourcesRemoved);
}
addExtraScript(script)
{
this._extraScriptCollection.add(script);
this.dispatchEventToListeners(WI.Frame.Event.ExtraScriptAdded, {script});
}
saveIdentityToCookie(cookie)
{
cookie[WI.Frame.MainResourceURLCookieKey] = this.mainResource.url.hash;
cookie[WI.Frame.IsMainFrameCookieKey] = this._isMainFrame;
}
// Private
_detachFromParentFrame()
{
if (this._domTree) {
this._domTree.disconnect();
this._domTree = null;
}
this._parentFrame = null;
}
_isProvisionalResource(resource)
{
return resource.loaderIdentifier && this._provisionalLoaderIdentifier && resource.loaderIdentifier === this._provisionalLoaderIdentifier;
}
_associateWithResource(resource)
{
console.assert(!resource._parentFrame);
if (resource._parentFrame)
return;
resource._parentFrame = this;
}
_disassociateWithResource(resource)
{
console.assert(resource.parentFrame === this);
if (resource.parentFrame !== this)
return;
resource._parentFrame = null;
}
_dispatchMainResourceDidChangeEvent(oldMainResource)
{
this.dispatchEventToListeners(WI.Frame.Event.MainResourceDidChange, {oldMainResource});
}
};
WI.Frame.Event = {
NameDidChange: "frame-name-did-change",
SecurityOriginDidChange: "frame-security-origin-did-change",
MainResourceDidChange: "frame-main-resource-did-change",
ProvisionalLoadStarted: "frame-provisional-load-started",
ProvisionalLoadCommitted: "frame-provisional-load-committed",
ProvisionalLoadCleared: "frame-provisional-load-cleared",
ProvisionalResourceWasAdded: "frame-provisional-resource-was-added",
ResourceWasAdded: "frame-resource-was-added",
ResourceWasRemoved: "frame-resource-was-removed",
AllResourcesRemoved: "frame-all-resources-removed",
ExtraScriptAdded: "frame-extra-script-added",
ChildFrameWasAdded: "frame-child-frame-was-added",
ChildFrameWasRemoved: "frame-child-frame-was-removed",
AllChildFramesRemoved: "frame-all-child-frames-removed",
PageExecutionContextChanged: "frame-page-execution-context-changed",
ExecutionContextAdded: "frame-execution-context-added",
ExecutionContextsCleared: "frame-execution-contexts-cleared"
};
WI.Frame.TypeIdentifier = "Frame";
WI.Frame.MainResourceURLCookieKey = "frame-main-resource-url";
WI.Frame.IsMainFrameCookieKey = "frame-is-main-frame";