Opera core object
What purposes does the Opera core object have?
This lists the primary purposes of the Opera core object:
- Initialization of the modules
- Destruction of the modules
- Storage of the global variables
- Scheduling of when to run Opera
How to initialize the Opera object
How you create and destroy the global Opera object depends on if FEATURE_STATIC_OPERA_OBJECT is turned on.
For static Opera you can do like this:
Opera::StaticInit();
g_opera->SetListener(some_listener);
TRAPD(status, g_opera->InitL());
if (OpStatus::IsSuccess(status))
{
...
loop:
g_opera->Run();
...
}
g_opera->Destroy();
and if FEATURE_STATIC_OPERA_OBJECT is NO, yo do:
g_opera = new Opera();
g_opera->SetListener(some_listener);
TRAPD(status, g_opera->InitL());
if (OpStatus::IsSuccess(status))
{
...
loop:
g_opera->Run();
...
}
g_opera->Destroy();
delete g_opera;
Note that the global Opera object should be destroyed the same way regardless of whether Opera::InitL()
succeeded and Opera was actually run.
How does a module add a module object to Opera?
Each module can have its own space in the Opera core object. For the module xxx the member
g_opera->xxx_module can be made available. Each module object has to implement the interface
OperaModule, which more or less has the two functions InitL() and Destroy().
The implementation of the OperaModule shall be put in a file called xxx_module.h in the root
of the xxx-module. Since the module object may not always be enabled a define will have to
be set, XXX_MODULE_REQUIRED, for the module object to be included. The name of the module object
shall be the name of the module capitalized, underscores removed and following character capitalized.
Example:
spatial_navigation becomes SpatialNavigationModule
webforms becomes WebformsModule
How is a global variable added?
The module objects will be publically available through the g_opera object. To be able
to easily access a member of a module object a #define is the best thing to use.
See the
coding standard on the subject of global variable naming.
The variable can be initialized in the constructor of the module object, constructed
in the InitL() function and finally destroyed in the Destroy() function.
Don't use the destructor to destroy the variables - use Destroy().
Example:
The module foo gets a new global variable:
class FooModule : public OperaModule
{
public:
...
Foo* foo;
};
#define g_foo g_opera->foo_module.foo
Example of module object file
In the following code, we assume that we have a module called xxx which is turned on if
FEATURE_XXX is turned on, and is controlled by the define XXX_SUPPORT.
File: xxx_module.h:
/* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
**
** Copyright (C) 1995-2005 Opera Software AS. All rights reserved.
**
** This file is part of the Opera web browser. It may not be distributed
** under any circumstances.
*/
#ifndef XXX_MODULE_H
#define XXX_MODULE_H
#ifdef XXX_SUPPORT
#include "modules/hardcore/opera/module.h"
#define XXX_MODULE_REQUIRED
class XxxHandler;
class XxxModule : public OperaModule
{
public:
void InitL(const OperaInitInfo& info);
void Destroy();
XxxHandler* handler;
};
#define g_xxx_handler g_opera->xxx_module.handler;
#endif // XXX_SUPPORT
#endif // !XXX_MODULE_H
How does the scheduling work?
The Opera object has a Run()-function which is the most important
function to be able to make Opera do something. Calling Run() will
make Opera handle one internal message and then return again. The
result of the call will be the time in milliseconds until the next
time Opera wants to be called. The product can then set a platform
timer event to wait for that time. If the return value is zero it means
there are more messages which would like to be handled as soon as possible.
It is important to know that Opera can return zero for a long period of time,
so it may not be a good solution to always let Opera run if it returns zero.
The platform must be able to provide user input and network input from time
to time too.
Since Opera sometimes is called from the platform interface layer, for example
when receiving network input or keyboard input, it can happen that the time
when Opera was scheduled to be run the next time is not valid anymore. If
this happens, the OnReschedulingNeeded() function in the listener object
will be called. This means the product must reset the timer for when Opera
is to be run the next time.
Here follows an example implementation, similar to that used on Windows
in the WinGogi port:
void
ProductModule::OnReschedulingNeeded(int next_event)
{
m_next_timer = next_event;
SetTimer(g_hwnd, 0, m_next_timer, NULL);
}
void
ProductModule::MainLoop()
{
// Main message loop:
MSG msg = {0};
while (true)
{
BOOL handle = FALSE;
if (m_next_timer == 0)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
handle = TRUE;
else
{
m_next_timer = g_opera->Run();
if (m_next_timer > 0)
SetTimer(g_hwnd, 0, m_next_timer, NULL);
}
}
else
{
if (!GetMessage(&msg, NULL, 0, 0))
break;
handle = TRUE;
}
if (handle)
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
PeekMessage() looks at the Windows message queue and returns TRUE if there is
a message available in the queue. GetMessage() blocks until a message is available
in the message queue. Will return FALSE if the application is quitting. TranslateMessage()
and DispatchMessage() are used to handle the message.
This implementation will keep handling Windows messages until there are no available, then it
will start running Opera. This makes sense if you want to make sure user input, network traffic etc
is always taken care of as soon as possible.