Title: [127861] trunk/Source/WebCore
Revision
127861
Author
vse...@chromium.org
Date
2012-09-07 05:32:51 -0700 (Fri, 07 Sep 2012)

Log Message

Web Inspector: Migrate Database to async protocol commands
https://bugs.webkit.org/show_bug.cgi?id=95983

Reviewed by Alexander Pavlov.

* English.lproj/localizedStrings.js:
* inspector/Inspector.json:
* inspector/InspectorDatabaseAgent.cpp:
(WebCore):
(WebCore::InspectorDatabaseAgent::didOpenDatabase):
(WebCore::InspectorDatabaseAgent::setFrontend):
(WebCore::InspectorDatabaseAgent::clearFrontend):
(WebCore::InspectorDatabaseAgent::enable):
(WebCore::InspectorDatabaseAgent::executeSQL):
* inspector/InspectorDatabaseAgent.h:
(InspectorDatabaseAgent):
* inspector/front-end/Database.js:
(WebInspector.Database.prototype.executeSql):
(WebInspector.DatabaseDispatcher.prototype.addDatabase):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (127860 => 127861)


--- trunk/Source/WebCore/ChangeLog	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/ChangeLog	2012-09-07 12:32:51 UTC (rev 127861)
@@ -1,3 +1,25 @@
+2012-09-06  Vsevolod Vlasov  <vse...@chromium.org>
+
+        Web Inspector: Migrate Database to async protocol commands
+        https://bugs.webkit.org/show_bug.cgi?id=95983
+
+        Reviewed by Alexander Pavlov.
+
+        * English.lproj/localizedStrings.js:
+        * inspector/Inspector.json:
+        * inspector/InspectorDatabaseAgent.cpp:
+        (WebCore):
+        (WebCore::InspectorDatabaseAgent::didOpenDatabase):
+        (WebCore::InspectorDatabaseAgent::setFrontend):
+        (WebCore::InspectorDatabaseAgent::clearFrontend):
+        (WebCore::InspectorDatabaseAgent::enable):
+        (WebCore::InspectorDatabaseAgent::executeSQL):
+        * inspector/InspectorDatabaseAgent.h:
+        (InspectorDatabaseAgent):
+        * inspector/front-end/Database.js:
+        (WebInspector.Database.prototype.executeSql):
+        (WebInspector.DatabaseDispatcher.prototype.addDatabase):
+
 2012-09-07  Ilya Tikhonovsky  <loi...@chromium.org>
 
         Unreviewed single line follow-up change for r127856.

Modified: trunk/Source/WebCore/English.lproj/localizedStrings.js (127860 => 127861)


--- trunk/Source/WebCore/English.lproj/localizedStrings.js	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/English.lproj/localizedStrings.js	2012-09-07 12:32:51 UTC (rev 127861)
@@ -150,7 +150,6 @@
 localizedStrings["DOMContent event"] = "DOMContent event";
 localizedStrings["DOM Node Count"] = "DOM Node Count";
 localizedStrings["Database no longer has expected version."] = "Database no longer has expected version.";
-localizedStrings["Database not found."] = "Database not found.";
 localizedStrings["Deactivate all breakpoints."] = "Deactivate all breakpoints.";
 localizedStrings["Deactivate all breakpoints"] = "Deactivate all breakpoints";
 localizedStrings["Debug"] = "Debug";

Modified: trunk/Source/WebCore/inspector/Inspector.json (127860 => 127861)


--- trunk/Source/WebCore/inspector/Inspector.json	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/inspector/Inspector.json	2012-09-07 12:32:51 UTC (rev 127861)
@@ -1074,7 +1074,11 @@
             {
                 "id": "Error",
                 "type": "object",
-                "description": "Database error."
+                "description": "Database error.",
+                "properties": [
+                    { "name": "message", "type": "string", "description": "Error message." },
+                    { "name": "code", "type": "integer", "description": "Error code." }
+                ]
             }
         ],
         "commands": [
@@ -1097,13 +1101,15 @@
             },
             {
                 "name": "executeSQL",
+                "async": true,
                 "parameters": [
                     { "name": "databaseId", "$ref": "DatabaseId" },
                     { "name": "query", "type": "string" }
                 ],
                 "returns": [
-                    { "name": "success", "type": "boolean" },
-                    { "name": "transactionId", "type": "integer" }
+                    { "name": "columnNames", "type": "array", "optional": true, "items": { "type": "string" } },
+                    { "name": "values", "type": "array", "optional": true, "items": { "type": "any" }},
+                    { "name": "sqlError", "$ref": "Error", "optional": true }
                 ]
             }
         ],
@@ -1113,21 +1119,6 @@
                 "parameters": [
                     { "name": "database", "$ref": "Database" }
                 ]
-            },
-            {
-                "name": "sqlTransactionSucceeded",
-                "parameters": [
-                    { "name": "transactionId", "type": "integer" },
-                    { "name": "columnNames", "type": "array", "items": { "type": "string" } },
-                    { "name": "values", "type": "array", "items": { "type": "any" }}
-                ]
-            },
-            {
-                "name": "sqlTransactionFailed",
-                "parameters": [
-                    { "name": "transactionId", "type": "integer" },
-                    { "name": "sqlError", "$ref": "Error" }
-                ]
             }
         ]
     },

Modified: trunk/Source/WebCore/inspector/InspectorDatabaseAgent.cpp (127860 => 127861)


--- trunk/Source/WebCore/inspector/InspectorDatabaseAgent.cpp	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/inspector/InspectorDatabaseAgent.cpp	2012-09-07 12:32:51 UTC (rev 127861)
@@ -51,56 +51,35 @@
 
 #include <wtf/Vector.h>
 
+typedef WebCore::InspectorBackendDispatcher::DatabaseCommandHandler::ExecuteSQLCallback ExecuteSQLCallback;
+
 namespace WebCore {
 
 namespace DatabaseAgentState {
 static const char databaseAgentEnabled[] = "databaseAgentEnabled";
 };
 
-class InspectorDatabaseAgent::FrontendProvider : public RefCounted<InspectorDatabaseAgent::FrontendProvider> {
-public:
-    static PassRefPtr<FrontendProvider> create(InspectorFrontend* inspectorFrontend)
-    {
-        return adoptRef(new FrontendProvider(inspectorFrontend));
-    }
-
-    virtual ~FrontendProvider() { }
-
-    InspectorFrontend::Database* frontend() { return m_inspectorFrontend; }
-    void clearFrontend() { m_inspectorFrontend = 0; }
-private:
-    FrontendProvider(InspectorFrontend* inspectorFrontend) : m_inspectorFrontend(inspectorFrontend->database()) { }
-    InspectorFrontend::Database* m_inspectorFrontend;
-};
-
 namespace {
 
-int lastTransactionId = 0;
-
-void reportTransactionFailed(InspectorFrontend::Database* frontend, int transactionId, SQLError* error)
+void reportTransactionFailed(ExecuteSQLCallback* requestCallback, SQLError* error)
 {
-    if (!frontend)
-        return;
-    RefPtr<InspectorObject> errorObject = InspectorObject::create();
-    errorObject->setString("message", error->message());
-    errorObject->setNumber("code", error->code());
-    frontend->sqlTransactionFailed(transactionId, errorObject);
+    RefPtr<TypeBuilder::Database::Error> errorObject = TypeBuilder::Database::Error::create()
+        .setMessage(error->message())
+        .setCode(error->code());
+    requestCallback->sendSuccess(0, 0, errorObject.release());
 }
 
 class StatementCallback : public SQLStatementCallback {
 public:
-    static PassRefPtr<StatementCallback> create(int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
+    static PassRefPtr<StatementCallback> create(PassRefPtr<ExecuteSQLCallback> requestCallback)
     {
-        return adoptRef(new StatementCallback(transactionId, frontendProvider));
+        return adoptRef(new StatementCallback(requestCallback));
     }
 
     virtual ~StatementCallback() { }
 
     virtual bool handleEvent(SQLTransaction*, SQLResultSet* resultSet)
     {
-        if (!m_frontendProvider->frontend())
-            return true;
-
         SQLResultSetRowList* rowList = resultSet->rows();
 
         RefPtr<TypeBuilder::Array<String> > columnNames = TypeBuilder::Array<String>::create();
@@ -118,92 +97,84 @@
             case SQLValue::NullValue: values->addItem(InspectorValue::null()); break;
             }
         }
-        m_frontendProvider->frontend()->sqlTransactionSucceeded(m_transactionId, columnNames, values);
+        m_requestCallback->sendSuccess(columnNames.release(), values.release(), 0);
         return true;
     }
 
 private:
-    StatementCallback(int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
-        : m_transactionId(transactionId)
-        , m_frontendProvider(frontendProvider) { }
-    int m_transactionId;
-    RefPtr<InspectorDatabaseAgent::FrontendProvider> m_frontendProvider;
+    StatementCallback(PassRefPtr<ExecuteSQLCallback> requestCallback)
+        : m_requestCallback(requestCallback) { }
+    RefPtr<ExecuteSQLCallback> m_requestCallback;
 };
 
 class StatementErrorCallback : public SQLStatementErrorCallback {
 public:
-    static PassRefPtr<StatementErrorCallback> create(int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
+    static PassRefPtr<StatementErrorCallback> create(PassRefPtr<ExecuteSQLCallback> requestCallback)
     {
-        return adoptRef(new StatementErrorCallback(transactionId, frontendProvider));
+        return adoptRef(new StatementErrorCallback(requestCallback));
     }
 
     virtual ~StatementErrorCallback() { }
 
     virtual bool handleEvent(SQLTransaction*, SQLError* error)
     {
-        reportTransactionFailed(m_frontendProvider->frontend(), m_transactionId, error);
+        reportTransactionFailed(m_requestCallback.get(), error);
         return true;  
     }
 
 private:
-    StatementErrorCallback(int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
-        : m_transactionId(transactionId)
-        , m_frontendProvider(frontendProvider) { }
-    int m_transactionId;
-    RefPtr<InspectorDatabaseAgent::FrontendProvider> m_frontendProvider;
+    StatementErrorCallback(PassRefPtr<ExecuteSQLCallback> requestCallback)
+        : m_requestCallback(requestCallback) { }
+    RefPtr<ExecuteSQLCallback> m_requestCallback;
 };
 
 class TransactionCallback : public SQLTransactionCallback {
 public:
-    static PassRefPtr<TransactionCallback> create(const String& sqlStatement, int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
+    static PassRefPtr<TransactionCallback> create(const String& sqlStatement, PassRefPtr<ExecuteSQLCallback> requestCallback)
     {
-        return adoptRef(new TransactionCallback(sqlStatement, transactionId, frontendProvider));
+        return adoptRef(new TransactionCallback(sqlStatement, requestCallback));
     }
 
     virtual ~TransactionCallback() { }
 
     virtual bool handleEvent(SQLTransaction* transaction)
     {
-        if (!m_frontendProvider->frontend())
+        if (!m_requestCallback->isActive())
             return true;
 
         Vector<SQLValue> sqlValues;
-        RefPtr<SQLStatementCallback> callback(StatementCallback::create(m_transactionId, m_frontendProvider));
-        RefPtr<SQLStatementErrorCallback> errorCallback(StatementErrorCallback::create(m_transactionId, m_frontendProvider));
+        RefPtr<SQLStatementCallback> callback(StatementCallback::create(m_requestCallback.get()));
+        RefPtr<SQLStatementErrorCallback> errorCallback(StatementErrorCallback::create(m_requestCallback.get()));
         ExceptionCode ec = 0;
         transaction->executeSQL(m_sqlStatement, sqlValues, callback.release(), errorCallback.release(), ec);
         return true;
     }
 private:
-    TransactionCallback(const String& sqlStatement, int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
+    TransactionCallback(const String& sqlStatement, PassRefPtr<ExecuteSQLCallback> requestCallback)
         : m_sqlStatement(sqlStatement)
-        , m_transactionId(transactionId)
-        , m_frontendProvider(frontendProvider) { }
+        , m_requestCallback(requestCallback) { }
     String m_sqlStatement;
-    int m_transactionId;
-    RefPtr<InspectorDatabaseAgent::FrontendProvider> m_frontendProvider;
+    RefPtr<ExecuteSQLCallback> m_requestCallback;
 };
 
 class TransactionErrorCallback : public SQLTransactionErrorCallback {
 public:
-    static PassRefPtr<TransactionErrorCallback> create(int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
+    static PassRefPtr<TransactionErrorCallback> create(PassRefPtr<ExecuteSQLCallback> requestCallback)
     {
-        return adoptRef(new TransactionErrorCallback(transactionId, frontendProvider));
+        return adoptRef(new TransactionErrorCallback(requestCallback));
     }
 
     virtual ~TransactionErrorCallback() { }
 
     virtual bool handleEvent(SQLError* error)
     {
-        reportTransactionFailed(m_frontendProvider->frontend(), m_transactionId, error);
+        reportTransactionFailed(m_requestCallback.get(), error);
         return true;
     }
 private:
-    TransactionErrorCallback(int transactionId, PassRefPtr<InspectorDatabaseAgent::FrontendProvider> frontendProvider)
-        : m_transactionId(transactionId)
-        , m_frontendProvider(frontendProvider) { }
-    int m_transactionId;
-    RefPtr<InspectorDatabaseAgent::FrontendProvider> m_frontendProvider;
+    TransactionErrorCallback(PassRefPtr<ExecuteSQLCallback> requestCallback)
+        : m_requestCallback(requestCallback) { }
+    RefPtr<ExecuteSQLCallback> m_requestCallback;
 };
 
 class TransactionSuccessCallback : public VoidCallback {
@@ -233,8 +204,8 @@
     RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
     m_resources.set(resource->id(), resource);
     // Resources are only bound while visible.
-    if (m_frontendProvider && m_enabled)
-        resource->bind(m_frontendProvider->frontend());
+    if (m_frontend && m_enabled)
+        resource->bind(m_frontend);
 }
 
 void InspectorDatabaseAgent::clearResources()
@@ -256,13 +227,12 @@
 
 void InspectorDatabaseAgent::setFrontend(InspectorFrontend* frontend)
 {
-    m_frontendProvider = FrontendProvider::create(frontend);
+    m_frontend = frontend->database();
 }
 
 void InspectorDatabaseAgent::clearFrontend()
 {
-    m_frontendProvider->clearFrontend();
-    m_frontendProvider.clear();
+    m_frontend = 0;
     disable(0);
 }
 
@@ -275,7 +245,7 @@
 
     DatabaseResourcesMap::iterator databasesEnd = m_resources.end();
     for (DatabaseResourcesMap::iterator it = m_resources.begin(); it != databasesEnd; ++it)
-        it->second->bind(m_frontendProvider->frontend());
+        it->second->bind(m_frontend);
 }
 
 void InspectorDatabaseAgent::disable(ErrorString*)
@@ -309,25 +279,25 @@
     }
 }
 
-void InspectorDatabaseAgent::executeSQL(ErrorString* error, const String& databaseId, const String& query, bool* success, int* transactionId)
+void InspectorDatabaseAgent::executeSQL(ErrorString*, const String& databaseId, const String& query, PassRefPtr<ExecuteSQLCallback> prpRequestCallback)
 {
+    RefPtr<ExecuteSQLCallback> requestCallback = prpRequestCallback;
+
     if (!m_enabled) {
-        *error = "Database agent is not enabled";
+        requestCallback->sendFailure("Database agent is not enabled");
         return;
     }
 
     Database* database = databaseForId(databaseId);
     if (!database) {
-        *success = false;
+        requestCallback->sendFailure("Database not found");
         return;
     }
 
-    *transactionId = ++lastTransactionId;
-    RefPtr<SQLTransactionCallback> callback(TransactionCallback::create(query, *transactionId, m_frontendProvider));
-    RefPtr<SQLTransactionErrorCallback> errorCallback(TransactionErrorCallback::create(*transactionId, m_frontendProvider));
+    RefPtr<SQLTransactionCallback> callback(TransactionCallback::create(query, requestCallback.get()));
+    RefPtr<SQLTransactionErrorCallback> errorCallback(TransactionErrorCallback::create(requestCallback.get()));
     RefPtr<VoidCallback> successCallback(TransactionSuccessCallback::create());
     database->transaction(callback.release(), errorCallback.release(), successCallback.release());
-    *success = true;
 }
 
 String InspectorDatabaseAgent::databaseId(Database* database)

Modified: trunk/Source/WebCore/inspector/InspectorDatabaseAgent.h (127860 => 127861)


--- trunk/Source/WebCore/inspector/InspectorDatabaseAgent.h	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/inspector/InspectorDatabaseAgent.h	2012-09-07 12:32:51 UTC (rev 127861)
@@ -32,6 +32,7 @@
 #if ENABLE(INSPECTOR) && ENABLE(SQL_DATABASE)
 
 #include "InspectorBaseAgent.h"
+#include "InspectorFrontend.h"
 #include <wtf/HashMap.h>
 #include <wtf/PassOwnPtr.h>
 #include <wtf/text/WTFString.h>
@@ -49,8 +50,6 @@
 
 class InspectorDatabaseAgent : public InspectorBaseAgent<InspectorDatabaseAgent>, public InspectorBackendDispatcher::DatabaseCommandHandler {
 public:
-    class FrontendProvider;
-
     static PassOwnPtr<InspectorDatabaseAgent> create(InstrumentingAgents* instrumentingAgents, InspectorState* state)
     {
         return adoptPtr(new InspectorDatabaseAgent(instrumentingAgents, state));
@@ -67,7 +66,7 @@
     virtual void enable(ErrorString*);
     virtual void disable(ErrorString*);
     virtual void getDatabaseTableNames(ErrorString*, const String& databaseId, RefPtr<TypeBuilder::Array<String> >& names);
-    virtual void executeSQL(ErrorString*, const String& databaseId, const String& query, bool* success, int* transactionId);
+    virtual void executeSQL(ErrorString*, const String& databaseId, const String& query, PassRefPtr<ExecuteSQLCallback>);
 
     // Called from the injected script.
     String databaseId(Database*);
@@ -79,9 +78,9 @@
     Database* databaseForId(const String& databaseId);
     InspectorDatabaseResource* findByFileName(const String& fileName);
 
+    InspectorFrontend::Database* m_frontend;
     typedef HashMap<String, RefPtr<InspectorDatabaseResource> > DatabaseResourcesMap;
     DatabaseResourcesMap m_resources;
-    RefPtr<FrontendProvider> m_frontendProvider;
     bool m_enabled;
 };
 

Modified: trunk/Source/WebCore/inspector/front-end/Database.js (127860 => 127861)


--- trunk/Source/WebCore/inspector/front-end/Database.js	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/inspector/front-end/Database.js	2012-09-07 12:32:51 UTC (rev 127861)
@@ -94,22 +94,35 @@
 
     /**
      * @param {string} query
-     * @param {function(Array.<string>, Array.<*>)} onSuccess
-     * @param {function(DatabaseAgent.Error)} onError
+     * @param {function(Array.<string>=, Array.<*>=)} onSuccess
+     * @param {function(string)} onError
      */
     executeSql: function(query, onSuccess, onError)
     {
-        function callback(error, success, transactionId)
+        /**
+         * @param {?Protocol.Error} error
+         * @param {Array.<string>=} columnNames
+         * @param {Array.<*>=} values
+         * @param {DatabaseAgent.Error=} errorObj
+         */
+        function callback(error, columnNames, values, errorObj)
         {
             if (error) {
                 onError(error);
                 return;
             }
-            if (!success) {
-                onError(WebInspector.UIString("Database not found."));
+            if (errorObj) {
+                var message;
+                if (errorObj.message)
+                    message = errorObj.message;
+                else if (errorObj.code == 2)
+                    message = WebInspector.UIString("Database no longer has expected version.");
+                else
+                    message = WebInspector.UIString("An unexpected error %s occurred.", errorObj.code);
+                onError(message);
                 return;
             }
-            this._model._callbacks[transactionId] = {"onSuccess": onSuccess, "onError": onError};
+            onSuccess(columnNames, values);
         }
         DatabaseAgent.executeSQL(this._id, query, callback.bind(this));
     }
@@ -121,7 +134,6 @@
  */
 WebInspector.DatabaseModel = function()
 {
-    this._callbacks = {};
     this._databases = [];
     InspectorBackend.registerDatabaseDispatcher(new WebInspector.DatabaseDispatcher(this));
     DatabaseAgent.enable();
@@ -159,37 +171,6 @@
     {
         this._databases.push(database);
         this.dispatchEventToListeners(WebInspector.DatabaseModel.Events.DatabaseAdded, database);
-    },
-
-    /**
-     * @param {number} transactionId
-     * @param {Array.<string>} columnNames
-     * @param {Array.<*>} values
-     */
-    _sqlTransactionSucceeded: function(transactionId, columnNames, values)
-    {
-        if (!this._callbacks[transactionId])
-            return;
-
-        var callback = this._callbacks[transactionId]["onSuccess"];
-        delete this._callbacks[transactionId];
-        if (callback)
-            callback(columnNames, values);
-    },
-
-    /**
-     * @param {number} transactionId
-     * @param {?DatabaseAgent.Error} errorObj
-     */
-    _sqlTransactionFailed: function(transactionId, errorObj)
-    {
-        if (!this._callbacks[transactionId])
-            return;
-
-        var callback = this._callbacks[transactionId]["onError"];
-        delete this._callbacks[transactionId];
-        if (callback)
-            callback(errorObj);
     }
 }
 
@@ -205,8 +186,6 @@
     this._model = model;
 }
 
-WebInspector.DatabaseDispatcher._callbacks = {};
-
 WebInspector.DatabaseDispatcher.prototype = {
     /**
      * @param {DatabaseAgent.Database} payload
@@ -219,25 +198,6 @@
             payload.domain,
             payload.name,
             payload.version));
-    },
-
-    /**
-     * @param {number} transactionId
-     * @param {Array.<string>} columnNames
-     * @param {Array.<*>} values
-     */
-    sqlTransactionSucceeded: function(transactionId, columnNames, values)
-    {
-        this._model._sqlTransactionSucceeded(transactionId, columnNames, values);
-    },
-
-    /**
-     * @param {number} transactionId
-     * @param {?DatabaseAgent.Error} errorObj
-     */
-    sqlTransactionFailed: function(transactionId, errorObj)
-    {
-        this._model._sqlTransactionFailed(transactionId, errorObj);
     }
 }
 

Modified: trunk/Source/WebCore/inspector/front-end/DatabaseQueryView.js (127860 => 127861)


--- trunk/Source/WebCore/inspector/front-end/DatabaseQueryView.js	2012-09-07 12:29:55 UTC (rev 127860)
+++ trunk/Source/WebCore/inspector/front-end/DatabaseQueryView.js	2012-09-07 12:32:51 UTC (rev 127861)
@@ -148,18 +148,9 @@
             this.dispatchEventToListeners(WebInspector.DatabaseQueryView.Events.SchemaUpdated, this.database);
     },
 
-    _queryError: function(query, error)
+    _queryError: function(query, errorMessage)
     {
-        if (typeof error === "string")
-            var message = error;
-        else if (error.message)
-            var message = error.message;
-        else if (error.code == 2)
-            var message = WebInspector.UIString("Database no longer has expected version.");
-        else
-            var message = WebInspector.UIString("An unexpected error %s occurred.", error.code);
-
-        this._appendErrorQueryResult(query, message);
+        this._appendErrorQueryResult(query, errorMessage);
     },
 
     /**
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to