blob: dc23e67c6f6c18ce488ec6e7cd37e21bfaea56f4 [file] [log] [blame]
/*
* Copyright (C) 2012 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.
*/
#include "config.h"
#if WK_HAVE_C_SPI
#include "InjectedBundleTest.h"
#include <WebKit/WKBundleDOMWindowExtension.h>
#include <WebKit/WKBundleFrame.h>
#include <WebKit/WKBundlePage.h>
#include <WebKit/WKBundlePageGroup.h>
#include <WebKit/WKBundlePrivate.h>
#include <WebKit/WKBundleScriptWorld.h>
#include <WebKit/WKRetainPtr.h>
#include <wtf/HashMap.h>
#include <assert.h>
namespace TestWebKitAPI {
static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void* clientInfo);
static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void* clientInfo);
static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo);
enum ExtensionState {
Uncreated = 0, Connected, Disconnected, Destroyed, Removed
};
const char* states[5] = {
"Uncreated",
"Connected",
"Disconnected",
"Destroyed",
"Removed"
};
typedef struct {
const char* name;
ExtensionState state;
} ExtensionRecord;
class DOMWindowExtensionNoCache : public InjectedBundleTest {
public:
DOMWindowExtensionNoCache(const std::string& identifier);
virtual void initialize(WKBundleRef, WKTypeRef userData);
virtual void didCreatePage(WKBundleRef, WKBundlePageRef);
virtual void willDestroyPage(WKBundleRef, WKBundlePageRef);
void globalObjectIsAvailableForFrame(WKBundleFrameRef, WKBundleScriptWorldRef);
void willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef);
void didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef);
void willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef);
void frameLoadFinished(WKBundleFrameRef);
private:
void updateExtensionStateRecord(WKBundleDOMWindowExtensionRef, ExtensionState);
void sendExtensionStateMessage();
void sendBundleMessage(const char*);
WKBundleRef m_bundle;
ExtensionRecord m_extensionRecords[10];
HashMap<WKBundleDOMWindowExtensionRef, int> m_extensionToRecordMap;
int m_numberMainFrameLoads;
};
static InjectedBundleTest::Register<DOMWindowExtensionNoCache> registrar("DOMWindowExtensionNoCache");
DOMWindowExtensionNoCache::DOMWindowExtensionNoCache(const std::string& identifier)
: InjectedBundleTest(identifier)
, m_numberMainFrameLoads(0)
{
m_extensionRecords[0].name = "First page, main frame, standard world";
m_extensionRecords[1].name = "First page, main frame, non-standard world";
m_extensionRecords[2].name = "First page, subframe, standard world";
m_extensionRecords[3].name = "First page, subframe, non-standard world";
m_extensionRecords[4].name = "Second page, main frame, standard world";
m_extensionRecords[5].name = "Second page, main frame, non-standard world";
m_extensionRecords[6].name = "First page, main frame, standard world";
m_extensionRecords[7].name = "First page, main frame, non-standard world";
m_extensionRecords[8].name = "First page, subframe, standard world";
m_extensionRecords[9].name = "First page, subframe, non-standard world";
for (size_t i = 0; i < 10; ++i)
m_extensionRecords[i].state = Uncreated;
}
void DOMWindowExtensionNoCache::frameLoadFinished(WKBundleFrameRef frame)
{
bool mainFrame = !WKBundleFrameGetParentFrame(frame);
if (mainFrame)
m_numberMainFrameLoads++;
char body[16384];
sprintf(body, "%s finished loading", mainFrame ? "Main frame" : "Subframe");
// Only consider load finished for the main frame
const char* name = mainFrame ? "DidFinishLoadForMainFrame" : "DidFinishLoadForFrame";
WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString(name));
WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body));
WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get());
sendExtensionStateMessage();
}
void DOMWindowExtensionNoCache::sendExtensionStateMessage()
{
char body[16384];
sprintf(body, "Extension states:\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s",
m_extensionRecords[0].name, states[m_extensionRecords[0].state],
m_extensionRecords[1].name, states[m_extensionRecords[1].state],
m_extensionRecords[2].name, states[m_extensionRecords[2].state],
m_extensionRecords[3].name, states[m_extensionRecords[3].state],
m_extensionRecords[4].name, states[m_extensionRecords[4].state],
m_extensionRecords[5].name, states[m_extensionRecords[5].state],
m_extensionRecords[6].name, states[m_extensionRecords[6].state],
m_extensionRecords[7].name, states[m_extensionRecords[7].state],
m_extensionRecords[8].name, states[m_extensionRecords[8].state],
m_extensionRecords[9].name, states[m_extensionRecords[9].state]);
WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("ExtensionStates"));
WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body));
WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get());
}
void DOMWindowExtensionNoCache::initialize(WKBundleRef bundle, WKTypeRef userData)
{
assert(WKGetTypeID(userData) == WKBundlePageGroupGetTypeID());
WKBundlePageGroupRef pageGroup = static_cast<WKBundlePageGroupRef>(userData);
WKRetainPtr<WKStringRef> source = adoptWK(WKStringCreateWithUTF8CString("alert('Unimportant alert');"));
WKBundleAddUserScript(bundle, pageGroup, WKBundleScriptWorldCreateWorld(), source.get(), 0, 0, 0, kWKInjectAtDocumentStart, kWKInjectInAllFrames);
}
void DOMWindowExtensionNoCache::didCreatePage(WKBundleRef bundle, WKBundlePageRef page)
{
m_bundle = bundle;
WKBundlePageLoaderClientV7 pageLoaderClient;
memset(&pageLoaderClient, 0, sizeof(pageLoaderClient));
pageLoaderClient.base.version = 7;
pageLoaderClient.base.clientInfo = this;
pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrameCallback;
pageLoaderClient.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrameCallback;
pageLoaderClient.willDisconnectDOMWindowExtensionFromGlobalObject = willDisconnectDOMWindowExtensionFromGlobalObjectCallback;
pageLoaderClient.didReconnectDOMWindowExtensionToGlobalObject = didReconnectDOMWindowExtensionToGlobalObjectCallback;
pageLoaderClient.willDestroyGlobalObjectForDOMWindowExtension = willDestroyGlobalObjectForDOMWindowExtensionCallback;
WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base);
}
void DOMWindowExtensionNoCache::willDestroyPage(WKBundleRef, WKBundlePageRef)
{
HashMap<WKBundleDOMWindowExtensionRef, int>::iterator it = m_extensionToRecordMap.begin();
HashMap<WKBundleDOMWindowExtensionRef, int>::iterator end = m_extensionToRecordMap.end();
for (; it != end; ++it) {
updateExtensionStateRecord(it->key, Removed);
WKRelease(it->key);
}
m_extensionToRecordMap.clear();
sendExtensionStateMessage();
sendBundleMessage("TestComplete");
}
void DOMWindowExtensionNoCache::updateExtensionStateRecord(WKBundleDOMWindowExtensionRef extension, ExtensionState state)
{
int index = m_extensionToRecordMap.get(extension);
m_extensionRecords[index].state = state;
}
void DOMWindowExtensionNoCache::sendBundleMessage(const char* message)
{
WKRetainPtr<WKStringRef> wkMessage = adoptWK(WKStringCreateWithUTF8CString(message));
WKBundlePostMessage(m_bundle, wkMessage.get(), wkMessage.get());
}
void DOMWindowExtensionNoCache::globalObjectIsAvailableForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
{
WKBundleDOMWindowExtensionRef extension = WKBundleDOMWindowExtensionCreate(frame, world);
int index = 0;
bool standard;
standard = world == WKBundleScriptWorldNormalWorld();
bool mainFrame = !WKBundleFrameGetParentFrame(frame);
switch (m_numberMainFrameLoads) {
case 0:
index = mainFrame ? (standard ? 0 : 1) : (standard ? 2 : 3);
break;
case 1:
index = standard ? 4 : 5;
break;
case 2:
index = mainFrame ? (standard ? 6 : 7) : (standard ? 8 : 9);
break;
default:
ASSERT_NOT_REACHED();
break;
}
m_extensionToRecordMap.set(extension, index);
updateExtensionStateRecord(extension, Connected);
sendBundleMessage("GlobalObjectIsAvailableForFrame called");
}
void DOMWindowExtensionNoCache::willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef extension)
{
// No items should be going into a 0-capacity back/forward cache.
ASSERT_NOT_REACHED();
}
void DOMWindowExtensionNoCache::didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef)
{
// No items should be coming out of a 0-capacity back/forward cache.
ASSERT_NOT_REACHED();
}
void DOMWindowExtensionNoCache::willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef extension)
{
sendBundleMessage("WillDestroyDOMWindowExtensionToGlobalObject called");
updateExtensionStateRecord(extension, Destroyed);
m_extensionToRecordMap.remove(extension);
WKRelease(extension);
}
static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
{
((DOMWindowExtensionNoCache*)clientInfo)->frameLoadFinished(frame);
}
static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo)
{
((DOMWindowExtensionNoCache*)clientInfo)->globalObjectIsAvailableForFrame(frame, world);
}
static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo)
{
((DOMWindowExtensionNoCache*)clientInfo)->willDisconnectDOMWindowExtensionFromGlobalObject(extension);
}
static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo)
{
((DOMWindowExtensionNoCache*)clientInfo)->didReconnectDOMWindowExtensionToGlobalObject(extension);
}
static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension , const void* clientInfo)
{
((DOMWindowExtensionNoCache*)clientInfo)->willDestroyGlobalObjectForDOMWindowExtension(extension);
}
} // namespace TestWebKitAPI
#endif