Modified: trunk/Source/WebKit/Platform/IPC/JSIPCBinding.h (268847 => 268848)
--- trunk/Source/WebKit/Platform/IPC/JSIPCBinding.h 2020-10-22 00:40:55 UTC (rev 268847)
+++ trunk/Source/WebKit/Platform/IPC/JSIPCBinding.h 2020-10-22 00:41:50 UTC (rev 268848)
@@ -38,6 +38,8 @@
#include <wtf/URL.h>
#include <wtf/text/WTFString.h>
+namespace IPC {
+
template<typename T, std::enable_if_t<!std::is_arithmetic<T>::value && !std::is_enum<T>::value>* = nullptr>
JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject*, const T&)
{
@@ -44,30 +46,35 @@
return JSC::jsUndefined();
}
-template<>
-JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const String& value)
+inline JSC::JSValue jsValueForDecodedStringArgumentValue(JSC::JSGlobalObject* globalObject, const String& value, ASCIILiteral type)
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype());
RETURN_IF_EXCEPTION(scope, JSC::JSValue());
- object->putDirect(vm, JSC::Identifier::fromString(vm, "type"_s), JSC::jsNontrivialString(vm, "String"_s));
+ object->putDirect(vm, JSC::Identifier::fromString(vm, "type"_s), JSC::jsNontrivialString(vm, type));
RETURN_IF_EXCEPTION(scope, JSC::JSValue());
- object->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), JSC::jsNontrivialString(vm, value));
+ object->putDirect(vm, JSC::Identifier::fromString(vm, "value"_s), value.isNull() ? JSC::jsNull() : JSC::jsString(vm, value));
RETURN_IF_EXCEPTION(scope, JSC::JSValue());
return object;
}
template<>
+JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const String& value)
+{
+ return jsValueForDecodedStringArgumentValue(globalObject, value, "String"_s);
+}
+
+template<>
JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const URL& value)
{
- return jsValueForDecodedArgumentValue(globalObject, value.string());
+ return jsValueForDecodedStringArgumentValue(globalObject, value.string(), "URL"_s);
}
template<>
JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const WebCore::RegistrableDomain& value)
{
- return jsValueForDecodedArgumentValue(globalObject, value.string());
+ return jsValueForDecodedStringArgumentValue(globalObject, value.string(), "RegistrableDomain"_s);
}
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value>* = nullptr>
@@ -207,6 +214,18 @@
return jsValueForDecodedArgumentRect(globalObject, value, "FloatRect");
}
+template<typename U>
+JSC::JSValue jsValueForDecodedArgumentValue(JSC::JSGlobalObject* globalObject, const OptionSet<U>& value)
+{
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ auto result = jsValueForDecodedArgumentValue(globalObject, value.toRaw());
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue());
+ result.getObject()->putDirect(vm, JSC::Identifier::fromString(vm, "isOptionSet"_s), JSC::jsBoolean(true));
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue());
+ return result;
+}
+
template<size_t remainingSize, typename... Elements>
struct DecodedArgumentJSValueConverter {
static bool convert(JSC::JSGlobalObject* globalObject, JSC::JSArray* array, const std::tuple<Elements...>& tuple)
@@ -253,3 +272,5 @@
return WTF::nullopt;
return jsValueForArgumentTuple(globalObject, *arguments);
}
+
+}
Modified: trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp (268847 => 268848)
--- trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp 2020-10-22 00:40:55 UTC (rev 268847)
+++ trunk/Source/WebKit/WebProcess/WebPage/IPCTestingAPI.cpp 2020-10-22 00:41:50 UTC (rev 268848)
@@ -54,8 +54,28 @@
namespace IPCTestingAPI {
-class JSIPC : public RefCounted<JSIPC> {
+class JSIPC;
+
+class JSMessageListener final : public IPC::Connection::MessageObserver {
+ WTF_MAKE_FAST_ALLOCATED;
public:
+ enum class Type { Incoming, Outgoing };
+
+ JSMessageListener(JSIPC&, Type, JSContextRef, JSObjectRef callback);
+
+private:
+ void willSendMessage(const IPC::Encoder&, OptionSet<IPC::SendOption>) override;
+ void didReceiveMessage(const IPC::Decoder&) override;
+ JSC::JSObject* jsDescriptionFromDecoder(JSC::JSGlobalObject*, IPC::Decoder&);
+
+ WeakPtr<JSIPC> m_jsIPC;
+ Type m_type;
+ JSContextRef m_context;
+ JSObjectRef m_callback;
+};
+
+class JSIPC : public RefCounted<JSIPC>, public CanMakeWeakPtr<JSIPC> {
+public:
static Ref<JSIPC> create(WebPage& webPage, WebFrame& webFrame)
{
return adoptRef(*new JSIPC(webPage, webFrame));
@@ -78,6 +98,10 @@
static const JSStaticFunction* staticFunctions();
static const JSStaticValue* staticValues();
+ static void addMessageListener(JSMessageListener::Type, JSContextRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+ static JSValueRef addIncomingMessageListener(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+ static JSValueRef addOutgoingMessageListener(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+
static JSValueRef sendMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
static JSValueRef sendSyncMessage(JSContextRef, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
@@ -92,6 +116,7 @@
WeakPtr<WebPage> m_webPage;
WeakPtr<WebFrame> m_webFrame;
+ Vector<UniqueRef<JSMessageListener>> m_messageListeners;
};
JSClassRef JSIPC::wrapperClass()
@@ -135,6 +160,8 @@
const JSStaticFunction* JSIPC::staticFunctions()
{
static const JSStaticFunction functions[] = {
+ { "addIncomingMessageListener", addIncomingMessageListener, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
+ { "addOutgoingMessageListener", addOutgoingMessageListener, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
{ "sendMessage", sendMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
{ "sendSyncMessage", sendSyncMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
{ 0, 0, 0 }
@@ -169,6 +196,12 @@
return WTF::nullopt;
}
+static JSValueRef createTypeError(JSContextRef context, const String& message)
+{
+ JSC::JSLockHolder lock(toJS(context)->vm());
+ return toRef(JSC::createTypeError(toJS(context), message));
+}
+
static RefPtr<IPC::Connection> processTargetFromArgument(JSC::JSGlobalObject* globalObject, JSValueRef valueRef, JSValueRef* exception)
{
auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
@@ -189,6 +222,53 @@
return nullptr;
}
+void JSIPC::addMessageListener(JSMessageListener::Type type, JSContextRef context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ auto* globalObject = toJS(context);
+ JSC::JSLockHolder lock(globalObject->vm());
+ auto jsIPC = makeRefPtr(toWrapped(context, thisObject));
+ if (!jsIPC) {
+ *exception = createTypeError(context, "Wrong type"_s);
+ return;
+ }
+
+ if (argumentCount < 1) {
+ *exception = createTypeError(context, "Must specify the target process as the first argument"_s);
+ return;
+ }
+
+ auto connection = processTargetFromArgument(globalObject, arguments[0], exception);
+ if (!connection)
+ return;
+
+ std::unique_ptr<JSMessageListener> listener;
+ if (argumentCount >= 2 && JSValueIsObject(context, arguments[1])) {
+ auto listenerObjectRef = JSValueToObject(context, arguments[1], exception);
+ if (JSObjectIsFunction(context, listenerObjectRef))
+ listener = makeUnique<JSMessageListener>(*jsIPC, type, context, listenerObjectRef);
+ }
+
+ if (!listener) {
+ *exception = createTypeError(context, "Must specify a callback function as the second argument"_s);
+ return;
+ }
+
+ connection->addMessageObserver(*listener);
+ jsIPC->m_messageListeners.append(makeUniqueRefFromNonNullUniquePtr(WTFMove(listener)));
+}
+
+JSValueRef JSIPC::addIncomingMessageListener(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ addMessageListener(JSMessageListener::Type::Incoming, context, thisObject, argumentCount, arguments, exception);
+ return JSValueMakeUndefined(context);
+}
+
+JSValueRef JSIPC::addOutgoingMessageListener(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ addMessageListener(JSMessageListener::Type::Outgoing, context, thisObject, argumentCount, arguments, exception);
+ return JSValueMakeUndefined(context);
+}
+
static Optional<uint64_t> destinationIDFromArgument(JSC::JSGlobalObject* globalObject, JSValueRef valueRef, JSValueRef* exception)
{
auto jsValue = toJS(globalObject, valueRef);
@@ -235,12 +315,6 @@
return true;
}
-static JSValueRef createTypeError(JSContextRef context, const String& message)
-{
- JSC::JSLockHolder lock(toJS(context)->vm());
- return toRef(JSC::createTypeError(toJS(context), message));
-}
-
template<typename PointType> bool encodePointType(IPC::Encoder& encoder, JSC::JSGlobalObject* globalObject, JSC::JSObject* jsObject, JSC::CatchScope& scope)
{
auto& vm = globalObject->vm();
@@ -804,6 +878,104 @@
return toRef(vm, messagesObject);
}
+JSMessageListener::JSMessageListener(JSIPC& jsIPC, Type type, JSContextRef context, JSObjectRef callback)
+ : m_jsIPC(makeWeakPtr(jsIPC))
+ , m_type(type)
+ , m_context(context)
+ , m_callback(callback)
+{
+ auto* globalObject = toJS(context);
+ auto& vm = globalObject->vm();
+ JSC::JSLockHolder lock(vm);
+
+ auto catchScope = DECLARE_CATCH_SCOPE(vm);
+
+ // We can't retain the global context here as that would cause a leak
+ // since this object is supposed to live as long as the global object is alive.
+ JSC::PrivateName uniquePrivateName;
+ globalObject->putDirect(vm, uniquePrivateName, toJS(globalObject, callback));
+}
+
+void JSMessageListener::didReceiveMessage(const IPC::Decoder& decoder)
+{
+ if (m_type != Type::Incoming)
+ return;
+
+ RELEASE_ASSERT(m_jsIPC);
+ auto protectOwnerOfThis = makeRef(*m_jsIPC);
+ auto* globalObject = toJS(m_context);
+ JSC::JSLockHolder lock(globalObject->vm());
+
+ auto mutableDecoder = IPC::Decoder::create(decoder.buffer(), decoder.length(), nullptr, { });
+ auto* description = jsDescriptionFromDecoder(globalObject, *mutableDecoder);
+
+ JSValueRef arguments[] = { description ? toRef(globalObject, description) : JSValueMakeUndefined(m_context) };
+ JSObjectCallAsFunction(m_context, m_callback, m_callback, std::size(arguments), arguments, nullptr);
+}
+
+void JSMessageListener::willSendMessage(const IPC::Encoder& encoder, OptionSet<IPC::SendOption>)
+{
+ if (m_type != Type::Outgoing)
+ return;
+
+ RELEASE_ASSERT(m_jsIPC);
+ auto protectOwnerOfThis = makeRef(*m_jsIPC);
+ auto* globalObject = toJS(m_context);
+ JSC::JSLockHolder lock(globalObject->vm());
+
+ auto decoder = IPC::Decoder::create(encoder.buffer(), encoder.bufferSize(), nullptr, { });
+ auto* description = jsDescriptionFromDecoder(globalObject, *decoder);
+
+ JSValueRef arguments[] = { description ? toRef(globalObject, description) : JSValueMakeUndefined(m_context) };
+ JSObjectCallAsFunction(m_context, m_callback, m_callback, WTF_ARRAY_LENGTH(arguments), arguments, nullptr);
+}
+
+JSC::JSObject* JSMessageListener::jsDescriptionFromDecoder(JSC::JSGlobalObject* globalObject, IPC::Decoder& decoder)
+{
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+
+ auto* jsResult = constructEmptyObject(globalObject, globalObject->objectPrototype());
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
+ jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "name"), JSC::JSValue(static_cast<unsigned>(decoder.messageName())));
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
+ jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "description"), JSC::jsString(vm, IPC::description(decoder.messageName())));
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
+ jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "destinationID"), JSC::JSValue(decoder.destinationID()));
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
+ if (decoder.isSyncMessage()) {
+ if (uint64_t syncRequestID = 0; decoder.decode(syncRequestID)) {
+ jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "syncRequestID"), JSC::JSValue(syncRequestID));
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ }
+ } else if (messageReplyArgumentDescriptions(decoder.messageName())) {
+ if (uint64_t listenerID = 0; decoder.decode(listenerID)) {
+ jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "listenerID"), JSC::JSValue(listenerID));
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ }
+ }
+
+ auto arrayBuffer = JSC::ArrayBuffer::create(decoder.buffer(), decoder.length());
+ if (auto* structure = globalObject->arrayBufferStructure(arrayBuffer->sharingMode())) {
+ if (auto* jsArrayBuffer = JSC::JSArrayBuffer::create(vm, structure, WTFMove(arrayBuffer))) {
+ jsResult->putDirect(vm, JSC::Identifier::fromString(vm, "buffer"), jsArrayBuffer);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ }
+ }
+
+ auto jsReplyArguments = jsValueForArguments(globalObject, decoder.messageName(), decoder);
+ if (jsReplyArguments) {
+ jsResult->putDirect(vm, vm.propertyNames->arguments, jsReplyArguments->isEmpty() ? JSC::jsNull() : *jsReplyArguments);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ }
+
+ return jsResult;
+}
+
void inject(WebPage& webPage, WebFrame& webFrame, WebCore::DOMWrapperWorld& world)
{
auto* globalObject = webFrame.coreFrame()->script().globalObject(world);
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm (268847 => 268848)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm 2020-10-22 00:40:55 UTC (rev 268847)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm 2020-10-22 00:41:50 UTC (rev 268848)
@@ -208,4 +208,89 @@
EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"args[2].type"] UTF8String], "String");
}
+TEST(IPCTestingAPI, CanInterceptAlert)
+{
+ auto webView = createWebViewWithIPCTestingAPI();
+
+ auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
+ [webView setUIDelegate:delegate.get()];
+
+ done = false;
+ [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>messages = []; IPC.addOutgoingMessageListener('UI', (message) => messages.push(message)); alert('ok');</script>"];
+ TestWebKitAPI::Util::run(&done);
+
+ EXPECT_STREQ([alertMessage UTF8String], "ok");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages = messages.filter((message) => message.name == IPC.messages.WebPageProxy_RunJavaScriptAlert.name); messages.length"].UTF8String, "1");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages[0].description"].UTF8String, "WebPageProxy_RunJavaScriptAlert");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].arguments.length"].intValue, 3);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(messages[0].syncRequestID)"].UTF8String, "number");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].destinationID"].intValue,
+ [webView stringByEvaluatingJavaScript:@"IPC.webPageProxyID.toString()"].intValue);
+}
+
+#if ENABLE(RESOURCE_LOAD_STATISTICS)
+TEST(IPCTestingAPI, CanInterceptHasStorageAccess)
+{
+ auto webView = createWebViewWithIPCTestingAPI();
+
+ auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
+ [webView setUIDelegate:delegate.get()];
+
+ done = false;
+ promptResult = @"foo";
+ [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>let targetMessage = {}; const messageName = IPC.messages.NetworkConnectionToWebProcess_HasStorageAccess.name;"
+ "IPC.addOutgoingMessageListener('Networking', (currentMessage) => { if (currentMessage.name == messageName) targetMessage = currentMessage; });"
+ "IPC.sendMessage('Networking', 0, messageName, [{type: 'RegistrableDomain', value: 'https://ipctestingapi.com'}, {type: 'RegistrableDomain', value: 'https://webkit.org'},"
+ "{type: 'uint64_t', value: IPC.frameID}, {type: 'uint64_t', value: IPC.pageID}]).then((result) => alert(JSON.stringify(result.arguments)));</script>"];
+ TestWebKitAPI::Util::run(&done);
+
+ EXPECT_STREQ([alertMessage UTF8String], "[{\"type\":\"bool\",\"value\":false}]");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.description"].UTF8String, "NetworkConnectionToWebProcess_HasStorageAccess");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments.length"].intValue, 4);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[0].type"].UTF8String, "RegistrableDomain");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[0].value"].UTF8String, "ipctestingapi.com");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[1].type"].UTF8String, "RegistrableDomain");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[1].value"].UTF8String, "webkit.org");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[2].type"].UTF8String, "uint64_t");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[2].value"].UTF8String, [webView stringByEvaluatingJavaScript:@"IPC.frameID.toString()"].UTF8String);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[3].type"].UTF8String, "uint64_t");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[3].value"].intValue, [webView stringByEvaluatingJavaScript:@"IPC.pageID.toString()"].intValue);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(targetMessage.syncRequestID)"].UTF8String, "undefined");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.destinationID"].intValue, 0);
+}
#endif
+
+TEST(IPCTestingAPI, CanInterceptFindString)
+{
+ auto webView = createWebViewWithIPCTestingAPI();
+
+ auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
+ [webView setUIDelegate:delegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><body><p>hello</p><script>messages = []; IPC.addIncomingMessageListener('UI', (message) => messages.push(message));</script>"];
+
+ done = false;
+ auto findConfiguration = adoptNS([[WKFindConfiguration alloc] init]);
+ [webView findString:@"hello" withConfiguration:findConfiguration.get() completionHandler:^(WKFindResult *result) {
+ EXPECT_TRUE(result.matchFound);
+ EXPECT_TRUE([webView selectionRangeHasStartOffset:0 endOffset:5]);
+ done = true;
+ }];
+ TestWebKitAPI::Util::run(&done);
+
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages = messages.filter((message) => message.name == IPC.messages.WebPage_FindString.name); messages.length"].UTF8String, "1");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages[0].description"].UTF8String, "WebPage_FindString");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args = messages[0].arguments; args.length"].intValue, 3);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].type"].UTF8String, "String");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].value"].UTF8String, "hello");
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[1].type"].UTF8String, "uint16_t");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1].value"].intValue, 0x11);
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1].isOptionSet"].boolValue, YES);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[2].type"].UTF8String, "uint32_t");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[2].value"].intValue, 1);
+ EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(messages[0].syncRequestID)"].UTF8String, "undefined");
+ EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].destinationID"].intValue,
+ [webView stringByEvaluatingJavaScript:@"IPC.webPageProxyID.toString()"].intValue);
+}
+
+#endif