Preferences module

Copyright © 1995-2012 Opera Software ASA. All rights reserved. This file is part of the Opera web browser. It may not be distributed under any circumstances.

Introduction

The Preferences module provides support for reading the configuration from disk and writing updates back. The list of preferences supported are defined by the preferences system. The low-level file format support is provided by the Prefsfile module.

Current information about the Preferences module.

Interface overview and API documentation

API documentation

The API documentation is extracted automatically by Doxygen.

Overview

Preferences framework

The PrefsManager is implemented as a framework for handling preferences. The PrefsManager class itself is seldom called, except for setup and exit operations, when using the string based preferences APIs (if enabled) and to commit changes to storage.

It interacts with the Prefsfile module to store them, normally on disk using the PrefsFile class, but other solutions are possible.

The PrefsNotifier is used for communicating changes in run-time configured default values to the preferences framework. When the client detects a change in one of these preferences, it calls the appropriate method on the global PrefsNotifier object, which will then handle any reconfiguration needed inside of the core.

The preferences themselves are stored and managed by sub-classes of OpPrefsCollection. They also provide OpPrefsListener objects, which provides a method for registering objects to listen to changes in certain OpPrefsCollection objects. The preferences inside each collection is defined using the preferences system, with a few exceptions.

The partitioning into collection objects reflects the modularised state of the Opera core, in such that the different settings can be owned by the code modules that are using them, and that build configurations that exclude certain code modules also should exclude the associated preference settings. The collections hold preferences for a given module, or for a set of tightly related modules (for instance PrefsCollectionJS contains JavaScript and DOM settings). Since collections are meant to be owned by their respective module, all the preferences relevant only to the module/module set, and only those, should be located in the respective collection class.

Some shared preferences without a clear owner will still reside in the preference module, as will the special files collection. The files collections also handles directory related settings in collaboration with OpFolderManager (it does not retain the directory settings itself, but relies on OpFolderManager doing so).

To handle the possibility of overriding settings on a host basis, OpPrefsCollectionWithHostOverride (a subclass of OpPrefsCollection, and the superclass of all collections supporting host overrides) holds a list of overridden settings. Changes in overrides are broadcast to the listeners using the BroadcastOverride() callback.

See the class hierarchy for details on the different classes.

The scope for PrefsManager and its associated objects is the settings read and written to the standard preference file (often called opera.ini or opera6.ini). There are a few minor exceptions to this, but the basic rule is thatif the setting is not in the main preference file, it does not belong to the preference manager and the preference collections.

Utility code

The OperaConfig class assists the URL code in generating HTML and JavaScript code for the opera:config document, which allows configuring of Opera from a document view, using JavaScript and DOM interfaces. It does so by using the framework from the About module.

The PrefsUpgrade class is used to support upgrading from previous versions of the preferences system. It will only be included if the appropriate feature definition is enabled, and will check a special version flag in the ini file to see if it should upgrade it to a newer version or not. Information about changes that have been made to the preferences compared to previous versions is available in a separate document.

Settings

There is also an automatically generated list of settings that is built automatically when the preferences system is updated. It can also be built by running the extract-documentation.pl script from the top-level directory of this module.

Use-cases

Setting up main Opera preferences

The settings are read during the prefs module initialisation phase. In its initialisation code, the platform creates an instance of the PrefsFile class representing its preferences file(s) and saves this in the OperaInitInfo. This object will be taken over and used when the PrefsManager is initialised.

Accessing a single preference

To read a preference, the code calls the global instance of the preference collection object holding the preference directly, for most collections this is done via the GetIntegerPref() and GetStringPref() methods.

Conversely, settings are stored using the WriteIntegerL() and WriteStringL() methods.

Using the string based API

If the string based API has been enabled, it is possible to access most settings with the names as used in the INI file, by calling the PrefsManager::GetPreferenceL() method.

Listening to changes

Code that wants to listen to changes in relevant settings inherit the OpPrefsListener interface (or PrefsCollectionFilesListener for file preferences) and implement the PrefChanged() method.

Example: The URL code listening to changes in the disk cache size.

 void URLManager::PrefChanged(enum OpPrefsCollection::Collections id,
                              int pref, int newvalue)
 {
   switch (id)
   {
   case OpPrefsCollection::Network:
     switch ((enum PrefsCollectionNetwork::integerpref) pref)
     {
     case DiskCacheSize:
       m_cachesize = newvalue;
       break;
     }
   default:
     OP_ASSERT(FALSE); // I should not be hearing this
   }
 }

Notifying changes

Code that wants to notify that the platform default settings have changed call the appropriate PrefsNotifier interface.

Example: The platform has detected that the user has moved to a different access point or network and that the proxy settings have changed.

 void PlatformProxyObserver::OnChange()
 {
  TRAPD(rc, PrefsNotifier::OnProxyChangedL());
  if (OpStatus::IsMemoryError(rc))
  {
   g_memory_manager->RaiseError(rc);
  }
 }

Adding a new preference

New preferences should go in the collection object owned by/most closely related to the module where the preference is used. The exceptions to this are font, color and file preferences which are handled by special collections.

The preferences are defined in a text files, which are used to generate source code. More details are available in the documentation for the preference system.

There should usually be no need to do alter the interface beyond this, but in some rare cases a specialised interface may be needed. Please see the API documentation for further information about how to do that.

Collections outside the prefs module

The same scripts are used for preference collections stored outside the prefs module. As long as the collections are listed in a module.prefs file as described in the preference system documentation, the core build setup scripts should take care of everything for you.

For an active example of preference collections outside the prefs module using this system, look at the coregogi preferences in platforms/core in a regular coregogi checkout.

See also the preference system guide to adding a new collection.

Read-only and file-less operation

For products with special requirements, it is possible to run the preferences system without preference files, with read-only preference files, or with fixed preferences. This is controlled by two features, FEATURE_PREFS_READ and FEATURE_PREFS_WRITE, and works as described in the following table.

 FEATURE_PREFS_READ
YESNO
FEA­TURE_­PREFS_­WRITE YES Normal operation. The preference file(s) are read from and the user preferences file is written to when you perform a Write call. Memory-only operation. All preferences will be initialised with their default values, and changes written using the write APIs will be retained until the end of the session. Use this if you want to run without any preference file at all.
NO Read-only operation. Preferences will be read as usual from the preference file(s), but they will never be written back. This mode can be useful when running from a read-only file system, when you still want the customer to be able to customise settings without having to recompile. Static operation. No preference file(s) are read and all settings are initialised with their default values. No settings can be changed at run-time.

Supported standards

n/a

Implementation and design

See also the API documentation.

Generalisation and re-implementation

The OpPrefsCollection class is written so that it is easy to create new preferences sets for new code. Having them divided into several collection objects like this also helps hiding irrelevant settings from builds not sporting the code where they are used, and makes it possible to store the objects in the code modules where the preferences are referenced, meaning that the prefs module can be released asynchronously from the other modules.

Memory management

Heap usage

All of the preferences collections will allocate their internal data structures on the heap, using arrays whose size is relative to the number of preferences.

Stack usage

There are no recursive calls.

Static memory usage

There are several global pointers, one to the PrefsManager object and one each for the OpPrefsCollection subclasses.

OOM policies

The vast majority of functions that require memory to be allocated is using the TRAP/LEAVE convention. This approach was selected since the calls are mostly low-level and thus needs its caller to do any necessary clean-up that would free memory.

Performance

Enabling host overrides will mean that lookups for the preferences that are overridable will be slower, as the preferences code must scan through all the overridden hosts to find an override, if present.

Using the string based API is slow as it requires a linear scan through the preferences to find a match. It should be restricted to where it is unavoidable, such as responding to external data, such as downloaded preferences or interfaces to outside code.

User-visible changes

There have been several changes to the preferences system in this version compared to previous versions.

References