| /** | |
| * Copyright 2020 Comcast Cable Communications Management, LLC | |
| * | |
| * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| * | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| #include "Cobalt.h" | |
| namespace WPEFramework { | |
| namespace Cobalt { | |
| class MemoryObserverImpl: public Exchange::IMemory { | |
| private: | |
| MemoryObserverImpl(); | |
| MemoryObserverImpl(const MemoryObserverImpl&); | |
| MemoryObserverImpl& operator=(const MemoryObserverImpl&); | |
| public: | |
| MemoryObserverImpl(const RPC::IRemoteConnection* connection) : | |
| _main(connection == nullptr ? Core::ProcessInfo().Id() : connection->RemoteId()) { | |
| } | |
| ~MemoryObserverImpl() { | |
| } | |
| public: | |
| virtual uint64_t Resident() const { | |
| return _main.Resident(); | |
| } | |
| virtual uint64_t Allocated() const { | |
| return _main.Allocated(); | |
| } | |
| virtual uint64_t Shared() const { | |
| return _main.Shared(); | |
| } | |
| virtual uint8_t Processes() const { | |
| return (IsOperational() ? 1 : 0); | |
| } | |
| #if THUNDER_VERSION == 4 | |
| virtual bool IsOperational() const { | |
| #else | |
| virtual const bool IsOperational() const { | |
| #endif | |
| return _main.IsActive(); | |
| } | |
| BEGIN_INTERFACE_MAP (MemoryObserverImpl) | |
| INTERFACE_ENTRY (Exchange::IMemory) | |
| END_INTERFACE_MAP | |
| private: | |
| Core::ProcessInfo _main; | |
| }; | |
| Exchange::IMemory* MemoryObserver(const RPC::IRemoteConnection* connection) { | |
| ASSERT(connection != nullptr); | |
| Exchange::IMemory* result = Core::Service<MemoryObserverImpl>::Create<Exchange::IMemory>(connection); | |
| return (result); | |
| } | |
| } // namespace Cobalt | |
| namespace Plugin { | |
| SERVICE_REGISTRATION(Cobalt, 1, 0); | |
| static Core::ProxyPoolType<Web::TextBody> _textBodies(2); | |
| static Core::ProxyPoolType<Web::JSONBodyType<Cobalt::Data>> jsonBodyDataFactory(2); | |
| /* encapsulated class Thread */ | |
| const string Cobalt::Initialize(PluginHost::IShell *service) { | |
| Config config; | |
| string message; | |
| ASSERT(_service == nullptr); | |
| ASSERT(_cobalt == nullptr); | |
| ASSERT(_memory == nullptr); | |
| config.FromString(service->ConfigLine()); | |
| _connectionId = 0; | |
| _service = service; | |
| _skipURL = _service->WebPrefix().length(); | |
| // Register the Connection::Notification stuff. The Remote process might die | |
| // before we get a | |
| // change to "register" the sink for these events !!! So do it ahead of | |
| // instantiation. | |
| _service->Register(&_notification); | |
| _cobalt = _service->Root < Exchange::IBrowser > (_connectionId, 20000, _T("CobaltImplementation")); | |
| if (_cobalt != nullptr) { | |
| PluginHost::IStateControl *stateControl( | |
| _cobalt->QueryInterface<PluginHost::IStateControl>()); | |
| if (stateControl == nullptr) { | |
| _cobalt->Release(); | |
| _cobalt = nullptr; | |
| } else { | |
| RPC::IRemoteConnection* remoteConnection = _service->RemoteConnection(_connectionId); | |
| _memory = WPEFramework::Cobalt::MemoryObserver(remoteConnection); | |
| ASSERT(_memory != nullptr); | |
| if (remoteConnection) | |
| remoteConnection->Release(); | |
| _cobalt->Register(&_notification); | |
| stateControl->Register(&_notification); | |
| stateControl->Configure(_service); | |
| stateControl->Release(); | |
| } | |
| } | |
| if (_cobalt == nullptr) { | |
| message = _T("Cobalt could not be instantiated."); | |
| _service->Unregister(&_notification); | |
| ConnectionTermination(_connectionId); | |
| _service = nullptr; | |
| } | |
| return message; | |
| } | |
| void Cobalt::Deinitialize(PluginHost::IShell *service) { | |
| ASSERT(_service == service); | |
| ASSERT(_cobalt != nullptr); | |
| ASSERT(_memory != nullptr); | |
| if (_cobalt == nullptr) | |
| return; | |
| PluginHost::IStateControl *stateControl( | |
| _cobalt->QueryInterface<PluginHost::IStateControl>()); | |
| // Make sure the Activated and Deactivated are no longer called before we | |
| // start cleaning up.. | |
| _service->Unregister(&_notification); | |
| _cobalt->Unregister(&_notification); | |
| _memory->Release(); | |
| // In case Cobalt crashed, there is no access to the statecontrol interface, | |
| // check it !! | |
| if (stateControl != nullptr) { | |
| stateControl->Unregister(&_notification); | |
| stateControl->Release(); | |
| } else { | |
| // On behalf of the crashed process, we will release the notification sink. | |
| _notification.Release(); | |
| } | |
| if (_cobalt->Release() != Core::ERROR_DESTRUCTION_SUCCEEDED) { | |
| ASSERT(_connectionId != 0); | |
| TRACE_L1("Cobalt Plugin is not properly destructed. %d", _connectionId); | |
| ConnectionTermination(_connectionId); | |
| } | |
| // Deinitialize what we initialized.. | |
| _memory = nullptr; | |
| _cobalt = nullptr; | |
| _service = nullptr; | |
| } | |
| string Cobalt::Information() const { | |
| // No additional info to report. | |
| return (string()); | |
| } | |
| void Cobalt::Inbound(Web::Request &request) { | |
| if (request.Verb == Web::Request::HTTP_POST) { | |
| // This might be a "launch" application thingy, make sure we receive the | |
| // proper info. | |
| request.Body(jsonBodyDataFactory.Element()); | |
| } | |
| } | |
| Core::ProxyType<Web::Response> Cobalt::Process(const Web::Request &request) { | |
| ASSERT(_skipURL <= request.Path.length()); | |
| TRACE(Trace::Information, (string(_T("Received cobalt request")))); | |
| Core::ProxyType<Web::Response> result(PluginHost::IFactories::Instance().Response()); | |
| Core::TextSegmentIterator index( | |
| Core::TextFragment(request.Path, _skipURL, | |
| request.Path.length() - _skipURL), false, '/'); | |
| result->ErrorCode = Web::STATUS_BAD_REQUEST; | |
| result->Message = "Unknown error"; | |
| if (request.Verb == Web::Request::HTTP_POST) { | |
| // We might be receiving a plugin download request. | |
| if ((index.Next() == true) && (index.Next() == true) | |
| && (_cobalt != nullptr)) { | |
| PluginHost::IStateControl *stateControl( | |
| _cobalt->QueryInterface<PluginHost::IStateControl>()); | |
| if (stateControl != nullptr) { | |
| if (index.Remainder() == _T("Suspend")) { | |
| stateControl->Request(PluginHost::IStateControl::SUSPEND); | |
| } else if (index.Remainder() == _T("Resume")) { | |
| stateControl->Request(PluginHost::IStateControl::RESUME); | |
| } else if ((index.Remainder() == _T("URL")) | |
| && (request.HasBody() == true) | |
| && (request.Body<const Data>()->URL.Value().empty() | |
| == false)) { | |
| _cobalt->SetURL(request.Body<const Data>()->URL.Value()); | |
| } | |
| stateControl->Release(); | |
| } | |
| } | |
| } | |
| else if (request.Verb == Web::Request::HTTP_GET) { | |
| } | |
| return result; | |
| } | |
| void Cobalt::LoadFinished(const string &URL) { | |
| string message( | |
| string("{ \"url\": \"") + URL + string("\", \"loaded\":true }")); | |
| TRACE(Trace::Information, (_T("LoadFinished: %s"), message.c_str())); | |
| _service->Notify(message); | |
| event_urlchange(URL, true); | |
| } | |
| void Cobalt::URLChanged(const string &URL) { | |
| string message(string("{ \"url\": \"") + URL + string("\" }")); | |
| TRACE(Trace::Information, (_T("URLChanged: %s"), message.c_str())); | |
| _service->Notify(message); | |
| event_urlchange(URL, false); | |
| } | |
| void Cobalt::Hidden(const bool hidden) { | |
| if (_hidden == hidden) | |
| return; | |
| TRACE(Trace::Information, | |
| (_T("Hidden: %s"), (hidden ? "true" : "false"))); | |
| string message( | |
| string("{ \"hidden\": ") + (hidden ? _T("true") : _T("false")) | |
| + string("}")); | |
| _hidden = hidden; | |
| _service->Notify(message); | |
| event_visibilitychange(hidden); | |
| } | |
| void Cobalt::StateChange(const PluginHost::IStateControl::state state) { | |
| switch (state) { | |
| case PluginHost::IStateControl::RESUMED: | |
| TRACE(Trace::Information, | |
| (string(_T("StateChange: { \"suspend\":false }")))); | |
| _service->Notify("{ \"suspended\":false }"); | |
| event_statechange(false); | |
| break; | |
| case PluginHost::IStateControl::SUSPENDED: | |
| TRACE(Trace::Information, | |
| (string(_T("StateChange: { \"suspend\":true }")))); | |
| _service->Notify("{ \"suspended\":true }"); | |
| event_statechange(true); | |
| break; | |
| case PluginHost::IStateControl::EXITED: | |
| // Exited by Cobalt app | |
| Core::IWorkerPool::Instance().Submit( | |
| PluginHost::IShell::Job::Create(_service, | |
| PluginHost::IShell::DEACTIVATED, | |
| PluginHost::IShell::REQUESTED)); | |
| break; | |
| case PluginHost::IStateControl::UNINITIALIZED: | |
| break; | |
| default: | |
| ASSERT(false); | |
| break; | |
| } | |
| } | |
| void Cobalt::Deactivated(RPC::IRemoteConnection *connection) { | |
| if (connection->Id() == _connectionId) { | |
| ASSERT(_service != nullptr); | |
| Core::IWorkerPool::Instance().Submit( | |
| PluginHost::IShell::Job::Create(_service, | |
| PluginHost::IShell::DEACTIVATED, | |
| PluginHost::IShell::FAILURE)); | |
| } | |
| } | |
| void Cobalt::Closure() { | |
| TRACE(Trace::Information, (_T("Closure: \"true\""))); | |
| _service->Notify(_T("{\"Closure\": true }")); | |
| event_closure(); | |
| } | |
| } // namespace Plugin | |
| } // namespace WPEFramework |