зеркало из https://github.com/mozilla/gecko-dev.git
532 строки
17 KiB
C++
532 строки
17 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#include "UDeferredTask.h"
|
|
|
|
#include <LCommander.h>
|
|
#include <LListener.h>
|
|
|
|
#include "CWindowMenu.h"
|
|
#include "CNetscapeWindow.h"
|
|
#include <typeinfo>
|
|
|
|
CDeferredTaskManager* CDeferredTaskManager::sManager = nil;
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTaskManager::CDeferredTaskManager()
|
|
//----------------------------------------------------------------------------------------
|
|
: mQueueList(nil)
|
|
{
|
|
sManager = this;
|
|
StartIdling();
|
|
} // CDeferredTaskManager::CDeferredTaskManager
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTaskManager::~CDeferredTaskManager()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
sManager = nil;
|
|
} // CDeferredTaskManager::CDeferredTaskManager
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
/*static*/ void CDeferredTaskManager::DoQuit(Int32 /*inSaveOption*/)
|
|
// Called from uapp.cp. Allow any pending tasks to complete before quitting.
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
// Allow all the close tasks to finish. When the last task is done, sManager should
|
|
// be set to null.
|
|
EventRecord stupidNullEvent = {0};
|
|
for (int timeOutCounter = 0; sManager && timeOutCounter < 500; timeOutCounter++)
|
|
{
|
|
sManager->DoExecuteTasks(); // netlib might be needed.
|
|
DevoteTimeToRepeaters(stupidNullEvent);
|
|
}
|
|
Assert_(!sManager); // ow! we ran 500 times and the manager is still alive!
|
|
} // CDeferredTaskManager::DoQuit
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
/* static */ void CDeferredTaskManager::Post(CDeferredTask* inTask, LPane* inPane, bool inUnique)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (!inTask)
|
|
return;
|
|
try
|
|
{
|
|
// If this is the first post, we'll make a new manager
|
|
if (!sManager)
|
|
new CDeferredTaskManager;
|
|
sManager->DoPost(inTask, inPane, inUnique);
|
|
}
|
|
catch(...)
|
|
{
|
|
}
|
|
} // CDeferredTaskManager::Post
|
|
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
/* static */ void CDeferredTaskManager::Remove(CDeferredTask*& inTask, LPane* inPane)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
Assert_(sManager);
|
|
if (sManager)
|
|
sManager->DoRemove(inTask, inPane);
|
|
// Task may already have executed and been removed. Always zero the caller's stale
|
|
// pointer.
|
|
inTask = nil;
|
|
} // CDeferredTaskManager::Remove
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
/* static */ void CDeferredTaskManager::ClearQueue(LPane* inPane)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
// If there's no sManager, then there are no queues, so there's nothing to do.
|
|
if (!sManager)
|
|
return;
|
|
sManager->DoClearQueue(inPane);
|
|
} // CDeferredTaskManager::Remove
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::DoPost(CDeferredTask* inTask, LPane* inPane, Boolean inUnique)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
LWindow* window = nil;
|
|
if (inPane)
|
|
window = LWindow::FetchWindowObject(inPane->GetMacPort());
|
|
|
|
// Try to find a queue matching this window:
|
|
CDeferredTaskQueue* q = mQueueList;
|
|
while (q)
|
|
{
|
|
if (q->mQueueWindow == window)
|
|
break;
|
|
q = q->mNext;
|
|
}
|
|
if (!q)
|
|
{
|
|
// no queue for this window yet. Make one, insert it at front of list.
|
|
try
|
|
{
|
|
q = new CDeferredTaskQueue(window);
|
|
q->mNext = mQueueList;
|
|
mQueueList = q;
|
|
}
|
|
catch(...)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
// Got a queue. Post the task to it. If it's a unique ("Post1") operation, remove
|
|
// all tasks of the same type first.
|
|
if (inUnique)
|
|
q->DoRemoveType(inTask);
|
|
q->DoPost(inTask);
|
|
} // CDeferredTaskManager::DoPost
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::DoRemove(CDeferredTask*& inTask, LPane* inPane)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (!inTask)
|
|
return;
|
|
LWindow* window = nil;
|
|
if (inPane)
|
|
window = LWindow::FetchWindowObject(inPane->GetMacPort());
|
|
if (window)
|
|
DoRemove(inTask, window);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::DoRemove(CDeferredTask*& inTask, LWindow* inWindow)
|
|
// A nil window is OK (and represents the global "nil" queue).
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (!inTask)
|
|
return;
|
|
|
|
// Try to find a queue matching the specified window.
|
|
// Note to those who follow: the window pointed to by inWindow may be deleted, but
|
|
// the pointer is still usable as an identifier for the task's queue.
|
|
CDeferredTaskQueue* q = mQueueList;
|
|
CDeferredTaskQueue* prev = nil;
|
|
while (q)
|
|
{
|
|
if (q->mQueueWindow == inWindow)
|
|
{
|
|
q->DoRemove(inTask);
|
|
break;
|
|
}
|
|
prev = q;
|
|
q = q->mNext;
|
|
}
|
|
|
|
// If the queue for this window is empty, delete it from the queue list.
|
|
if (q && q->mFrontTask == nil)
|
|
RemoveNextQueueAfter(prev);
|
|
|
|
// If there are no more queues, die. Life is not worth living if there are no more
|
|
// queues for me to manage.
|
|
if (!mQueueList)
|
|
delete this;
|
|
} // CDeferredTaskManager::DoRemove
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::RemoveNextQueueAfter(CDeferredTaskQueue* inPreviousQueue)
|
|
// Remove the queue following the inPreviousQueue. If inPreviousQueue is nil, remove
|
|
// first queue.
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
CDeferredTaskQueue* q = inPreviousQueue ? inPreviousQueue->mNext : mQueueList;
|
|
CDeferredTaskQueue* next = q->mNext;
|
|
delete q;
|
|
if (inPreviousQueue)
|
|
inPreviousQueue->mNext = next;
|
|
else
|
|
mQueueList = next;
|
|
} // CDeferredTaskManager::RemoveNextQueueAfter
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::DoClearQueue(LPane* inPane)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
LWindow* window = nil;
|
|
if (inPane)
|
|
window = LWindow::FetchWindowObject(inPane->GetMacPort());
|
|
if (window)
|
|
DoClearQueue(window);
|
|
} // CDeferredTaskManager::DoClearQueue
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::DoClearQueue(LWindow* inWindow)
|
|
// A nil window is OK (and represents the global "nil" queue).
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
// Try to find a queue matching the specified window.
|
|
// Note to those who follow: the window pointed to by inWindow may be deleted, but
|
|
// the pointer is still usable as an identifier for the task's queue.
|
|
CDeferredTaskQueue* q = mQueueList;
|
|
CDeferredTaskQueue* prev = nil;
|
|
while (q)
|
|
{
|
|
if (q->mQueueWindow == inWindow)
|
|
{
|
|
q->DoClearSelf();
|
|
break;
|
|
}
|
|
prev = q;
|
|
q = q->mNext;
|
|
}
|
|
|
|
// If the queue for this window is empty, delete it from the queue list.
|
|
if (q && q->mFrontTask == nil)
|
|
RemoveNextQueueAfter(prev);
|
|
|
|
// If there are no more queues, die. Life is not worth living if there are no more
|
|
// queues for me to manage.
|
|
if (!mQueueList)
|
|
delete this;
|
|
} // CDeferredTaskManager::DoClearQueue
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::DoExecuteTasks()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
// Try to execute the first task in each window's queue.
|
|
CDeferredTaskQueue* q = mQueueList;
|
|
while (q)
|
|
{
|
|
CDeferredTask* frontTask = q->mFrontTask;
|
|
CDeferredTaskQueue* nextQueue = q->mNext;
|
|
if (frontTask)
|
|
{
|
|
CDeferredTask::ExecuteResult result = frontTask->Execute();
|
|
if (result == CDeferredTask::eDoneDelete)
|
|
{
|
|
DoRemove(frontTask, q->mQueueWindow);
|
|
// Note to those who follow: the window may be deleted here, but
|
|
// the pointer is still usable as an identifier for the task's queue.
|
|
}
|
|
else if (result == CDeferredTask::eWaitStepBack)
|
|
{
|
|
// front task didn't complete its work, and wants to yield to the second
|
|
// task (if any) on the next go round.
|
|
CDeferredTask* nextTask = frontTask->mNext;
|
|
if (nextTask)
|
|
{
|
|
q->mFrontTask = nextTask;
|
|
frontTask->mNext = nextTask->mNext;
|
|
nextTask->mNext = frontTask;
|
|
}
|
|
}
|
|
// The third case (eWaitStayFront) is handled by just passing on to the next
|
|
// queue.
|
|
}
|
|
q = nextQueue;
|
|
}
|
|
} // CDeferredTaskManager::ExecuteTasks
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskManager::SpendTime(const EventRecord&)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
DoExecuteTasks();
|
|
} // CDeferredTaskManager::SpendTime
|
|
|
|
#pragma mark -
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTaskQueue::CDeferredTaskQueue(LWindow* inWindow)
|
|
// A nil window is OK (and represents the global "nil" queue).
|
|
//----------------------------------------------------------------------------------------
|
|
: mFrontTask(nil)
|
|
, mQueueWindow(inWindow)
|
|
, mNext(nil)
|
|
{
|
|
} // CDeferredTaskQueue::CDeferredTaskQueue
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTaskQueue::~CDeferredTaskQueue()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
} // CDeferredTaskQueue::CDeferredTaskQueue
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskQueue::DoPost(CDeferredTask* inTask)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
CDeferredTask* cur = mFrontTask;
|
|
CDeferredTask* prev = nil;
|
|
while (cur)
|
|
{
|
|
prev = cur;
|
|
cur = cur->mNext;
|
|
}
|
|
if (prev)
|
|
prev->mNext = inTask;
|
|
else
|
|
mFrontTask = inTask;
|
|
} // CDeferredTaskQueue::DoPost
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskQueue::DoRemove(CDeferredTask*& inTask)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (!inTask)
|
|
return;
|
|
CDeferredTask* cur = mFrontTask;
|
|
CDeferredTask* prev = nil;
|
|
while (cur)
|
|
{
|
|
if (cur == inTask)
|
|
{
|
|
// Found the task in the queue, remove it.
|
|
CDeferredTask* next = inTask->mNext;
|
|
delete inTask;
|
|
inTask = nil; // that's why it's a reference.
|
|
// Note that possibly inTask == mFrontTask
|
|
if (prev)
|
|
prev->mNext = next;
|
|
else
|
|
mFrontTask = next;
|
|
break;
|
|
}
|
|
prev = cur;
|
|
cur = cur->mNext;
|
|
}
|
|
} // CDeferredTaskQueue::DoRemove
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskQueue::DoRemoveType(CDeferredTask* inTask)
|
|
// Remove all tasks whose class type is the same as inTask. Used in Post1() calls before
|
|
// posting the new class.
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (!inTask)
|
|
return;
|
|
CDeferredTask* cur = mFrontTask;
|
|
CDeferredTask* prev = nil;
|
|
while (cur)
|
|
{
|
|
CDeferredTask* next = cur->mNext;
|
|
if (typeid(*cur) == typeid(*inTask))
|
|
{
|
|
// Found a matching task in the queue, remove it.
|
|
delete cur;
|
|
if (prev)
|
|
prev->mNext = next;
|
|
else
|
|
mFrontTask = next;
|
|
}
|
|
else
|
|
{
|
|
prev = cur;
|
|
}
|
|
cur = next;
|
|
}
|
|
} // CDeferredTaskQueue::DoRemove
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void CDeferredTaskQueue::DoClearSelf()
|
|
// Remove all tasks whose class type is the same as inTask. Used in Post1() calls before
|
|
// posting the new class.
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
while (mFrontTask)
|
|
{
|
|
CDeferredTask* dead = mFrontTask;
|
|
mFrontTask = dead->mNext;
|
|
delete dead;
|
|
}
|
|
} // CDeferredTaskQueue::ClearSelf
|
|
|
|
#pragma mark -
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTask::CDeferredTask()
|
|
//----------------------------------------------------------------------------------------
|
|
: mNext(nil)
|
|
, mExecuting(false)
|
|
#if DEBUG
|
|
, mExecuteAttemptCount(0)
|
|
#endif
|
|
{
|
|
} // CDeferredTask::CDeferredTask
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTask::~CDeferredTask()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
} // CDeferredTask::~CDeferredTask
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTask::ExecuteResult CDeferredTask::Execute()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (mExecuting)
|
|
return eWaitStayFront;
|
|
#if DEBUG
|
|
mExecuteAttemptCount++;
|
|
Assert_((mExecuteAttemptCount & 0x000003FF) != 0); // assert every 1024 attempts.
|
|
#endif
|
|
mExecuting = true;
|
|
ExecuteResult result = eWaitStayFront;
|
|
try
|
|
{
|
|
result = ExecuteSelf();
|
|
}
|
|
catch(...)
|
|
{
|
|
result = eDoneDelete; // if threw exception, delete task so it won't execute again.
|
|
}
|
|
mExecuting = false;
|
|
return result;
|
|
} // CDeferredTask::Execute
|
|
|
|
#pragma mark -
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredCommand::CDeferredCommand(
|
|
LCommander* inCommander,
|
|
CommandT inCommand,
|
|
void* ioParam)
|
|
//----------------------------------------------------------------------------------------
|
|
: mCommander(inCommander)
|
|
, mCommand(inCommand)
|
|
, mParam(ioParam)
|
|
{
|
|
} // CDeferredCommand::CDeferredCommand
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTask::ExecuteResult CDeferredCommand::ExecuteSelf()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (mCommander && !mCommander->ObeyCommand(mCommand, mParam))
|
|
return eWaitStayFront;
|
|
return eDoneDelete;
|
|
} // CDeferredCommand::ExecuteSelf
|
|
|
|
#pragma mark -
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredMessage::CDeferredMessage(
|
|
LListener* inListener,
|
|
MessageT inMessage,
|
|
void* ioParam)
|
|
//----------------------------------------------------------------------------------------
|
|
: mListener(inListener)
|
|
, mMessage(inMessage)
|
|
, mParam(ioParam)
|
|
{
|
|
} // CDeferredMessage::CDeferredMessage
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTask::ExecuteResult CDeferredMessage::ExecuteSelf()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (mListener)
|
|
mListener->ListenToMessage(mMessage, mParam);
|
|
return eDoneDelete;
|
|
} // CDeferredMessage::ExecuteSelf
|
|
|
|
#pragma mark -
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredCloseTask::CDeferredCloseTask(
|
|
LPane* inPane)
|
|
//----------------------------------------------------------------------------------------
|
|
: mWindow(nil)
|
|
{
|
|
if (inPane)
|
|
mWindow = dynamic_cast<CNetscapeWindow*>
|
|
(LWindow::FetchWindowObject(inPane->GetMacPort()));
|
|
if (mWindow)
|
|
{
|
|
mWindow->StopAllContexts(); // This can reshow the window, so do it first.
|
|
mWindow->Hide();
|
|
// Make sure there are no other load tasks and such.
|
|
CDeferredTaskManager::ClearQueue(mWindow);
|
|
}
|
|
} // CDeferredCloseTask::CDeferredCloseTask
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
CDeferredTask::ExecuteResult CDeferredCloseTask::ExecuteSelf()
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
if (!mWindow)
|
|
return eDoneDelete;
|
|
// Allow the double-click timer in the thread window to time out and delete itself
|
|
// probably unnecessary to wait - safer?
|
|
if (mWindow->ClickTimesAreClose(::TickCount()))
|
|
return eWaitStayFront;
|
|
// Wait till any pending URLs are finished
|
|
if (mWindow->IsAnyContextBusy())
|
|
return eWaitStayFront;
|
|
if (mWindow)
|
|
mWindow->DoClose();
|
|
return eDoneDelete;
|
|
} // CDeferredCloseTask::ExecuteSelf
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
/* static */ CDeferredCloseTask* CDeferredCloseTask::DeferredClose(LPane* inPane)
|
|
//----------------------------------------------------------------------------------------
|
|
{
|
|
CDeferredCloseTask* task = new CDeferredCloseTask(inPane);
|
|
CDeferredTaskManager::Post(task, inPane);
|
|
return task;
|
|
}
|
|
|