/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla IPC. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include "plstr.h" #include "nsIServiceManager.h" #include "nsIObserverService.h" #include "nsICategoryManager.h" #include "nsCategoryManagerUtils.h" #include "netCore.h" #include "ipcConfig.h" #include "ipcLog.h" #include "ipcService.h" #include "ipcMessageUtils.h" #include "ipcm.h" //----------------------------------------------------------------------------- // helpers //----------------------------------------------------------------------------- static PRBool PR_CALLBACK ipcReleaseMessageObserver(nsHashKey *aKey, void *aData, void* aClosure) { ipcIMessageObserver *obs = (ipcIMessageObserver *) aData; NS_RELEASE(obs); return PR_TRUE; } //---------------------------------------------------------------------------- // ipcClientQuery //---------------------------------------------------------------------------- class ipcClientQuery { public: ipcClientQuery(PRUint32 cID, ipcIClientQueryHandler *handler) : mNext(nsnull) , mQueryID(++gLastQueryID) , mClientID(cID) , mHandler(handler) { } static PRUint32 gLastQueryID; void SetClientID(PRUint32 cID) { mClientID = cID; } void OnQueryComplete(nsresult status, const ipcmMessageClientInfo *msg); PRUint32 QueryID() { return mQueryID; } PRBool IsCanceled() { return mHandler.get() == NULL; } ipcClientQuery *mNext; private: PRUint32 mQueryID; PRUint32 mClientID; nsCOMPtr mHandler; }; PRUint32 ipcClientQuery::gLastQueryID = 0; void ipcClientQuery::OnQueryComplete(nsresult status, const ipcmMessageClientInfo *msg) { NS_ASSERTION(mHandler, "no handler"); PRUint32 nameCount = 0; PRUint32 targetCount = 0; const char **names = NULL; const nsID **targets = NULL; if (NS_SUCCEEDED(status)) { nameCount = msg->NameCount(); targetCount = msg->TargetCount(); PRUint32 i; names = (const char **) malloc(nameCount * sizeof(char *)); const char *lastName = NULL; for (i = 0; i < nameCount; ++i) { lastName = msg->NextName(lastName); names[i] = lastName; } targets = (const nsID **) malloc(targetCount * sizeof(nsID *)); const nsID *lastTarget = NULL; for (i = 0; i < targetCount; ++i) { lastTarget = msg->NextTarget(lastTarget); targets[i] = lastTarget; } } mHandler->OnQueryComplete(mQueryID, status, mClientID, names, nameCount, targets, targetCount); mHandler = NULL; if (names) free(names); if (targets) free(targets); } //----------------------------------------------------------------------------- // ipcService //----------------------------------------------------------------------------- ipcService::ipcService() : mTransport(nsnull) , mClientID(0) { NS_INIT_ISUPPORTS(); IPC_InitLog(">>>"); } ipcService::~ipcService() { if (mTransport) { mTransport->Shutdown(); NS_RELEASE(mTransport); } mObserverDB.Reset(ipcReleaseMessageObserver, nsnull); } nsresult ipcService::Init() { nsresult rv; mTransport = new ipcTransport(); if (!mTransport) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(mTransport); rv = mTransport->Init(this); if (NS_FAILED(rv)) return rv; return NS_OK; } void ipcService::OnIPCMClientID(const ipcmMessageClientID *msg) { LOG(("ipcService::OnIPCMClientID\n")); ipcClientQuery *query = mQueryQ.First(); if (!query) { NS_WARNING("no pending query; ignoring message."); return; } PRUint32 cID = msg->ClientID(); // // (1) store client ID in query // (2) move query to end of queue // (3) issue CLIENT_INFO request // query->SetClientID(cID); mQueryQ.RemoveFirst(); mQueryQ.Append(query); mTransport->SendMsg(new ipcmMessageQueryClientInfo(cID)); } void ipcService::OnIPCMClientInfo(const ipcmMessageClientInfo *msg) { LOG(("ipcService::OnIPCMClientInfo\n")); ipcClientQuery *query = mQueryQ.First(); if (!query) { NS_WARNING("no pending query; ignoring message."); return; } if (!query->IsCanceled()) query->OnQueryComplete(NS_OK, msg); mQueryQ.DeleteFirst(); } void ipcService::OnIPCMError(const ipcmMessageError *msg) { LOG(("ipcService::OnIPCMError [reason=0x%08x]\n", msg->Reason())); ipcClientQuery *query = mQueryQ.First(); if (!query) { NS_WARNING("no pending query; ignoring message."); return; } if (!query->IsCanceled()) query->OnQueryComplete(NS_ERROR_FAILURE, NULL); mQueryQ.DeleteFirst(); } //----------------------------------------------------------------------------- // interface impl //----------------------------------------------------------------------------- NS_IMPL_ISUPPORTS1(ipcService, ipcIService) NS_IMETHODIMP ipcService::GetClientID(PRUint32 *clientID) { if (mClientID == 0) return NS_ERROR_NOT_AVAILABLE; *clientID = mClientID; return NS_OK; } NS_IMETHODIMP ipcService::AddClientName(const nsACString &name) { NS_ENSURE_TRUE(mTransport, NS_ERROR_NOT_INITIALIZED); ipcMessage *msg = new ipcmMessageClientAddName(PromiseFlatCString(name).get()); if (!msg) return NS_ERROR_OUT_OF_MEMORY; return mTransport->SendMsg(msg); } NS_IMETHODIMP ipcService::RemoveClientName(const nsACString &name) { NS_ENSURE_TRUE(mTransport, NS_ERROR_NOT_INITIALIZED); ipcMessage *msg = new ipcmMessageClientDelName(PromiseFlatCString(name).get()); if (!msg) return NS_ERROR_OUT_OF_MEMORY; return mTransport->SendMsg(msg); } NS_IMETHODIMP ipcService::QueryClientByName(const nsACString &name, ipcIClientQueryHandler *handler, PRUint32 *queryID) { if (!mTransport) return NS_ERROR_NOT_AVAILABLE; ipcMessage *msg; msg = new ipcmMessageQueryClientByName(PromiseFlatCString(name).get()); if (!msg) return NS_ERROR_OUT_OF_MEMORY; nsresult rv; rv = mTransport->SendMsg(msg); if (NS_FAILED(rv)) return rv; ipcClientQuery *query = new ipcClientQuery(0, handler); if (queryID) *queryID = query->QueryID(); mQueryQ.Append(query); return NS_OK; } NS_IMETHODIMP ipcService::QueryClientByID(PRUint32 clientID, ipcIClientQueryHandler *handler, PRUint32 *queryID) { if (!mTransport) return NS_ERROR_NOT_AVAILABLE; ipcMessage *msg; msg = new ipcmMessageQueryClientInfo(clientID); if (!msg) return NS_ERROR_OUT_OF_MEMORY; nsresult rv; rv = mTransport->SendMsg(msg); if (NS_FAILED(rv)) return rv; ipcClientQuery *query = new ipcClientQuery(clientID, handler); if (queryID) *queryID = query->QueryID(); mQueryQ.Append(query); return NS_OK; } NS_IMETHODIMP ipcService::CancelQuery(PRUint32 queryID) { ipcClientQuery *query = mQueryQ.First(); while (query) { if (query->QueryID() == queryID) { query->OnQueryComplete(NS_BINDING_ABORTED, NULL); break; } query = query->mNext; } return NS_OK; } NS_IMETHODIMP ipcService::SetClientObserver(ipcIClientObserver *observer) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP ipcService::SetMessageObserver(const nsID &target, ipcIMessageObserver *observer) { NS_ENSURE_TRUE(mTransport, NS_ERROR_NOT_INITIALIZED); nsIDKey key(target); PRBool sendAdd = PR_TRUE; ipcIMessageObserver *cobs = (ipcIMessageObserver *) mObserverDB.Get(&key); if (cobs) { NS_RELEASE(cobs); if (!observer) { mObserverDB.Remove(&key); // // send CLIENT_DEL_TARGET // mTransport->SendMsg(new ipcmMessageClientDelTarget(target)); return NS_OK; } sendAdd = PR_FALSE; } if (observer) { NS_ADDREF(observer); mObserverDB.Put(&key, observer); if (sendAdd) { // // send CLIENT_ADD_TARGET // mTransport->SendMsg(new ipcmMessageClientAddTarget(target)); } } return NS_OK; } NS_IMETHODIMP ipcService::SendMessage(PRUint32 clientID, const nsID &target, const PRUint8 *data, PRUint32 dataLen) { NS_ENSURE_TRUE(mTransport, NS_ERROR_NOT_INITIALIZED); if (target.Equals(IPCM_TARGET)) { NS_ERROR("do not try to talk to the IPCM target directly"); return NS_ERROR_INVALID_ARG; } ipcMessage *msg; if (clientID) msg = new ipcmMessageForward(clientID, target, (const char *) data, dataLen); else msg = new ipcMessage(target, (const char *) data, dataLen); if (!msg) return NS_ERROR_OUT_OF_MEMORY; return mTransport->SendMsg(msg); } //----------------------------------------------------------------------------- // ipcTransportObserver impl //----------------------------------------------------------------------------- void ipcService::OnConnectionEstablished(PRUint32 clientID) { mClientID = clientID; // // enumerate ipc startup category... // NS_CreateServicesFromCategory(IPC_SERVICE_STARTUP_CATEGORY, NS_STATIC_CAST(nsISupports *, this), IPC_SERVICE_STARTUP_TOPIC); } void ipcService::OnConnectionLost() { mClientID = 0; // // error out any pending queries // while (mQueryQ.First()) { ipcClientQuery *query = mQueryQ.First(); query->OnQueryComplete(NS_BINDING_ABORTED, NULL); mQueryQ.DeleteFirst(); } // // broadcast ipc shutdown... // nsCOMPtr observ( do_GetService("@mozilla.org/observer-service;1")); if (observ) observ->NotifyObservers(NS_STATIC_CAST(nsISupports *, this), IPC_SERVICE_SHUTDOWN_TOPIC, nsnull); } void ipcService::OnMessageAvailable(const ipcMessage *msg) { if (msg->Target().Equals(IPCM_TARGET)) { // // all IPCM messages stop here. // PRUint32 type = IPCM_GetMsgType(msg); switch (type) { case IPCM_MSG_TYPE_CLIENT_ID: OnIPCMClientID((const ipcmMessageClientID *) msg); break; case IPCM_MSG_TYPE_CLIENT_INFO: OnIPCMClientInfo((const ipcmMessageClientInfo *) msg); break; case IPCM_MSG_TYPE_ERROR: OnIPCMError((const ipcmMessageError *) msg); break; } } else { nsIDKey key(msg->Target()); ipcIMessageObserver *observer = (ipcIMessageObserver *) mObserverDB.Get(&key); if (observer) observer->OnMessageAvailable(msg->Target(), (const PRUint8 *) msg->Data(), msg->DataLen()); } }