Disclaimer: This page is not yet updated to reflect the current SEB version 2.0. Most of this information is therefore outdated. Please note that the source code of SEB version 2.x is no longer available in the SEB SourceForge SVN repository, but on SourceForge Git.
This manual is providing information about the architecture of Safe Exam Browser for Mac OS X, about getting the SEB (and WebKit) sources, linking the components and how to build SEB.
SEB for macOS is a modern Mac OS X application, written in Objective-C, using the Cocoa system framework. It has been developed for Mac OS X 10.6 Snow Leopard and updated for OS X 10.7 Lion, we don't intend to support older system versions. SEB for macOS is using the WebKit browser engine, on which Safari on Mac and iOS and some new open source browsers like Google Chrome and Android browsers are based. SEB for macOS embeds a private copy of the WebKit framework, so Apple updates of Safari don't affect the compatibility of SEB with existing quizzes. Please note: This doesn't apply to the Mac App Store version of SEB which is using the system's built-in WebKit framework (due to restrictions of the Mac App Store).
SEB for macOS version 1.3 consists of the classes listed below. The class structure is supposed to change soon, since SEB will go trough major enhancements in the next months. Among others there will be additional preferences panes for more options and SEB will be ported to a document-based architecture to support opening multiple webpages in separate windows and tabs.
As usual for Cocoa applications, you also have to examine the Interface Builder XIB file to understand how SEB GUI elements work, especially Cocoa bindings in the preferences window. At the moment the GUI elements are contained the XIB files MainMenu.xib, PreferencesGeneral.xib. and PreferencesAdvanced.xib.
Add the SEB Sourceforge repository to Xcode. In Xcode 4 you do this in the Organizer windows' Repositories tab (see the Xcode documentation for details). Name it as you like and use the URL below as Location:
https://seb.svn.sourceforge.net/svnroot/seb
Type is Subversion, and you don't need a username or password to check out the code.
You will find the current developer version of SEB for macOS at the path trunk/mac/SafeExamBrowser-src. Checkout this directory to your computer.
To translate Interface Builder xib files, you best use the ibtool command-line utility to extract all the strings. Enter in Terminal:
ibtool --generate-strings-file en.lproj/MainMenu.strings en.lproj/MainMenu.xib
The resulting file MainMenu.strings can be given to the translator. Best is to keep it in the UTF-16 text encoding format, for example Apple's TextEdit reads and saves this format correctly.
Below the commands for the other xib files:
ibtool --generate-strings-file en.lproj/PreferencesGeneral.strings en.lproj/PreferencesGeneral.xib
ibtool --generate-strings-file en.lproj/PreferencesBrowser.strings en.lproj/PreferencesBrowser.xib
ibtool --generate-strings-file en.lproj/PreferencesAdvanced.strings en.lproj/PreferencesAdvanced.xib
Afterwards you can generate a new MainMenu.xib file based on the original one and the translated strings file, again using ibtool:
ibtool --strings-file fr.lproj/MainMenu.strings --write fr.lproj/MainMenu.xib en.lproj/MainMenu.xib
Then you need to add the resource file to Xcode. I didn't find a better way in Xcode 4 than clicking on the Resource folder, adding a localisation with the + button in the utility area at the right of the workspace window and then pasting the contents of the strings-file into the new created language/region specific file (I guess there's a bug in Xcode 4, in Xcode 3 it was possible to add the files directly).
Here the commands for the other localized xib files (English to German):
ibtool --strings-file de.lproj/PreferencesGeneral.strings --write de.lproj/PreferencesGeneral.xib en.lproj/PreferencesGeneral.xib
ibtool --strings-file de.lproj/PreferencesBrowser.strings --write de.lproj/PreferencesBrowser.xib en.lproj/PreferencesBrowser.xib
ibtool --strings-file de.lproj/PreferencesAdvanced.strings --write de.lproj/PreferencesAdvanced.xib en.lproj/PreferencesAdvanced.xib
Here the commands for the other localized xib files (English to French):
ibtool --strings-file fr.lproj/PreferencesGeneral.strings --write fr.lproj/PreferencesGeneral.xib en.lproj/PreferencesGeneral.xib
ibtool --strings-file fr.lproj/PreferencesBrowser.strings --write fr.lproj/PreferencesBrowser.xib en.lproj/PreferencesBrowser.xib
ibtool --strings-file fr.lproj/PreferencesAdvanced.strings --write fr.lproj/PreferencesAdvanced.xib en.lproj/PreferencesAdvanced.xib
In the end you should check the GUI in the new localized MainMenu.xib in Xcode/Interface Builder and adjust size and position of text fields according to the length of the localized text.
Strings placed in the code instead of xib files are even easier to localize. In each .lproj language directory you can find a file Localizable.strings, containing all original and the corresponding localized strings. You just have to use the appropriate string-loading macros in the code (for example NSLocalizedString(@"FirstTimeUserNotice", nil) and then use the genstrings command-line tool to extract those strings and create strings files for you. For example:
genstrings -o en.lproj *.m
This command parses all Objective-C source files in the current directory and puts the resulting strings files in the en.lproj subdirectory. You can then use this English Localizable.strings file to create the according files for other languages, by replacing the English strings with the translated ones, for example:
"FirstTimeUserNotice" = "Bitte benutzen Sie die Fenster-Schliessen-Schaltfläche oder Befehlstaste+Q um SEB zu beenden und (fn+)F3+F6 um die Einstellungen zu öffnen.\n\nDieser Hinweis wird solange angezeigt, bis Sie in den Einstellungen eine Start-URL eingeben. Sie sollten dort ausserdem ein Beenden-Passwort und ein Administrator-Passwort festlegen, um das Beenden von SEB und den Zugang zu den Einstellungen einzuschränken.";
(This is one line of the German localized strings file de.lproj/Localizable.strings)
Until version 1.4.1, SEB downloaded from this website contained a private copy of WebKit. From version 1.5 it was omitted, because experiences didn't show any real advantage of using a private WebKit instead of the system's in practice, but there was a lot of additional time and effort needed for every update. Besides that at least some WebKit builds from December 2011 and January 2012 were not working on Mac OS X 10.6.8 anymore (and compiling on Snow Leopard resulted in build errors). But besides you can find how to include your private copy of WebKit if you see an advantage in that:
Check out the WebKit source code in Terminal:
Change to your working directory
Enter the following command in Terminal (and be prepared that it may take several hours):
svn checkout http://svn.webkit.org/repository/webkit/trunk WebKit-src
(the last line should say something like Checked out revision 71626.)
In case you want to update a WebKit build checked out some time ago, you can use the update-webkit script:
Tools/Scripts/update-webkit
(the last line should say something like Updated to revision 71728.)
Now open from the framework source directories in WebKit-src/Source/ each .xcodeproj of JavaScriptCore, JavaScriptGlue, WebCore and WebKit in Xcode and select the framework bundle in Target. Go on Build Settings and change in section Deployment the Installation Directory for release to the following special path:
@executable_path/../Frameworks
Then close each of the Xcode projects, the new path will be saved automatically.
Change to the WebKit directory:
cd WebKit-src
Build WebKit (this will also take some time and stress your CPU quite a lot, so you might want to run it during your lunch break).
Building has to be done twice, separately for the 32- and 64-bit build:
Tools/Scripts/set-webkit-configuration --32-bit
Tools/Scripts/build-webkit
(it should be written somewhere in the end ** BUILD SUCCEEDED **)
Now you have the build 32-bit frameworks in directory WebKitBuild/Release. Rename it to WebKitBuild/Release-i386. Build the 64-bit version:
Tools/Scripts/set-webkit-configuration --64-bit
Tools/Scripts/build-webkit
Rename the built 64-bit framework directory WebKitBuild/Release/ to WebKitBuild/Release-x86_64.
In Terminal you can check if the relative paths have been set in the frameworks with the otool -L command, for example:
otool -L WebKitBuild/Release-x86_64/WebCore.framework/WebCore
WebKitBuild/Release-x86_64/WebCore.framework/WebCore:
@executable_path/../Frameworks/WebCore.framework/Versions/A/WebCore (compatibility version 1.0.0, current version 534.13.0)
/usr/lib/libsqlite3.dylib (compatibility version 9.0.0, current version 9.6.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 38.0.0)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 152.0.0)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 15.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
@executable_path/../Frameworks/JavaScriptCore.framework/Versions/A/JavaScriptCore (compatibility version 1.0.0, current version 534.13.0)
/usr/lib/libicucore.A.dylib (compatibility version 1.0.0, current version 40.0.0)
/usr/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.3.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.6.3)
/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 293.6.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.1)
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 44.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 550.42.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.42.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1038.35.0)
As you can see, WebCore.framework and JavaScriptCore.framework have the right path @executable_path/../Frameworks now.
For this we need the lipo command in Terminal:
lipo -create WebKitBuild/Release-i386/JavaScriptCore.framework/JavaScriptCore WebKitBuild/Release-x86_64/JavaScriptCore.framework/JavaScriptCore -output WebKitBuild/JavaScriptCore
We can check what the resulting fat framework file contains:
lipo -detailed_info WebKitBuild/JavaScriptCore
Fat header in: /Users/drs/developer/WebKit-src/WebKitBuild/JavaScriptCore
fat_magic 0xcafebabe
nfat_arch 2
architecture i386
cputype CPU_TYPE_I386
cpusubtype CPU_SUBTYPE_I386_ALL
offset 4096
size 2933032
align 2^12 (4096)
architecture x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
offset 2940928
size 2993520
align 2^12 (4096)
So now we have a framework file with 32 and 64 bit binaries inside!
We have to make the other frameworks fat too:
lipo -create WebKitBuild/Release-i386/JavaScriptGlue.framework/JavaScriptGlue WebKitBuild/Release-x86_64/JavaScriptGlue.framework/JavaScriptGlue -output WebKitBuild/JavaScriptGlue
lipo -create WebKitBuild/Release-i386/WebCore.framework/WebCore WebKitBuild/Release-x86_64/WebCore.framework/WebCore -output WebKitBuild/WebCore
lipo -create WebKitBuild/Release-i386/WebKit.framework/WebKit WebKitBuild/Release-x86_64/WebKit.framework/WebKit -output WebKitBuild/WebKit
In the end we have to copy the newly created fat framework binaries into the .framework bundles. You can first duplicate one of the WebKitBuild/Release directories and rename it WebKitBuild/Release-FAT. Then copy the four fat framework binaries JavaScriptCore, JavaScriptGlue, WebCore and WebKit to the right place in the .framework bundles (overwriting the old framework binaries). Usually the subdirectory xxx.framework/Versions/A/ is the right location for the framework binaries.
To comply with version control, you first have to delete the old version of the frameworks in Xcode (remove it with the SVN command in terminal or in Xcode):
svn delete --force SafeExamBrowser-src/JavaScriptCore.framework
svn delete --force SafeExamBrowser-src/JavaScriptGlue.framework
svn delete --force SafeExamBrowser-src/WebCore.framework
svn delete --force SafeExamBrowser-src/WebKit.framework
svn commit -m "deleted old WebKit frameworks" SafeExamBrowser-src
Then you have to add the new frameworks to the SafeExamBrowser project in Xcode. Important is to remove the WebCore.framework from the Link Binary With Libraries build phase of the target Safe Exam Browser in Xcode, otherwise building will fail, since WebCore is only linked through its umbrella framework WebKit. The four frameworks also have to be in the Copy Files build phase.
There is a Code Signing build setting in the SafeExamBrowser Xcode project, which you will have to modify, because you don't have the ETH Zurich code signing identity. We will add an explanation about how to code sign the SEB binary to this manual after the implementation of further security measures in SEB based on checking code signatures of running processes.
So when you succeeded to build SEB with a new version of the WebKit frameworks, you can check if it is really used on this page. If it displays Your WebKit version is a nightly build, then SEB is using the private embedded WebKit.
The second target in the Xcode project, Safe Exam Browser App (which is used to build the Mac App Store version of SEB), is using the system's built-in WebKit framework.
Use this page if you want to test the JavaScript popup window opening settings in SEB 1.5.x.