WebKit Bugzilla
Attachment 360940 Details for
Bug 193806
: [PlayStation] Upstream playstation's remote inspector server
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Proof of Concept
193806-poc.diff (text/plain), 19.80 KB, created by
Christopher Reid
on 2019-02-01 18:03:40 PST
(
hide
)
Description:
Proof of Concept
Filename:
MIME Type:
Creator:
Christopher Reid
Created:
2019-02-01 18:03:40 PST
Size:
19.80 KB
patch
obsolete
>diff --git a/Source/JavaScriptCore/PlatformGTK.cmake b/Source/JavaScriptCore/PlatformGTK.cmake >index 0b2968d27cd..dd42805a2e5 100644 >--- a/Source/JavaScriptCore/PlatformGTK.cmake >+++ b/Source/JavaScriptCore/PlatformGTK.cmake >@@ -8,12 +8,15 @@ list(APPEND JavaScriptCore_UNIFIED_SOURCE_LIST_FILES > > list(APPEND JavaScriptCore_PRIVATE_INCLUDE_DIRECTORIES > "${DERIVED_SOURCES_JAVASCRIPCOREGTK_DIR}" >- "${JAVASCRIPTCORE_DIR}/inspector/remote/glib" >+ "${JAVASCRIPTCORE_DIR}/inspector/remote/playstation" > ) > > list(APPEND JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS >- inspector/remote/glib/RemoteInspectorServer.h >- inspector/remote/glib/RemoteInspectorUtils.h >+ inspector/remote/RemoteConnectionToTarget.h >+ >+ inspector/remote/generic/RemoteInspectorServer.h >+ >+ inspector/remote/playstation/RemoteInspectorSocket.h > ) > > configure_file(javascriptcoregtk.pc.in ${JavaScriptCore_PKGCONFIG_FILE} @ONLY) >diff --git a/Source/JavaScriptCore/SourcesGTK.txt b/Source/JavaScriptCore/SourcesGTK.txt >index 1d1965ab0ab..c00ac0fd371 100644 >--- a/Source/JavaScriptCore/SourcesGTK.txt >+++ b/Source/JavaScriptCore/SourcesGTK.txt >@@ -28,8 +28,8 @@ inspector/remote/RemoteControllableTarget.cpp > inspector/remote/RemoteInspectionTarget.cpp > inspector/remote/RemoteInspector.cpp > >-inspector/remote/glib/RemoteInspectorGlib.cpp >-inspector/remote/glib/RemoteInspectorServer.cpp >-inspector/remote/glib/RemoteInspectorUtils.cpp >- >+inspector/remote/generic/RemoteInspectorGeneric.cpp >+inspector/remote/generic/RemoteInspectorServerGeneric.cpp > inspector/remote/generic/RemoteConnectionToTargetGeneric.cpp >+ >+inspector/remote/playstation/RemoteInspectorSocketPlayStation.cpp >diff --git a/Source/JavaScriptCore/inspector/remote/RemoteInspector.h b/Source/JavaScriptCore/inspector/remote/RemoteInspector.h >index f9b7bbf560a..a2703c5db4d 100644 >--- a/Source/JavaScriptCore/inspector/remote/RemoteInspector.h >+++ b/Source/JavaScriptCore/inspector/remote/RemoteInspector.h >@@ -43,7 +43,7 @@ OBJC_CLASS NSString; > typedef RetainPtr<NSDictionary> TargetListing; > #endif > >-#if USE(GLIB) >+#if 0 // USE(GLIB) > #include <wtf/glib/GRefPtr.h> > typedef GRefPtr<GVariant> TargetListing; > typedef struct _GCancellable GCancellable; >@@ -51,7 +51,7 @@ typedef struct _GDBusConnection GDBusConnection; > typedef struct _GDBusInterfaceVTable GDBusInterfaceVTable; > #endif > >-#if PLATFORM(PLAYSTATION) >+#if PLATFORM(PLAYSTATION) || USE(GLIB) > #include "RemoteConnectionToTarget.h" > #include "RemoteInspectorSocket.h" > #include <wtf/RefCounted.h> >@@ -104,7 +104,7 @@ class JS_EXPORT_PRIVATE RemoteInspector final > > struct SessionCapabilities { > bool acceptInsecureCertificates { false }; >-#if USE(GLIB) >+#if 0 // USE(GLIB) > Vector<std::pair<String, String>> certificates; > #endif > #if PLATFORM(COCOA) >@@ -155,12 +155,12 @@ class JS_EXPORT_PRIVATE RemoteInspector final > > void updateTargetListing(unsigned targetIdentifier); > >-#if USE(GLIB) >+#if 0 // USE(GLIB) > void requestAutomationSession(const char* sessionID, const Client::SessionCapabilities&); > void setup(unsigned targetIdentifier); > void sendMessageToTarget(unsigned targetIdentifier, const char* message); > #endif >-#if PLATFORM(PLAYSTATION) >+#if PLATFORM(PLAYSTATION) || USE(GLIB) > void setup(unsigned targetIdentifier); > void sendMessageToTarget(unsigned targetIdentifier, const char* message); > static void setConnectionIdentifier(ConnectionIdentifier); >@@ -177,7 +177,7 @@ class JS_EXPORT_PRIVATE RemoteInspector final > #if PLATFORM(COCOA) > void setupXPCConnectionIfNeeded(); > #endif >-#if USE(GLIB) >+#if 0 // USE(GLIB) > void setupConnection(GRefPtr<GDBusConnection>&&); > static const GDBusInterfaceVTable s_interfaceVTable; > >@@ -218,7 +218,7 @@ class JS_EXPORT_PRIVATE RemoteInspector final > void receivedAutomaticInspectionRejectMessage(NSDictionary *userInfo); > void receivedAutomationSessionRequestMessage(NSDictionary *userInfo); > #endif >-#if PLATFORM(PLAYSTATION) >+#if PLATFORM(PLAYSTATION) || USE(GLIB) > using RemoteInspectorCall = void (RemoteInspector::*)(const InspectorEvent&); > HashMap<String, RemoteInspectorCall>& methodTable(); > >@@ -248,12 +248,12 @@ class JS_EXPORT_PRIVATE RemoteInspector final > #if PLATFORM(COCOA) > RefPtr<RemoteInspectorXPCConnection> m_relayConnection; > #endif >-#if USE(GLIB) >+#if 0 // USE(GLIB) > GRefPtr<GDBusConnection> m_dbusConnection; > GRefPtr<GCancellable> m_cancellable; > #endif > >-#if PLATFORM(PLAYSTATION) >+#if PLATFORM(PLAYSTATION) || USE(GLIB) > std::unique_ptr<RemoteInspectorSocketClient> m_socketConnection; > static ConnectionIdentifier connectionIdentifier; > #endif >diff --git a/Source/WebKit/Shared/WebProcessCreationParameters.cpp b/Source/WebKit/Shared/WebProcessCreationParameters.cpp >index 29f9933ed8a..ff7304946ed 100644 >--- a/Source/WebKit/Shared/WebProcessCreationParameters.cpp >+++ b/Source/WebKit/Shared/WebProcessCreationParameters.cpp >@@ -159,6 +159,10 @@ void WebProcessCreationParameters::encode(IPC::Encoder& encoder) const > encoder << screenProperties; > encoder << useOverlayScrollbars; > #endif >+ >+#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(GTK) >+ encoder << remoteInspectorConnectionHandle; >+#endif > } > > bool WebProcessCreationParameters::decode(IPC::Decoder& decoder, WebProcessCreationParameters& parameters) >@@ -421,6 +425,11 @@ bool WebProcessCreationParameters::decode(IPC::Decoder& decoder, WebProcessCreat > return false; > #endif > >+#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(GTK) >+ if (!decoder.decode(parameters.remoteInspectorConnectionHandle)) >+ return false; >+#endif >+ > return true; > } > >diff --git a/Source/WebKit/Shared/WebProcessCreationParameters.h b/Source/WebKit/Shared/WebProcessCreationParameters.h >index b7821aa5cfb..7cde325e7e2 100644 >--- a/Source/WebKit/Shared/WebProcessCreationParameters.h >+++ b/Source/WebKit/Shared/WebProcessCreationParameters.h >@@ -198,6 +198,11 @@ struct WebProcessCreationParameters { > WebCore::ScreenProperties screenProperties; > bool useOverlayScrollbars { true }; > #endif >+ >+#if PLATFORM(GTK) && ENABLE(REMOTE_INSPECTOR) // TODO Might not be the best approach >+ IPC::Attachment remoteInspectorConnectionHandle; >+#endif >+ > }; > > } // namespace WebKit >diff --git a/Source/WebKit/UIProcess/API/glib/WebKitAutomationSession.cpp b/Source/WebKit/UIProcess/API/glib/WebKitAutomationSession.cpp >index d2af64bad86..51498f64d8a 100644 >--- a/Source/WebKit/UIProcess/API/glib/WebKitAutomationSession.cpp >+++ b/Source/WebKit/UIProcess/API/glib/WebKitAutomationSession.cpp >@@ -293,6 +293,7 @@ static void webkit_automation_session_class_init(WebKitAutomationSessionClass* s > #if ENABLE(REMOTE_INSPECTOR) > WebKitAutomationSession* webkitAutomationSessionCreate(WebKitWebContext* webContext, const char* sessionID, const Inspector::RemoteInspector::Client::SessionCapabilities& capabilities) > { >+#if 0 // Unsupported currently > auto* session = WEBKIT_AUTOMATION_SESSION(g_object_new(WEBKIT_TYPE_AUTOMATION_SESSION, "id", sessionID, nullptr)); > session->priv->webContext = webContext; > if (capabilities.acceptInsecureCertificates) >@@ -303,6 +304,9 @@ WebKitAutomationSession* webkitAutomationSessionCreate(WebKitWebContext* webCont > webkit_web_context_allow_tls_certificate_for_host(webContext, tlsCertificate.get(), certificate.first.utf8().data()); > } > return session; >+#else >+ return nullptr; >+#endif > } > #endif > >diff --git a/Source/WebKit/UIProcess/glib/RemoteInspectorClient.cpp b/Source/WebKit/UIProcess/glib/RemoteInspectorClient.cpp >index b874fb0fb2e..487451f3e16 100644 >--- a/Source/WebKit/UIProcess/glib/RemoteInspectorClient.cpp >+++ b/Source/WebKit/UIProcess/glib/RemoteInspectorClient.cpp >@@ -29,8 +29,11 @@ > #if ENABLE(REMOTE_INSPECTOR) > > #include "RemoteWebInspectorProxy.h" >+#if 0 // Currently don't support backend mismatches > #include <JavaScriptCore/RemoteInspectorUtils.h> >+#endif > #include <gio/gio.h> >+#include <wtf/JSONValues.h> > #include <wtf/glib/GUniquePtr.h> > #include <wtf/text/Base64.h> > >@@ -152,11 +155,101 @@ const GDBusInterfaceVTable RemoteInspectorClient::s_interfaceVTable = { > { 0 } > }; > >+void RemoteInspectorClient::sendWebInspectorEvent(const String& event) >+{ >+ m_socketConnection->send(event.utf8().data(), event.utf8().length()); >+} >+ >+void RemoteInspectorClient::didReceiveWebInspectorEvent(int clientID, Vector<uint8_t>&& data) >+{ >+ String jsonData = String::adopt(WTFMove(data)); >+ callOnMainThread([this, jsonData] { >+ >+ RefPtr<JSON::Value> messageValue; >+ if (!JSON::Value::parseJSON(jsonData, messageValue)) >+ return; >+ >+ RefPtr<JSON::Object> messageObject; >+ if (!messageValue->asObject(messageObject)) >+ return; >+ >+ String event; >+ if (!messageObject->getString("event"_s, event)) >+ return; >+ >+ if (event == "SetTargetList"_s) { >+ >+ uint64_t connectionID; >+ if (!messageObject->getInteger("connectionID"_s, connectionID)) >+ return; >+ >+ String targetListMessage; >+ if (!messageObject->getString("message"_s, targetListMessage)) { >+ return; >+ } >+ >+ RefPtr<JSON::Value> targetListValue; >+ if (!JSON::Value::parseJSON(targetListMessage, targetListValue)) >+ return; >+ >+ RefPtr<JSON::Array> targetListJSON; >+ if (!targetListValue->asArray(targetListJSON)) >+ return; >+ >+ Vector<RemoteInspectorClient::Target> targetList; >+ for (unsigned i=0; i < targetListJSON->length(); i++) { >+ RefPtr<JSON::Object> targetObject; >+ if (!targetListJSON->get(i)->asObject(targetObject)) { >+ continue; >+ } >+ >+ uint64_t targetID; >+ String type, name, url; >+ targetObject->getInteger("targetID"_s, targetID); >+ targetObject->getString("type"_s, type); >+ targetObject->getString("name"_s, name); >+ targetObject->getString("url"_s, url); >+ if ((type != "Web"_s) || (type != "JavaScript"_s)) { >+ targetList.append({ targetID, type.utf8(), name.utf8(), url.utf8() }); >+ } >+ } >+ setTargetList(connectionID, WTFMove(targetList)); >+ } else if (event == "SendMessageToFrontend"_s) { >+ uint64_t connectionID, targetID; >+ String message; >+ >+ if (!messageObject->getInteger("connectionID"_s, connectionID)) >+ return; >+ >+ if (!messageObject->getInteger("targetID"_s, targetID)) >+ return; >+ >+ if (!messageObject->getString("message"_s, message)) >+ return; >+ >+ // Not sure if the message can be binary data and utf8() could cause issues >+ sendMessageToFrontend(connectionID, targetID, message.utf8().data()); >+ >+ } else >+ LOG_ERROR("Client received unknown event: %s", event.utf8().data()); >+ }); >+ >+} >+ > RemoteInspectorClient::RemoteInspectorClient(const char* address, unsigned port, RemoteInspectorObserver& observer) > : m_hostAndPort(String::fromUTF8(address) + ':' + String::number(port)) > , m_observer(observer) > , m_cancellable(adoptGRef(g_cancellable_new())) > { >+ m_socketConnection = Inspector::RemoteInspectorSocketClient::create(); >+ m_socketConnection->setDidReceiveDataListener([this](int clientID, Vector<uint8_t>&& data) { >+ didReceiveWebInspectorEvent(clientID, WTFMove(data)); >+ }); >+ >+ m_socketConnection->setDidCloseListener([this](int clientID) { }); >+ if (m_socketConnection->connectInet(address, port)) >+ setupSocketConnection(); >+#if 0 > GUniquePtr<char> dbusAddress(g_strdup_printf("tcp:host=%s,port=%u", address, port)); > g_dbus_connection_new_for_address(dbusAddress.get(), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, nullptr, m_cancellable.get(), > [](GObject*, GAsyncResult* result, gpointer userData) { >@@ -167,10 +260,12 @@ RemoteInspectorClient::RemoteInspectorClient(const char* address, unsigned port, > WTFLogAlways("RemoteInspectorClient failed to connect to inspector server: %s", error->message); > client->setupConnection(WTFMove(connection)); > }, this); >+#endif > } > > RemoteInspectorClient::~RemoteInspectorClient() > { >+ m_socketConnection->close(); > } > > static void dbusConnectionCallAsyncReadyCallback(GObject* source, GAsyncResult* result, gpointer) >@@ -206,7 +301,11 @@ void RemoteInspectorClient::setupConnection(GRefPtr<GDBusConnection>&& connectio > INSPECTOR_DBUS_OBJECT_PATH, > INSPECTOR_DBUS_INTERFACE, > "SetupInspectorClient", >+ #if 0 // Currently don't support backendCommandsHash > g_variant_new("(@ay)", g_variant_new_bytestring(Inspector::backendCommandsHash().data())), >+ #else >+ g_variant_new("(@ay)", g_variant_new_bytestring("")), >+ #endif > nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, > -1, m_cancellable.get(), [](GObject* source, GAsyncResult* result, gpointer userData) { > GUniqueOutPtr<GError> error; >@@ -225,6 +324,14 @@ void RemoteInspectorClient::setupConnection(GRefPtr<GDBusConnection>&& connectio > }, this); > } > >+void RemoteInspectorClient::setupSocketConnection() >+{ >+ auto setupEvent = JSON::Object::create(); >+ setupEvent->setString("event"_s, "SetupInspectorClient"_s); >+ setupEvent->setString("message"_s, "test"); >+ sendWebInspectorEvent(setupEvent->toJSONString()); >+} >+ > void RemoteInspectorClient::setBackendCommands(const char* backendCommands) > { > if (!backendCommands || !backendCommands[0]) { >@@ -256,6 +363,13 @@ void RemoteInspectorClient::inspect(uint64_t connectionID, uint64_t targetID) > return; > } > >+ auto setupEvent = JSON::Object::create(); >+ setupEvent->setString("event"_s, "Setup"_s); >+ setupEvent->setInteger("connectionID"_s, connectionID); >+ setupEvent->setInteger("targetID"_s, targetID); >+ sendWebInspectorEvent(setupEvent->toJSONString()); >+ >+#if 0 > g_dbus_connection_call(m_dbusConnection.get(), nullptr, > INSPECTOR_DBUS_OBJECT_PATH, > INSPECTOR_DBUS_INTERFACE, >@@ -264,11 +378,20 @@ void RemoteInspectorClient::inspect(uint64_t connectionID, uint64_t targetID) > nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, > -1, m_cancellable.get(), dbusConnectionCallAsyncReadyCallback, nullptr); > >+#endif > addResult.iterator->value->load(); > } > > void RemoteInspectorClient::sendMessageToBackend(uint64_t connectionID, uint64_t targetID, const String& message) > { >+ auto backendEvent = JSON::Object::create(); >+ backendEvent->setString("event"_s, "SendMessageToBackend"_s); >+ backendEvent->setInteger("connectionID"_s, connectionID); >+ backendEvent->setInteger("targetID"_s, targetID); >+ backendEvent->setString("message"_s, message); >+ sendWebInspectorEvent(backendEvent->toJSONString()); >+ >+#if 0 > g_dbus_connection_call(m_dbusConnection.get(), nullptr, > INSPECTOR_DBUS_OBJECT_PATH, > INSPECTOR_DBUS_INTERFACE, >@@ -276,11 +399,18 @@ void RemoteInspectorClient::sendMessageToBackend(uint64_t connectionID, uint64_t > g_variant_new("(tts)", connectionID, targetID, message.utf8().data()), > nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, > -1, m_cancellable.get(), dbusConnectionCallAsyncReadyCallback, nullptr); >+#endif > } > > void RemoteInspectorClient::closeFromFrontend(uint64_t connectionID, uint64_t targetID) > { > ASSERT(m_inspectorProxyMap.contains(std::make_pair(connectionID, targetID))); >+ auto closedEvent = JSON::Object::create(); >+ closedEvent->setString("event"_s, "FrontendDidClose"_s); >+ closedEvent->setInteger("connectionID"_s, connectionID); >+ closedEvent->setInteger("targetID"_s, targetID); >+ sendWebInspectorEvent(closedEvent->toJSONString()); >+#if 0 > g_dbus_connection_call(m_dbusConnection.get(), nullptr, > INSPECTOR_DBUS_OBJECT_PATH, > INSPECTOR_DBUS_INTERFACE, >@@ -288,6 +418,7 @@ void RemoteInspectorClient::closeFromFrontend(uint64_t connectionID, uint64_t ta > g_variant_new("(tt)", connectionID, targetID), > nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, > -1, m_cancellable.get(), dbusConnectionCallAsyncReadyCallback, nullptr); >+#endif > m_inspectorProxyMap.remove(std::make_pair(connectionID, targetID)); > } > >diff --git a/Source/WebKit/UIProcess/glib/RemoteInspectorClient.h b/Source/WebKit/UIProcess/glib/RemoteInspectorClient.h >index 68818cf02f4..ea2f4efa840 100644 >--- a/Source/WebKit/UIProcess/glib/RemoteInspectorClient.h >+++ b/Source/WebKit/UIProcess/glib/RemoteInspectorClient.h >@@ -27,6 +27,7 @@ > > #if ENABLE(REMOTE_INSPECTOR) > >+#include <JavaScriptCore/RemoteInspectorSocket.h> > #include <wtf/HashMap.h> > #include <wtf/Vector.h> > #include <wtf/glib/GRefPtr.h> >@@ -71,6 +72,10 @@ class RemoteInspectorClient { > void sendMessageToBackend(uint64_t connectionID, uint64_t targetID, const String&); > void closeFromFrontend(uint64_t connectionID, uint64_t targetID); > >+ void sendWebInspectorEvent(const String&); >+ void didReceiveWebInspectorEvent(int clientID, Vector<uint8_t>&& data); >+ void setupSocketConnection(); >+ > private: > static void connectionClosedCallback(GDBusConnection*, gboolean remotePeerVanished, GError*, RemoteInspectorClient*); > void setupConnection(GRefPtr<GDBusConnection>&&); >@@ -88,6 +93,7 @@ class RemoteInspectorClient { > GRefPtr<GCancellable> m_cancellable; > HashMap<uint64_t, Vector<Target>> m_targets; > HashMap<std::pair<uint64_t, uint64_t>, std::unique_ptr<RemoteInspectorProxy>> m_inspectorProxyMap; >+ std::unique_ptr<Inspector::RemoteInspectorSocketClient> m_socketConnection; > }; > > } // namespace WebKit >diff --git a/Source/WebKit/UIProcess/gtk/WebProcessPoolGtk.cpp b/Source/WebKit/UIProcess/gtk/WebProcessPoolGtk.cpp >index 6b2357404e0..526154c9aa2 100644 >--- a/Source/WebKit/UIProcess/gtk/WebProcessPoolGtk.cpp >+++ b/Source/WebKit/UIProcess/gtk/WebProcessPoolGtk.cpp >@@ -94,6 +94,27 @@ void WebProcessPool::platformInitializeWebProcess(WebProcessCreationParameters& > #if USE(GSTREAMER) > parameters.gstreamerOptions = WebCore::extractGStreamerOptionsFromCommandLine(); > #endif >+ >+#if ENABLE(REMOTE_INSPECTOR) >+ // Should check if server exists >+ { >+ IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection(); >+ Inspector::RemoteInspectorServer::singleton().addServerConnection(socketPair.server); >+ parameters.remoteInspectorConnectionHandle = IPC::Attachment(socketPair.client); >+ } >+ >+ // Workaround for https://trac.webkit.org/changeset/238192/webkit >+ // There might be a better solution for this than having both >+ // a client and server running in the UIProcess, but it's similar >+ // to what DBus is doing. >+ // This also seems like the wrong place to set up this connection >+ { >+ IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection(); >+ Inspector::RemoteInspector::setConnectionIdentifier(socketPair.client); >+ Inspector::RemoteInspectorServer::singleton().addServerConnection(socketPair.server); >+ } >+#endif >+ > } > > void WebProcessPool::platformInvalidateContext() >diff --git a/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp b/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp >index 17da52de5ed..33aed69aa4a 100644 >--- a/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp >+++ b/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp >@@ -35,6 +35,10 @@ > #include "WaylandCompositorDisplay.h" > #endif > >+#if ENABLE(REMOTE_INSPECTOR) >+#include <JavaScriptCore/RemoteInspector.h> >+#endif >+ > namespace WebKit { > > void WebProcess::platformSetCacheModel(CacheModel cacheModel) >@@ -50,6 +54,9 @@ void WebProcess::platformInitializeWebProcess(WebProcessCreationParameters&& par > #if USE(GSTREAMER) > WebCore::initializeGStreamer(WTFMove(parameters.gstreamerOptions)); > #endif >+#if ENABLE(REMOTE_INSPECTOR) >+ Inspector::RemoteInspector::setConnectionIdentifier(parameters.remoteInspectorConnectionHandle.releaseFileDescriptor()); >+#endif > } > > void WebProcess::platformTerminate()
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 193806
:
360074
|
360938
|
360940
|
361665
|
361668
|
361701
|
361707
|
362388
|
362940
|
363397
|
363398