Web Inspector: InspectorFrontendAPIDispatcher should not ignore all exceptions
[WebKit-https.git] / Source / WebKit / WebProcess / Inspector / WebInspectorUIExtensionController.cpp
1 /*
2  * Copyright (C) 2020 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebInspectorUIExtensionController.h"
28
29 #if ENABLE(INSPECTOR_EXTENSIONS)
30
31 #include "Logging.h"
32 #include "WebInspectorUI.h"
33 #include "WebInspectorUIExtensionControllerMessages.h"
34 #include "WebInspectorUIExtensionControllerMessagesReplies.h"
35 #include "WebPage.h"
36 #include "WebProcess.h"
37 #include <JavaScriptCore/JSCInlines.h>
38 #include <WebCore/ExceptionDetails.h>
39 #include <WebCore/InspectorFrontendAPIDispatcher.h>
40
41 namespace WebKit {
42
43 WebInspectorUIExtensionController::WebInspectorUIExtensionController(WebCore::InspectorFrontendClient& inspectorFrontend)
44     : m_frontendClient(makeWeakPtr(inspectorFrontend))
45 {
46     Page* page = inspectorFrontend.frontendPage();
47     ASSERT(page);
48
49     WebProcess::singleton().addMessageReceiver(Messages::WebInspectorUIExtensionController::messageReceiverName(), WebPage::fromCorePage(*page).identifier(), *this);
50 }
51
52 WebInspectorUIExtensionController::~WebInspectorUIExtensionController()
53 {
54     WebProcess::singleton().removeMessageReceiver(*this);
55 }
56
57 Optional<InspectorExtensionError> WebInspectorUIExtensionController::parseInspectorExtensionErrorFromEvaluationResult(InspectorFrontendAPIDispatcher::EvaluationResult result)
58 {
59     if (!result.has_value()) {
60         switch (result.error()) {
61         case WebCore::InspectorFrontendAPIDispatcher::EvaluationError::ContextDestroyed:
62             return InspectorExtensionError::ContextDestroyed;
63         case WebCore::InspectorFrontendAPIDispatcher::EvaluationError::ExecutionSuspended:
64             return InspectorExtensionError::InternalError;
65         }
66     }
67
68     ASSERT(m_frontendClient);
69     auto globalObject = m_frontendClient->frontendAPIDispatcher().frontendGlobalObject();
70     if (!globalObject)
71         return InspectorExtensionError::ContextDestroyed;
72
73     auto valueOrException = result.value();
74     if (!valueOrException.has_value()) {
75         LOG(Inspector, "Encountered exception while evaluating upon the frontend: %s", valueOrException.error().message.utf8().data());
76         return InspectorExtensionError::InternalError;
77     }
78     
79     // If the evaluation result is a string, the frontend returned an error string.
80     // If there was a JS exception, log it and return error code InternalError.
81     // Anything else (falsy values, objects, arrays, DOM, etc.) is interpreted as success.
82     JSC::JSValue scriptValue = valueOrException.value();
83     if (scriptValue.isString()) {
84         auto resultString = scriptValue.toWTFString(globalObject);
85         if (resultString == "ContextDestroyed"_s)
86             return InspectorExtensionError::ContextDestroyed;
87         if (resultString == "InternalError"_s)
88             return InspectorExtensionError::InternalError;
89         if (resultString == "InvalidRequest"_s)
90             return InspectorExtensionError::InvalidRequest;
91         if (resultString == "RegistrationFailed"_s)
92             return InspectorExtensionError::RegistrationFailed;
93
94         ASSERT_NOT_REACHED();
95         return InspectorExtensionError::InternalError;
96     }
97
98     return WTF::nullopt;
99 }
100
101 // WebInspectorUIExtensionController IPC messages.
102
103 void WebInspectorUIExtensionController::registerExtension(const InspectorExtensionID& extensionID, const String& displayName, CompletionHandler<void(Expected<bool, InspectorExtensionError>)>&& completionHandler)
104 {
105     if (!m_frontendClient) {
106         completionHandler(makeUnexpected(InspectorExtensionError::InvalidRequest));
107         return;
108     }
109
110     Vector<Ref<JSON::Value>> arguments {
111         JSON::Value::create(extensionID),
112         JSON::Value::create(displayName),
113     };
114     m_frontendClient->frontendAPIDispatcher().dispatchCommandWithResultAsync("registerExtension"_s, WTFMove(arguments), [weakThis = makeWeakPtr(this), completionHandler = WTFMove(completionHandler)](InspectorFrontendAPIDispatcher::EvaluationResult&& result) mutable {
115         if (!weakThis || !result) {
116             completionHandler(makeUnexpected(InspectorExtensionError::ContextDestroyed));
117             return;
118         }
119
120         if (auto parsedError = weakThis->parseInspectorExtensionErrorFromEvaluationResult(result)) {
121             completionHandler(makeUnexpected(parsedError.value()));
122             return;
123         }
124
125         completionHandler(true);
126     });
127 }
128
129 void WebInspectorUIExtensionController::unregisterExtension(const InspectorExtensionID& extensionID, CompletionHandler<void(Expected<bool, InspectorExtensionError>)>&& completionHandler)
130 {
131     if (!m_frontendClient) {
132         completionHandler(makeUnexpected(InspectorExtensionError::InvalidRequest));
133         return;
134     }
135
136     Vector<Ref<JSON::Value>> arguments { JSON::Value::create(extensionID) };
137     m_frontendClient->frontendAPIDispatcher().dispatchCommandWithResultAsync("unregisterExtension"_s, WTFMove(arguments), [weakThis = makeWeakPtr(this), completionHandler = WTFMove(completionHandler)](InspectorFrontendAPIDispatcher::EvaluationResult&& result) mutable {
138         if (!weakThis || !result) {
139             completionHandler(makeUnexpected(InspectorExtensionError::ContextDestroyed));
140             return;
141         }
142
143         if (auto parsedError = weakThis->parseInspectorExtensionErrorFromEvaluationResult(result.value())) {
144             completionHandler(makeUnexpected(parsedError.value()));
145             return;
146         }
147
148         completionHandler(true);
149     });
150 }
151
152 } // namespace WebKit
153
154 #endif // ENABLE(INSPECTOR_EXTENSIONS)