Skip to content

Commit

Permalink
Merge pull request #29 from rojkov/stateful-embedliteapp
Browse files Browse the repository at this point in the history
Make EmbedLiteApp aware of its internal state
  • Loading branch information
tmeshkova committed Nov 6, 2014
2 parents 1efc815 + 14824c9 commit 6492e88
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 41 deletions.
128 changes: 89 additions & 39 deletions embedding/embedlite/EmbedLiteApp.cpp
@@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Expand Down Expand Up @@ -50,7 +50,7 @@ EmbedLiteApp::EmbedLiteApp()
, mAppChild(NULL)
, mEmbedType(EMBED_INVALID)
, mViewCreateID(0)
, mDestroying(false)
, mState(STOPPED)
, mRenderType(RENDER_AUTO)
, mProfilePath(strdup("mozembed"))
, mIsAsyncLoop(false)
Expand All @@ -64,6 +64,7 @@ EmbedLiteApp::~EmbedLiteApp()
LOGT();
NS_ASSERTION(!mUILoop, "Main Loop not stopped before destroy");
NS_ASSERTION(!mSubThread, "Thread not stopped/destroyed before destroy");
NS_ASSERTION(mState == STOPPED, "Pre-mature deletion of still running application");
sSingleton = NULL;
if (mProfilePath) {
free(mProfilePath);
Expand Down Expand Up @@ -124,9 +125,11 @@ void
EmbedLiteApp::StartChild(EmbedLiteApp* aApp)
{
LOGT();
NS_ASSERTION(aApp->mState == STARTING, "Wrong timing");
if (aApp->mEmbedType == EMBED_THREAD) {
if (!aApp->mListener ||
!aApp->mListener->ExecuteChildThread()) {
// If toolkit hasn't started a child thread we have to create the thread on our own
aApp->mSubThread = new EmbedLiteSubThread(aApp);
if (!aApp->mSubThread->StartEmbedThread()) {
LOGE("Failed to start child thread");
Expand All @@ -138,7 +141,7 @@ EmbedLiteApp::StartChild(EmbedLiteApp* aApp)
void
EmbedLiteApp::SetProfilePath(const char* aPath)
{
NS_ASSERTION(mEmbedType == EMBED_INVALID, "SetProfilePath must be called before Start");
NS_ASSERTION(mState == STOPPED, "SetProfilePath must be called before Start");
if (mProfilePath)
free(mProfilePath);

Expand All @@ -155,7 +158,9 @@ bool
EmbedLiteApp::StartWithCustomPump(EmbedType aEmbedType, EmbedLiteMessagePump* aEventLoop)
{
LOGT("Type: %s", aEmbedType == EMBED_THREAD ? "Thread" : "Process");
NS_ASSERTION(mState == STOPPED, "App can be started only when it stays still");
NS_ASSERTION(!mUILoop, "Start called twice");
SetState(STARTING);
mEmbedType = aEmbedType;
mUILoop = aEventLoop->GetMessageLoop();
mUILoop->PostTask(FROM_HERE,
Expand All @@ -169,7 +174,9 @@ bool
EmbedLiteApp::Start(EmbedType aEmbedType)
{
LOGT("Type: %s", aEmbedType == EMBED_THREAD ? "Thread" : "Process");
NS_ASSERTION(mState == STOPPED, "App can be started only when it stays still");
NS_ASSERTION(!mUILoop, "Start called twice");
SetState(STARTING);
mEmbedType = aEmbedType;
base::AtExitManager exitManager;
mUILoop = new EmbedLiteUILoop();
Expand Down Expand Up @@ -198,10 +205,10 @@ EmbedLiteApp::Start(EmbedType aEmbedType)
void
EmbedLiteApp::AddManifestLocation(const char* manifest)
{
if (!mAppParent) {
sComponentDirs.AppendElement(nsCString(manifest));
} else {
if (mState == INITIALIZED) {
unused << mAppParent->SendLoadComponentManifest(nsDependentCString(manifest));
} else {
sComponentDirs.AppendElement(nsCString(manifest));
}
}

Expand Down Expand Up @@ -256,7 +263,7 @@ EmbedLiteApp::StopChildThread()

void _FinalStop(EmbedLiteApp* app)
{
app->Stop();
app->Shutdown();
}

void
Expand All @@ -269,102 +276,127 @@ void
EmbedLiteApp::Stop()
{
LOGT();
if (!mViews.empty()) {
std::map<uint32_t, EmbedLiteView*>::iterator it;
for (it = mViews.begin(); it != mViews.end(); it++) {
EmbedLiteView* view = it->second;
delete view;
it->second = nullptr;
}
mDestroying = true;
} else if (!mDestroying) {
mDestroying = true;
mUILoop->PostTask(FROM_HERE,
NewRunnableFunction(&EmbedLiteApp::PreDestroy, this));
} else {
NS_ASSERTION(mUILoop, "Start was not called before stop");
mUILoop->DoQuit();
if (mIsAsyncLoop) {
if (mSubThread) {
mSubThread->Stop();
mSubThread = NULL;
} else if (mListener) {
NS_ABORT_IF_FALSE(mListener->StopChildThread(),
"StopChildThread must be implemented when ExecuteChildThread defined");
}
if (mUILoop && !mIsAsyncLoop) {
delete mUILoop;
NS_ASSERTION(mState == STARTING || mState == INITIALIZED, "Wrong timing");

if (mState == INITIALIZED) {
if (mViews.empty()) {
mUILoop->PostTask(FROM_HERE,
NewRunnableFunction(&EmbedLiteApp::PreDestroy, this));
} else {
std::map<uint32_t, EmbedLiteView*>::iterator it;
for (it = mViews.begin(); it != mViews.end(); it++) {
EmbedLiteView* view = it->second;
delete view;
it->second = nullptr;
// NOTE: we still keep dangling keys here. They are supposed to be erased in ViewDestroyed().
}
mUILoop = NULL;
}
}

if (mListener) {
mListener->Destroyed();
}
SetState(DESTROYING);
}

void
EmbedLiteApp::Shutdown()
{
LOGT();
NS_ASSERTION(mState == DESTROYING, "Wrong timing");

if (mIsAsyncLoop) {
if (mSubThread) {
mSubThread->Stop();
mSubThread = NULL;
} else if (mListener) {
NS_ABORT_IF_FALSE(mListener->StopChildThread(),
"StopChildThread must be implemented when ExecuteChildThread defined");
}
}

mUILoop->DoQuit();

if (mIsAsyncLoop) {
delete mUILoop;
mUILoop = nullptr;
}

if (mListener) {
mListener->Destroyed();
}

SetState(STOPPED);
}

void
EmbedLiteApp::SetBoolPref(const char* aName, bool aValue)
{
NS_ENSURE_TRUE(mState == INITIALIZED, );
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendSetBoolPref(nsDependentCString(aName), aValue);
}

void
EmbedLiteApp::SetCharPref(const char* aName, const char* aValue)
{
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendSetCharPref(nsDependentCString(aName), nsDependentCString(aValue));
}

void
EmbedLiteApp::SetIntPref(const char* aName, int aValue)
{
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendSetIntPref(nsDependentCString(aName), aValue);
}

void
EmbedLiteApp::LoadGlobalStyleSheet(const char* aUri, bool aEnable)
{
LOGT();
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendLoadGlobalStyleSheet(nsDependentCString(aUri), aEnable);
}

void
EmbedLiteApp::SendObserve(const char* aMessageName, const char16_t* aMessage)
{
LOGT("topic:%s", aMessageName);
NS_ENSURE_TRUE(mAppParent, );
NS_ENSURE_TRUE(mState == INITIALIZED, );
unused << mAppParent->SendObserve(nsDependentCString(aMessageName), aMessage ? nsDependentString((const char16_t*)aMessage) : nsString());
}

void
EmbedLiteApp::AddObserver(const char* aMessageName)
{
LOGT("topic:%s", aMessageName);
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendAddObserver(nsDependentCString(aMessageName));
}

void
EmbedLiteApp::RemoveObserver(const char* aMessageName)
{
LOGT("topic:%s", aMessageName);
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendRemoveObserver(nsDependentCString(aMessageName));
}

void EmbedLiteApp::AddObservers(nsTArray<nsCString>& observersList)
{
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendAddObservers(observersList);
}

void EmbedLiteApp::RemoveObservers(nsTArray<nsCString>& observersList)
{
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
unused << mAppParent->SendRemoveObservers(observersList);
}

EmbedLiteView*
EmbedLiteApp::CreateView(uint32_t aParent)
{
LOGT();
NS_ASSERTION(mState == INITIALIZED, "The app must be up and runnning by now");
mViewCreateID++;
EmbedLiteView* view = new EmbedLiteView(this, mViewCreateID, aParent);
mViews[mViewCreateID] = view;
Expand All @@ -386,7 +418,7 @@ void
EmbedLiteApp::ChildReadyToDestroy()
{
LOGT();
if (mDestroying) {
if (mState == DESTROYING) {
mUILoop->PostTask(FROM_HERE,
NewRunnableFunction(&_FinalStop, this));
}
Expand Down Expand Up @@ -416,7 +448,7 @@ EmbedLiteApp::ViewDestroyed(uint32_t id)
if (it != mViews.end()) {
mViews.erase(it);
}
if (mDestroying && mViews.empty()) {
if (mState == DESTROYING && mViews.empty()) {
mUILoop->PostTask(FROM_HERE,
NewRunnableFunction(&EmbedLiteApp::PreDestroy, this));
}
Expand All @@ -425,6 +457,7 @@ EmbedLiteApp::ViewDestroyed(uint32_t id)
void EmbedLiteApp::DestroyView(EmbedLiteView* aView)
{
LOGT();
NS_ASSERTION(mState == INITIALIZED, "Wrong timing");
std::map<uint32_t, EmbedLiteView*>::iterator it;
for (it = mViews.begin(); it != mViews.end(); it++) {
if (it->second == aView) {
Expand Down Expand Up @@ -453,11 +486,28 @@ EmbedLiteApp::SetIsAccelerated(bool aIsAccelerated)
void
EmbedLiteApp::Initialized()
{
LOGT();
NS_ASSERTION(mState == STARTING || mState == DESTROYING, "Wrong timing");

if (mState == DESTROYING) {
mUILoop->PostTask(FROM_HERE,
NewRunnableFunction(&EmbedLiteApp::PreDestroy, this));
return;
}

SetState(INITIALIZED);
if (mListener) {
mListener->Initialized();
}
}

void
EmbedLiteApp::SetState(State aState)
{
LOGT("State transition: %d -> %d", mState, aState);
mState = aState;
}

} // namespace embedlite
} // namespace mozilla

Expand Down
31 changes: 29 additions & 2 deletions embedding/embedlite/EmbedLiteApp.h
@@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Expand Down Expand Up @@ -132,9 +132,36 @@ class EmbedLiteApp
// Only one EmbedHelper object allowed
static EmbedLiteApp* GetInstance();

// TODO: this must be hidden for API users
void Shutdown();

private:
EmbedLiteApp();

/*
* States of EmbedLiteApp's lifecycle
*/
enum State {
// This is the initial and final state of EmbedLiteApp's lifecycle
// Allowed next states: STARTING
STOPPED,

// The app is in a start-up process. No messages can be sent to the
// EmbedLiteApp instance from a toolkit in this state except Stop().
// Allowed next states: INITIALIZED, DESTROYING
STARTING,

// The app is ready to operate with views.
// Allowed next states: DESTROYING
INITIALIZED,

// The app is in shutdown process.
// Allowed next states: STOPPED
DESTROYING
};

void SetState(State aState);

static void StartChild(EmbedLiteApp* aApp);
void Initialized();

Expand Down Expand Up @@ -162,7 +189,7 @@ class EmbedLiteApp
EmbedType mEmbedType;
std::map<uint32_t, EmbedLiteView*> mViews;
uint32_t mViewCreateID;
bool mDestroying;
State mState;
RenderType mRenderType;
char* mProfilePath;
bool mIsAsyncLoop;
Expand Down

0 comments on commit 6492e88

Please sign in to comment.