Web Inspector: add protocol test for existing error handling performed by the backend
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Test / InspectorProtocol.js
1 /*
2  * Copyright (C) 2012 Samsung Electronics. All rights reserved.
3  * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 InspectorProtocol = {};
28 InspectorProtocol._dispatchTable = [];
29 InspectorProtocol._placeholderRequestIds = [];
30 InspectorProtocol._requestId = -1;
31 InspectorProtocol.eventHandler = {};
32
33 InspectorProtocol.sendCommand = function(methodOrObject, params, handler)
34 {
35     // Allow new-style arguments object, as in awaitCommand.
36     let method = methodOrObject;
37     if (typeof methodOrObject === "object")
38         var {method, params, handler} = methodOrObject;
39
40     this._dispatchTable[++this._requestId] = handler;
41     let messageObject = {method, params, id: this._requestId};
42     this._sendMessage(messageObject);
43
44     return this._requestId;
45 }
46
47 InspectorProtocol.awaitCommand = function(args)
48 {
49     let {method, params} = args;
50     let messageObject = {method, params, id: ++this._requestId};
51
52     return this.awaitMessage(messageObject);
53 }
54
55 InspectorProtocol.awaitMessage = function(messageObject)
56 {
57     // Send a raw message to the backend. Mostly used to test the backend's error handling.
58     return new Promise((resolve, reject) => {
59         let requestId = messageObject.id;
60
61         // If the caller did not provide an id, then make one up so that the response
62         // can be used to settle a promise.
63         if (typeof requestId !== "number") {
64             requestId = ++this._requestId;
65             this._placeholderRequestIds.push(requestId);
66         }
67
68         this._dispatchTable[requestId] = {resolve, reject};
69         this._sendMessage(messageObject);
70     });
71 }
72
73 InspectorProtocol.awaitEvent = function(args)
74 {
75     let event = args.event;
76     if (typeof event !== "string")
77         throw new Error("Event must be a string.");
78
79     return new Promise((resolve, reject) => {
80         InspectorProtocol.eventHandler[event] = function(message) {
81             InspectorProtocol.eventHandler[event] = undefined;
82             resolve(message);
83         }
84     });
85 }
86
87 InspectorProtocol._sendMessage = function(messageObject)
88 {
89     let messageString = typeof messageObject !== "string" ? JSON.stringify(messageObject) : messageObject;
90
91     if (ProtocolTest.dumpInspectorProtocolMessages)
92         console.log(`frontend: ${messageString}`);
93
94     InspectorFrontendHost.sendMessageToBackend(messageString);
95 }
96
97 InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
98 {
99     let event = eventTypeOrObject;
100     if (typeof eventTypeOrObject === "object")
101         var {event, listener} = eventTypeOrObject;
102
103     if (typeof event !== "string")
104         throw new Error("Event name must be a string.");
105
106     if (typeof listener !== "function")
107         throw new Error("Event listener must be callable.");
108
109     // Convert to an array of listeners.
110     let listeners = InspectorProtocol.eventHandler[event];
111     if (!listeners)
112         listeners = InspectorProtocol.eventHandler[event] = [];
113     else if (typeof listeners === "function")
114         listeners = InspectorProtocol.eventHandler[event] = [listeners];
115
116     // Prevent registering multiple times.
117     if (listeners.includes(listener))
118         throw new Error("Cannot register the same listener more than once.");
119
120     listeners.push(listener);
121 }
122
123 InspectorProtocol.checkForError = function(responseObject)
124 {
125     if (responseObject.error) {
126         ProtocolTest.log("PROTOCOL ERROR: " + JSON.stringify(responseObject.error));
127         ProtocolTest.completeTest();
128         throw "PROTOCOL ERROR";
129     }
130 }
131
132 InspectorProtocol.dispatchMessageFromBackend = function(messageObject)
133 {
134     // This matches the debug dumping in InspectorBackend, which is bypassed
135     // by InspectorProtocol. Return messages should be dumped by InspectorBackend.
136     if (ProtocolTest.dumpInspectorProtocolMessages)
137         console.log("backend: " + JSON.stringify(messageObject));
138
139     // If the message has an id, then it is a reply to a command.
140     let messageId = messageObject.id;
141
142     // If the id is 'null', then it may be an error response.
143     if (messageId === null)
144         messageId = InspectorProtocol._placeholderRequestIds.shift();
145
146     // If we could figure out a requestId, then dispatch the message.
147     if (typeof messageId === "number") {
148         let handler = InspectorProtocol._dispatchTable[messageId];
149         if (!handler)
150             return;
151
152         if (typeof handler === "function")
153             handler(messageObject);
154         else if (typeof handler === "object") {
155             let {resolve, reject} = handler;
156             if ("error" in messageObject)
157                 reject(messageObject.error);
158             else
159                 resolve(messageObject.result);
160         }
161     } else {
162         // Otherwise, it is an event.
163         let eventName = messageObject["method"];
164         let handler = InspectorProtocol.eventHandler[eventName];
165         if (!handler)
166             return;
167
168         if (typeof handler === "function")
169             handler(messageObject);
170         else if (handler instanceof Array) {
171             handler.map((listener) => { listener.call(null, messageObject); });
172         } else if (typeof handler === "object") {
173             let {resolve, reject} = handler;
174             if ("error" in messageObject)
175                 reject(messageObject.error);
176             else
177                 resolve(messageObject.result);
178         }
179     }
180 }